diff --git a/public/theme/admin/css/login.css b/public/theme/admin/css/login.css index 92d45bb..6b96953 100644 --- a/public/theme/admin/css/login.css +++ b/public/theme/admin/css/login.css @@ -1 +1 @@ -*{box-sizing:border-box}body{display:flex;align-items:center;justify-content:center;height:100vh;margin:0 auto;width:calc(100% - 20px);background-color:#303030;color:#fff;font-family:sans-serif;font-size:16px}main{display:flex;align-items:center;justify-content:center;position:relative;max-width:540px;width:90%;height:480px;background-color:#424242;color:#fff;border-radius:3px;border-bottom:#007769 solid 20px;box-shadow:0 3px 6px rgba(0,0,0,.16),0 3px 6px rgba(0,0,0,.23)}main:after{z-index:-1;content:'';position:absolute;top:0;left:0;width:calc(100% + 10px);height:calc(100% + 10px);background-color:#3949ab;transform:translate(-5px,-5px) rotate(-5deg);box-shadow:0 3px 6px rgba(0,0,0,.16),0 3px 6px rgba(0,0,0,.23)}p{font-size:1.4rem;text-align:center}a{font-size:.7rem;color:#fff;text-decoration:none;border-bottom:1px solid transparent}a:hover{border-bottom-color:#c51162}form{margin:0 auto;width:80%;min-width:240px}.input-group input{background-color:rgba(0,0,0,.4);border:none;border-bottom:2px solid #3949ab;color:#fff;padding:6px}.input-group input:focus,.input-group.focus input{border-color:#ff0089}.input-group.valid input{border-color:#39ab48}.input-group.invalid input{border-color:#cf1b1b}.input-group{display:flex;flex-direction:column;margin-bottom:3px;position:relative;padding-top:.5rem}.input-group label{font-size:.6rem;position:absolute;top:.5rem;left:6px;height:1rem;vertical-align:middle;transform:translateY(50%);color:#dcdcdc;transition:all .2s ease-out}.input-group input:focus~label,.input-group.focus label{top:0;left:0;transform:translateY(0);font-size:.4rem}.input-group .error{display:none}.input-group.invalid .error{margin-top:2px;display:block;font-size:.5rem;color:#ff3232}.warning{background-color:#c51162} \ No newline at end of file +body{display:flex;align-items:center;justify-content:center;height:100vh;width:calc(100% - 20px);font-size:2vw}main{display:flex;align-items:center;justify-content:center;position:relative;max-width:540px;width:90%;height:350px;background-color:#424242;color:#fff;border-radius:3px;border-bottom:#007769 solid 20px;box-shadow:0 3px 6px rgba(0,0,0,.16),0 3px 6px rgba(0,0,0,.23);transform-style:preserve-3d}main:after{content:'';position:absolute;top:0;left:0;width:calc(100% + 10px);height:calc(100% + 10px);background-color:#3949ab;transform:translate3d(-5px,-5px,-1px) rotate(-5deg);box-shadow:0 3px 6px rgba(0,0,0,.16),0 3px 6px rgba(0,0,0,.23)}p{font-size:1.4rem;text-align:center}a{font-size:.7rem;color:#fff;text-decoration:none;border-bottom:1px solid transparent}a:hover{border-bottom-color:#c51162}form{margin:0 auto;width:80%;min-width:240px}.input-group input{background-color:rgba(0,0,0,.4);border:none;border-bottom:2px solid #3949ab;color:#fff;padding:6px}.input-group input:focus,.input-group.focus input{border-color:#ff0089}.input-group.valid input{border-color:#39ab48}.input-group.invalid input{border-color:#cf1b1b}.input-group{display:flex;flex-direction:column;margin-bottom:3px;position:relative;padding-top:.7rem}.input-group label{font-size:.7rem;position:absolute;top:.7rem;left:6px;height:1rem;vertical-align:middle;transform:translateY(50%);color:#dcdcdc;transition:all .2s ease-out}.input-group input:focus~label,.input-group.focus label{top:0;left:0;transform:translateY(0);font-size:.6rem}.input-group .error{display:none}.input-group.invalid .error{margin-top:2px;display:block;font-size:.5rem;color:#ff3232}.error-message{background-color:#c51162;padding:1rem;font-size:.7rem} \ No newline at end of file diff --git a/public/theme/admin/css/style.css b/public/theme/admin/css/style.css index 5dd4324..5b3f6e4 100644 --- a/public/theme/admin/css/style.css +++ b/public/theme/admin/css/style.css @@ -1 +1 @@ -.btn{border:none;background:#3949ab radial-gradient(circle at 0 0,#3949ab 0,rgba(0,0,0,.2) 100%) no-repeat;color:#fff;padding:5px 15px;margin:10px 0;cursor:pointer;position:relative;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;border-radius:4px;box-shadow:0 2px 5px 0 rgba(0,0,0,.26);overflow:hidden;display:flex;justify-content:center;align-items:center;transition:.5s}.btn--outline{background:0 0;border:1px solid #3949ab}.btn:focus{box-shadow:0 3px 8px 1px rgba(0,0,0,.4)}.btn--accent{background:#ff0089 radial-gradient(circle at 0 0,#ff0089 0,rgba(0,0,0,.2) 100%) no-repeat}.btn--warn{background:#f87229 radial-gradient(circle at 0 0,#f87229 0,rgba(0,0,0,.2) 100%) no-repeat}.btn-fab{border-radius:2rem;width:2em;height:2em;padding:5px}.btn-ripple{position:absolute;top:0;left:0;width:100%;height:100%;overflow:hidden;background:0 0}.btn-ripple__effect{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);opacity:1;width:200%;height:0;padding-bottom:200%;border-radius:50%;background:rgba(190,190,190,.3);-webkit-animation:a-ripple .4s ease-in;animation:a-ripple .4s ease-in}.btn-ripple__effect.to-remove{-webkit-animation:remove-ripple .2s linear;animation:remove-ripple .2s linear}.btn--outline .btn-ripple__effect{background:rgba(57,73,171,.5);z-index:-1}.btn__content{z-index:1;font-weight:400;pointer-events:none}@-webkit-keyframes remove-ripple{from{opacity:1}to{opacity:0}}@keyframes remove-ripple{from{opacity:1}to{opacity:0}}@-webkit-keyframes a-ripple{0%{opacity:0;padding-bottom:0;width:0}25%{opacity:1}100%{width:200%;padding-bottom:200%}}@keyframes a-ripple{0%{opacity:0;padding-bottom:0;width:0}25%{opacity:1}100%{width:200%;padding-bottom:200%}} \ No newline at end of file +:focus{outline:0}*{box-sizing:border-box}body,html{padding:0;margin:0;background-color:#303030;color:#fff;font-family:sans-serif;font-size:16px}.hide{display:none!important}.btn{border:none;background:#3949ab radial-gradient(circle at 0 0,#3949ab 0,rgba(0,0,0,.2) 100%) no-repeat;color:#fff;padding:5px 15px;margin:10px 0;cursor:pointer;position:relative;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;border-radius:4px;box-shadow:0 2px 5px 0 rgba(0,0,0,.26);overflow:hidden;display:flex;justify-content:center;align-items:center;transition:.5s}.btn--outline{background:0 0;border:1px solid #3949ab}.btn:focus{box-shadow:0 3px 8px 1px rgba(0,0,0,.4)}.btn--accent{background:#ff0089 radial-gradient(circle at 0 0,#ff0089 0,rgba(0,0,0,.2) 100%) no-repeat}.btn--warn{background:#f87229 radial-gradient(circle at 0 0,#f87229 0,rgba(0,0,0,.2) 100%) no-repeat}.btn-fab{border-radius:2rem;width:2em;height:2em;padding:5px}.btn-ripple{position:absolute;top:0;left:0;width:100%;height:100%;overflow:hidden;background:0 0}.btn-ripple__effect{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);opacity:1;width:200%;height:0;padding-bottom:200%;border-radius:50%;background:rgba(190,190,190,.3);-webkit-animation:a-ripple .4s ease-in;animation:a-ripple .4s ease-in}.btn-ripple__effect.to-remove{-webkit-animation:remove-ripple .2s linear;animation:remove-ripple .2s linear}.btn--outline .btn-ripple__effect{background:rgba(57,73,171,.5);z-index:-1}.btn__content{z-index:1;font-weight:400;pointer-events:none}@-webkit-keyframes remove-ripple{from{opacity:1}to{opacity:0}}@keyframes remove-ripple{from{opacity:1}to{opacity:0}}@-webkit-keyframes a-ripple{0%{opacity:0;padding-bottom:0;width:0}25%{opacity:1}100%{width:200%;padding-bottom:200%}}@keyframes a-ripple{0%{opacity:0;padding-bottom:0;width:0}25%{opacity:1}100%{width:200%;padding-bottom:200%}} \ No newline at end of file diff --git a/public/theme/admin/js/scripts.js b/public/theme/admin/js/scripts.js index 79e47cf..6aa374b 100644 --- a/public/theme/admin/js/scripts.js +++ b/public/theme/admin/js/scripts.js @@ -217,17 +217,30 @@ class FormHandler { body: new FormData(el), redirect: 'manual' }).then(res => { - if(!res.ok) { + if (!res.ok) { throw new Error('Network response errored'); } return res.json() - }).then(this.cb).catch(this.err); + }).then(ev => this.cb(ev, el)).catch(ev => this.err(ev, el)); + } else { + VUtils.forEach($$('input', el), ele => { + if (!ele.checkValidity()) { + let parent = ele.parentNode; + parent.classList.remove('valid'); + parent.classList.add('invalid'); + } + }); } } } + (function () { const body = $('body'); body.addDelegatedEventListener('change input', 'input', (e, el) => { + let errorMessage = $('.error-message', el.find('form')); + if (errorMessage) { + errorMessage.classList.add('hide') + } let parent = el.parentNode; if (el.value === "") { parent.classList.remove('focus') @@ -242,9 +255,12 @@ class FormHandler { parent.classList.add('invalid'); } }) - if($('#login')) { - new FormHandler('form#login', 'body', e => { - console.log(e); + + if ($('#login')) { + new FormHandler('form#login', 'body', () => { + location.reload(); + }, (e, el) => { + $('.error-message', el).classList.remove('hide'); }) } })() \ No newline at end of file diff --git a/public/theme/admin/js/scripts.min.js b/public/theme/admin/js/scripts.min.js new file mode 100644 index 0000000..47e60ed --- /dev/null +++ b/public/theme/admin/js/scripts.min.js @@ -0,0 +1 @@ +class VUtils{static makePublic(){VUtils.isUsed||(this.initHandlers(),VUtils.isUsed=!0,console.log("[VUtils] is now available in the Global Space! no VUtils. anymore needed"))}static initHandlers(){window.$=this.$,window.$$=this.$$,window.tryCatch=this.tryCatch,VUtils.nodePrototypes()}static $(e,t){return(t=t||document).querySelector(e)}static $$(e,t){return(t=t||document).querySelectorAll(e)}static tryCatch(e,t,i){e=VUtils.wrap(e,[]),i=i||console.error,t=t||console.log;try{t(...e)}catch(e){i(e)}}static forEach(e,t,i){for(let s=0;s{let n=e.target;if(e.detail instanceof HTMLElement&&(n=e.detail),n instanceof HTMLElement)if(n.matches(t))VUtils.tryCatch([e,n],i,s);else{const o=n.find(t);o&&VUtils.tryCatch([e,o],i,s)}})},Node.prototype.addMultiListener=function(e,t,i={}){let s=e.split(" ");for(let e of s)this.addEventListener(e,t,i)}}}VUtils.makePublic();class VRipple{constructor(e={}){if(!VUtils.isUsed)throw"VRipply is only with Public VUtils usable!";let t=this;if(t.options=JSON.parse('{"classes":["btn-ripple__effect"],"target":"body","selector":".btn-ripple"}'),VUtils.mergeOptions(t.options,e),t.options.selector.indexOf("#")>-1)throw"ID's are not allowed as selector!";this.instanceCheck(),this.ripples=[],requestAnimationFrame(this.initHandler.bind(this))}instanceCheck(){let e=this.options;const t=[e.target,e.selector,e.classes.join(".")].join(" ");VRipple.instances=VRipple.instances||{},VRipple.instances[t]=this}initHandler(){let e=this,t=e.options.selector;$(e.options.target).addDelegatedEventListener("mousedown touchstart",t,(t,i)=>{let s=t.touches?t.touches[0]:t,n=i.parentNode,o=i.createNew("span",e.options),l=n.getBoundingClientRect(),r=s.clientX-l.left,a=s.clientY-l.top;o.style.top=a+"px",o.style.left=r+"px",o._mouseDown=!0,o._animationEnded=!1,e.ripples.push(o)}),document.body.addDelegatedEventListener("animationend","."+VUtils.get(e.options.classes,""),e.rippleEnd.bind(e)),document.body._vRippleInit||(document.body.addMultiListener("mouseup touchend mouseleave rippleClose",t=>{let i=Object.keys(VRipple.instances);for(let s of i)for(let i of VRipple.instances[s].ripples)e.rippleEnd.bind(VRipple.instances[s])(t,i)}),document.body._vRippleInit=!0)}rippleEnd(e,t){t.parentNode&&("animationend"===e.type?t._animationEnded=!0:t._mouseDown=!1,!t._mouseDown&&t._animationEnded&&(t.classList.contains("to-remove")?(t.parentNode.removeChild(t),this.ripples.splice(this.ripples.indexOf(t),1)):t.classList.add("to-remove")))}}const rippler=new VRipple;class FormHandler{constructor(e,t,i,s){this.cb=i||console.log,this.err=s||console.err,$(t).addDelegatedEventListener("submit",e,this.handleEvent.bind(this))}handleEvent(e,t){if(e.preventDefault(),t.checkValidity()){if(""===(t.action??""))return void console.error("No URL Found on Form",t);fetch(t.action,{method:t.method.toUpperCase(),credentials:"same-origin",body:new FormData(t),redirect:"manual"}).then(e=>{if(!e.ok)throw new Error("Network response errored");return e.json()}).then(e=>this.cb(e,t)).catch(e=>this.err(e,t))}else VUtils.forEach($$("input",t),e=>{if(!e.checkValidity()){let t=e.parentNode;t.classList.remove("valid"),t.classList.add("invalid")}})}}$("body").addDelegatedEventListener("change input","input",(e,t)=>{let i=$(".error-message",t.find("form"));i&&i.classList.add("hide");let s=t.parentNode;""===t.value?s.classList.remove("focus"):s.classList.add("focus"),t.checkValidity()?(s.classList.add("valid"),s.classList.remove("invalid")):(s.classList.remove("valid"),s.classList.add("invalid"))}),$("#login")&&new FormHandler("form#login","body",()=>{location.reload()},(e,t)=>{$(".error-message",t).classList.remove("hide")}); \ No newline at end of file diff --git a/src/Venom/Admin/AdminController.php b/src/Venom/Admin/AdminController.php index 80f37a4..62d9235 100644 --- a/src/Venom/Admin/AdminController.php +++ b/src/Venom/Admin/AdminController.php @@ -24,7 +24,7 @@ class AdminController implements RenderController public function render(VenomRenderer $renderer): bool { - if (URLHelper::getInstance()->getUrl() !== '/admin/') { + if (!in_array(URLHelper::getInstance()->getUrl(), ['/admin/', '/admin'])) { http_response_code(404); $this->tpl = 'async'; } @@ -32,9 +32,9 @@ class AdminController implements RenderController $isLogin = Security::get()->hasRole(User::ADMIN_ROLE); $renderer->addVar('isLoggedIn', $isLogin); if (!$isLogin) { - Asset::get()->addCSS('login','login.css'); + Asset::get()->addCSS('login', 'login.css'); } - Asset::get()->addCSS('styles','style.css', 1); + Asset::get()->addCSS('styles', 'style.css', 1); Asset::get()->addJS('scripts', 'scripts.min.js', 1); return true; diff --git a/src/Venom/Helper/URLHelper.php b/src/Venom/Helper/URLHelper.php index 9891eaa..697b8fa 100644 --- a/src/Venom/Helper/URLHelper.php +++ b/src/Venom/Helper/URLHelper.php @@ -45,6 +45,6 @@ class URLHelper public function isAdminUrl(): bool { - return strpos($this->parsedUrl, '/admin/') === 0; + return strpos($this->parsedUrl, '/admin') === 0; } } \ No newline at end of file diff --git a/src/Venom/Security/BaseLogin.php b/src/Venom/Security/BaseLogin.php index e4de7e9..5343391 100644 --- a/src/Venom/Security/BaseLogin.php +++ b/src/Venom/Security/BaseLogin.php @@ -31,7 +31,11 @@ class BaseLogin implements Login public function redirect(): void { $url = ArgumentHandler::get()->getPostItem('REDIRECT_TO', URLHelper::getInstance()->getUrl()); - header('Location: ' . $url); + if($url === 'NO') { + echo json_encode(['message' => 'login'], JSON_THROW_ON_ERROR); + } else { + header('Location: ' . $url); + } die(); } diff --git a/tpl/admin/login.php b/tpl/admin/login.php index 50f49a0..62eff0c 100644 --- a/tpl/admin/login.php +++ b/tpl/admin/login.php @@ -1,15 +1,16 @@

- Admin Login -
Be Carefully!

-
+

Login Failed

+
- - + + Username is required
- + Password is required