From 7e89073249e1793b7ab23a272dd8590e9eefcc34 Mon Sep 17 00:00:00 2001 From: ZhurmilovVadim Date: Mon, 4 Mar 2024 12:23:44 +0300 Subject: [PATCH] =?UTF-8?q?=D0=9F=D0=BE=D0=BF=D1=80=D0=B0=D0=B2=D0=B8?= =?UTF-8?q?=D0=BB=20=D0=B2=D0=B5=D1=80=D1=81=D1=82=D0=BA=D1=83=20=D0=B8=20?= =?UTF-8?q?=D0=B8=D1=81=D0=BF=D1=80=D0=B0=D0=B2=D0=B8=D0=BB=20=D0=B2=D0=B0?= =?UTF-8?q?=D0=BB=D0=B8=D0=B4=D0=B0=D1=86=D0=B8=D1=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.sh | 1 + package.json | 3 +- public/config.js | 17 ++- public/index.css | 1 + public/index.html | 3 +- public/src/assets/close.png | Bin 0 -> 407 bytes public/src/assets/logo.png | Bin 0 -> 2172 bytes public/src/components/button/button.hbs | 2 +- public/src/components/button/button.js | 22 ++- public/src/components/button/events.js | 4 +- public/src/components/footer/footer.js | 6 +- public/src/components/header/header.css | 17 --- public/src/components/header/header.js | 66 ++++----- public/src/components/image/image.js | 12 +- public/src/components/input/events.js | 4 +- public/src/components/input/input.css | 19 ++- public/src/components/input/input.hbs | 4 + public/src/components/input/input.js | 37 +++-- public/src/components/link/link.js | 20 ++- public/src/components/logo/logo.css | 8 ++ public/src/components/logo/logo.hbs | 3 + public/src/components/logo/logo.js | 37 +++++ .../components/note-editor/note-editor.css | 49 ++++++- .../components/note-editor/note-editor.hbs | 7 +- .../src/components/note-editor/note-editor.js | 59 +++++++- public/src/components/note/note.css | 6 + public/src/components/note/note.hbs | 2 +- public/src/components/note/note.js | 37 ++++- public/src/components/notes/notes.js | 34 ----- .../settings-panel/settings-panel.js | 47 +++---- public/src/components/span/span.js | 12 +- public/src/components/wrapper/wrapper.js | 6 +- public/src/modules/ajax.js | 131 ++++++++++-------- public/src/modules/dispathcer.js | 2 +- public/src/modules/eventMaker.js | 2 +- public/src/modules/router.js | 60 ++++---- public/src/modules/utils.js | 3 + public/src/pages/home/home.css | 2 +- public/src/pages/home/home.hbs | 2 +- public/src/pages/home/home.js | 52 +++---- public/src/pages/login/login.css | 12 +- public/src/pages/login/login.hbs | 2 +- public/src/pages/login/login.js | 95 ++++++------- public/src/pages/notFound/not-found.js | 43 ++---- public/src/pages/notes/events.js | 3 + public/src/pages/notes/notes.css | 12 +- public/src/pages/notes/notes.hbs | 4 + public/src/pages/notes/notes.js | 75 +++++----- public/src/pages/page.js | 37 +++++ public/src/pages/register/register.css | 8 +- public/src/pages/register/register.hbs | 2 +- public/src/pages/register/register.js | 104 ++++++++------ public/src/shared/uuid.js | 6 +- public/src/shared/validation.js | 32 +++-- public/src/stores/user/events.js | 8 +- public/src/stores/user/userStore.js | 30 ++-- 56 files changed, 720 insertions(+), 552 deletions(-) create mode 100644 public/src/assets/close.png create mode 100644 public/src/assets/logo.png create mode 100644 public/src/components/logo/logo.css create mode 100644 public/src/components/logo/logo.hbs create mode 100644 public/src/components/logo/logo.js delete mode 100644 public/src/components/notes/notes.js create mode 100644 public/src/modules/utils.js create mode 100644 public/src/pages/notes/events.js create mode 100644 public/src/pages/page.js diff --git a/build.sh b/build.sh index 9b1b90b0..c3bd47c7 100644 --- a/build.sh +++ b/build.sh @@ -16,4 +16,5 @@ handlebars -m public/src/components/footer/footer.hbs -f public/build/footer.js handlebars -m public/src/components/button/button.hbs -f public/build/button.js handlebars -m public/src/components/settings-panel/settings-panel.hbs -f public/build/settings-panel.js handlebars -m public/src/components/span/span.hbs -f public/build/span.js +handlebars -m public/src/components/logo/logo.hbs -f public/build/logo.js diff --git a/package.json b/package.json index cd5e09f4..843da0f3 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,8 @@ { "scripts": { "start": "npm run build && node server/index.js", - "build": "./build.sh" + "lint": "npx eslint --fix ./public/src", + "build": "build.sh" }, "dependencies": { "handlebars": "^4.7.8", diff --git a/public/config.js b/public/config.js index d2d8aef6..aa24694d 100644 --- a/public/config.js +++ b/public/config.js @@ -10,14 +10,24 @@ const homePage = { const notesPage = { id: "notes-page", href: "/notes", - needAuth: true + needAuth: true, + noteEditor: { + id: "note-editor", + closeBtn: { + id: "close-editor-btn", + src: "/src/assets/close.png" + } + } } const header = { name: "YouNote", logo: { - href: "/", - text: "YouNote" + id: "logo", + img: { + id: "logo-icon", + src: "/src/assets/logo.png" + } }, settings: { id: "settings-wrapper", @@ -170,7 +180,6 @@ export const config = { notFoundPage: notFoundPage, header: header, notes: notes, - noteEditor: noteEditor, note: note, avatar: avatar, wrapper: wrapper, diff --git a/public/index.css b/public/index.css index f4f2d624..14df90b4 100644 --- a/public/index.css +++ b/public/index.css @@ -14,6 +14,7 @@ @import "src/components/wrapper/wrapper.css"; @import "src/components/footer/footer.css"; @import "src/components/settings-panel/settings-panel.css"; +@import "src/components/logo/logo.css"; *, *::before, *::after { box-sizing: border-box; diff --git a/public/index.html b/public/index.html index 6d7dbe93..1a873bd6 100644 --- a/public/index.html +++ b/public/index.html @@ -3,12 +3,13 @@ - Document + YouNote + diff --git a/public/src/assets/close.png b/public/src/assets/close.png new file mode 100644 index 0000000000000000000000000000000000000000..466786ce340d58a56752e306b89bc5689dd8b9d3 GIT binary patch literal 407 zcmV;I0cie-P)x><=^`Wz#Tx{)&syKU24@&FJ;1PY(}t;So=BFvt>)=h-l5Vtjaak0yah zgcYvR%k#DP)NlJ)2&43Q69m$Hk<$)JG7hPLwgn5 za}wZ*Hc3pHJk4w80)cA1;Eq-_sK0fy!YctD`5^dMfM7ciY&;eq*bW36vP(YYSASsh z7oTA^n*-&KTNuosHw*e@kDy1N{yy826=3JKuUYot`#ulS6!`b|rR0BE=(8PJ0dn7* zMS7QR=yf`u2l5xRCPAH1qFb-Nez7MjKveVQ3>o%IzYOFrxVjPY7LR-od@Mk)9SAmL zW5B`h_A-Co?2t->R$(hIzWh2<-g?((d$Iy(>*~0E{aQ$60RTyoxK(-GFScX_7(Hej zqsNT%%Rv5uf-De$ZJ#p-+qU0sU;$9)*y*8(0e!Nbr(JUUYTzWcP2TOmVo3_?79Jjv zhE0s4K!W3S4~+obGyAc>tRML~_EZ%mJrF*ZFo;e7h7jO`0X}|8F5dgrT z(}m$ce$I*mB(!cvOl(VjpSOd~y$6F$gumIZk&OXXtCjQT&SJKNP8O&&2x|0#v*S9o2PX9o@wC$3n;i7OUSd`Tzzzc5k&B0fPa0HKO&Dc=38M64+`)R)yq zb)t2iL>vh(NA>q93Enk-tDvhBOc$h_&}ICgM19n#X-xcvZ| z*RSH^CGWHB?cvN?zMtfdy#Uy@avnFXT>@J$ya#RsNb8u+$DeL!QbJ1Mmn2 zP^nbRoI4-DNrckEzjNU0?dn$L3cS(laTIlO;!)GI2CIZ5=b`&ZlQ)-v`N`b+q-f zn*k79ZTT~&D=q-Y zl7|pm6C5&pBpp&aHri%18kst2EQJNT8vN%S5f>Fh_oP%#S6o1HLmu1+@Vgna>D)B~ zomQ)migG;BCP|+B>*FZcw~G{YTfTZ_QKRGm@{Vj|-Kjk;fAbGQR)A(vQM@qpMGq-7 z4eH77bv^Cv$UCx;{E}@RlZOz`0!>XGLRbKA$j8RDFe)JrAu513(KL5)?GBPr1l=>tqfL^Dgx~dAD&Ocee zVzE$Ld(UONJ=GQLlTUR3?;l>zrzdvSlTYr@i4(;|EwSC(c)W8Wdm|J`_(DFVQ#ZD5 z$mgR4ZvhMk3OpS*Bw~QlJpUkhfU3Lyvv+CL=!Gs2bOC(%&D+WQf}jfE4fzDM|Hy*} zYl5dvmMF>sk|fbHvk$Sc^5+h`A+J_z*s$XeiOGtN1js^GfR!sgV&VLE6dQe}-vGYd zf5c_G)oN87@YHGzJN6Wjl+x9&+vBnV3>`6wYuBz~QTRCgZ2y6>?W^*aQLt|p0Mq-v zd1)U1ym~fZ6~Zlf;UWgsRNb+E zRP^Lle{d40h=_=6U2?jN6^up$IYWC;{=-FpbfC;rzw9%>3am1lO}vpahV$nXJyLsw z&sXOogg}#$#hG36sIE8zfW;~pHewXpcNOy8KTgV$*P0rVzkBBv3*LUsPQC~Q + \ No newline at end of file diff --git a/public/src/components/button/button.js b/public/src/components/button/button.js index 94b6544c..dc850875 100644 --- a/public/src/components/button/button.js +++ b/public/src/components/button/button.js @@ -1,4 +1,4 @@ -import '../../../build/button.js' +import "../../../build/button.js"; import {create_UUID} from "../../shared/uuid.js"; export class Button { @@ -11,7 +11,6 @@ export class Button { constructor(parent, config, onSubmit) { this.id = create_UUID(); - console.log(this.id); this.#parent = parent; this.#props.id = this.id; this.#props.text = config.text; @@ -19,20 +18,20 @@ export class Button { } get self(){ - return document.getElementById(`submit-btn-${this.id}`); + return document.getElementById(this.id); } #addEventListeners(){ if (this.#onSubmit !== undefined) { - this.self.addEventListener('click', (e) => { - e.preventDefault() - this.#onSubmit() + this.self.addEventListener("click", (e) => { + e.preventDefault(); + this.#onSubmit(); }); } } #removeEventListeners(){ - this.self.removeEventListener('click', this.#onSubmit); + this.self.removeEventListener("click", this.#onSubmit); } remove(){ @@ -42,13 +41,10 @@ export class Button { } render(){ - - this.#parent.insertAdjacentHTML( - 'beforeend', - Handlebars.templates['button.hbs'](this.#props) - ) - + "beforeend", + window.Handlebars.templates["button.hbs"](this.#props) + ); this.#addEventListeners(); } diff --git a/public/src/components/button/events.js b/public/src/components/button/events.js index 711ccc72..fab20063 100644 --- a/public/src/components/button/events.js +++ b/public/src/components/button/events.js @@ -1,3 +1,3 @@ export const SubmitButtonEvents = { - BUTTON_SUBMIT: 'BUTTON_SUBMIT' -} \ No newline at end of file + BUTTON_SUBMIT: "BUTTON_SUBMIT" +}; \ No newline at end of file diff --git a/public/src/components/footer/footer.js b/public/src/components/footer/footer.js index 4e311095..e3762171 100644 --- a/public/src/components/footer/footer.js +++ b/public/src/components/footer/footer.js @@ -1,4 +1,4 @@ -import "../../../build/footer.js" +import "../../../build/footer.js"; export class Footer { #parent; @@ -10,11 +10,11 @@ export class Footer { } get self() { - return document.getElementById(`${this.#config.id}`) + return document.getElementById(`${this.#config.id}`); } render() { const template = window.Handlebars.templates["footer.hbs"]; - this.#parent.insertAdjacentHTML('beforeend', template(this.#config)); + this.#parent.insertAdjacentHTML("beforeend", template(this.#config)); } } \ No newline at end of file diff --git a/public/src/components/header/header.css b/public/src/components/header/header.css index c1770767..5bd92ef9 100644 --- a/public/src/components/header/header.css +++ b/public/src/components/header/header.css @@ -16,26 +16,9 @@ header.black { background: linear-gradient(rgba(0,0,0,0.2), rgba(0,0,0,0)); } -header.black .logo-container a { - color: #fff; -} - -header .logo-container a.white { - color: #fff; -} - -header .logo-container a{ - color: #000; - text-decoration: none; - font-weight: bold; - font-size: 22px; - transition: 0.5s; -} - header .right-container { display: flex; align-items: center; - gap: 25px; } header .menu-container { diff --git a/public/src/components/header/header.js b/public/src/components/header/header.js index 919555e5..fd0b5a3b 100644 --- a/public/src/components/header/header.js +++ b/public/src/components/header/header.js @@ -1,11 +1,11 @@ -import {Link} from "../link/link.js"; -import '../../../build/header.js'; +import "../../../build/header.js"; import {AppEventMaker} from "../../modules/eventMaker.js"; import {UserStoreEvents} from "../../stores/user/events.js"; import {AppUserStore} from "../../stores/user/userStore.js"; import {router} from "../../modules/router.js"; import {Button} from "../button/button.js"; import {SettingsPanel} from "../settings-panel/settings-panel.js"; +import {Logo} from "../logo/logo.js"; export class Header { #parent; @@ -25,49 +25,39 @@ export class Header { } get self () { - return document.getElementById("header") + return document.getElementById("header"); } #addEventListeners(){ - document.addEventListener("scroll", (event) => { - if (window.scrollY > 100) { - this.self.classList.add("black") - } else { - this.self.classList.remove("black") - } - }); - - AppEventMaker.subscribe(UserStoreEvents.SUCCESSFUL_LOGIN, () => { if (this.#settingsPanel === undefined) { - this.#settingsPanel = new SettingsPanel(document.querySelector(".right-container"), this.#config.settings) + this.#settingsPanel = new SettingsPanel(document.querySelector(".right-container"), this.#config.settings); } - this.#settingsPanel.render() + this.#settingsPanel.render(); this.#authPageLink.self.classList.add("hidden"); - }) + }); AppEventMaker.subscribe(UserStoreEvents.CHANGE_PAGE, (href) => { - console.log("CHANGE_PAGE") - console.log(href) + console.log("CHANGE_PAGE"); + console.log(href); if (href === "/") { - this.#logo.self.classList.add("white") if (!AppUserStore.IsAuthenticated()) { this.#authPageLink.self.classList.remove("hidden"); } } else { this.#authPageLink.self.classList.add("hidden"); } - }) + }); AppEventMaker.subscribe(UserStoreEvents.LOGOUT, () => { - console.log("LOGOUT") + console.log("LOGOUT"); - this.#settingsPanel.remove() + this.#settingsPanel.remove(); if (this.#authPageLink === undefined) { - this.#authPageLink = new Button(this.#menu, this.#config.menu.auth, this.handleButtonClick) - this.#authPageLink.render() + this.#authPageLink = new Button(this.#menu, this.#config.menu.auth, this.handleButtonClick); + this.#authPageLink.render(); } else { this.#authPageLink.self.classList.remove("hidden"); } @@ -75,34 +65,34 @@ export class Header { } handleButtonClick = () => { - router.redirect("/login") - } + router.redirect("/login"); + }; render() { - console.log("header render") + console.log("header render"); this.#parent.insertAdjacentHTML( - 'afterbegin', - window.Handlebars.templates['header.hbs'](this.#config) + "afterbegin", + window.Handlebars.templates["header.hbs"](this.#config) ); - this.#logo = new Link(document.querySelector(".logo-container"), this.#config.logo) - this.#logo.render() + this.#logo = new Logo(document.querySelector(".logo-container"), this.#config.logo); + this.#logo.render(); - const rightContainer = document.querySelector(".right-container") + const rightContainer = document.querySelector(".right-container"); - this.#menu = document.createElement("div") - this.#menu.className = "menu-container" + this.#menu = document.createElement("div"); + this.#menu.className = "menu-container"; - rightContainer.appendChild(this.#menu) + rightContainer.appendChild(this.#menu); if (AppUserStore.IsAuthenticated()) { - this.#settingsPanel = new SettingsPanel(document.querySelector(".right-container"), this.#config.settings) - this.#settingsPanel.render() + this.#settingsPanel = new SettingsPanel(document.querySelector(".right-container"), this.#config.settings); + this.#settingsPanel.render(); } else { - this.#authPageLink = new Button(this.#menu, this.#config.menu.auth, this.handleButtonClick) - this.#authPageLink.render() + this.#authPageLink = new Button(this.#menu, this.#config.menu.auth, this.handleButtonClick); + this.#authPageLink.render(); } this.#addEventListeners(); diff --git a/public/src/components/image/image.js b/public/src/components/image/image.js index 69299696..df6cc6f6 100644 --- a/public/src/components/image/image.js +++ b/public/src/components/image/image.js @@ -1,4 +1,4 @@ -import "../../../build/image.js" +import "../../../build/image.js"; export class Image { #parent; @@ -10,21 +10,21 @@ export class Image { } get self() { - return document.getElementById(this.#config.id) + return document.getElementById(this.#config.id); } updateImage(path) { - this.self.src = path + this.self.src = path; } render() { this.#parent.insertAdjacentHTML( - 'afterbegin', - window.Handlebars.templates['image.hbs'](this.#config) + "afterbegin", + window.Handlebars.templates["image.hbs"](this.#config) ); if (this.#config.src) { - this.updateImage(this.#config.src) + this.updateImage(this.#config.src); } } } diff --git a/public/src/components/input/events.js b/public/src/components/input/events.js index d3eac25e..805a7e5d 100644 --- a/public/src/components/input/events.js +++ b/public/src/components/input/events.js @@ -1,3 +1,3 @@ export const inputEvents = { - INPUT_CHANGE: 'INPUT_CHANGE' -} \ No newline at end of file + INPUT_CHANGE: "INPUT_CHANGE" +}; \ No newline at end of file diff --git a/public/src/components/input/input.css b/public/src/components/input/input.css index f8a7072d..15308e9d 100644 --- a/public/src/components/input/input.css +++ b/public/src/components/input/input.css @@ -1,5 +1,5 @@ .input-container { - width: 240px; + width: 100%; position: relative; } @@ -13,23 +13,22 @@ transition: 0.3s; } - .input-container input:focus { border: 1px solid #56A6F0; } -.input-container[data-error] input { +.input-container.success input { + border: 1px solid green; +} + +.input-container.error input { border: 1px solid #c92432; - color: #c92432; - background: #fffafa; } -.input-container[data-error]::after { - content: attr(data-error); +.input-container .errors-container { + height: 30px; color: #c92432; - font-size: 0.85em; - display: block; - margin: 5px 0; + font-size: 13px; } .input-container .show-password-btn { diff --git a/public/src/components/input/input.hbs b/public/src/components/input/input.hbs index 58f5b0af..eaa74316 100644 --- a/public/src/components/input/input.hbs +++ b/public/src/components/input/input.hbs @@ -1,6 +1,10 @@
+
+ +
+ {{#if isPassword}} {{/if}} diff --git a/public/src/components/input/input.js b/public/src/components/input/input.js index 7aacf2e7..184d706f 100644 --- a/public/src/components/input/input.js +++ b/public/src/components/input/input.js @@ -1,4 +1,4 @@ -import "../../../build/input.js" +import "../../../build/input.js"; import {inputEvents} from "./events.js"; import {AppEventMaker} from "../../modules/eventMaker.js"; import {create_UUID} from "../../shared/uuid.js"; @@ -7,9 +7,9 @@ export class Input { #parent; #config; #listeners = { - showPassword: '', - change: '' - } + showPassword: "", + change: "" + }; #image; #input; @@ -26,7 +26,7 @@ export class Input { this.#listeners.showPassword = this.#showPassword.bind(this); this.#listeners.change = this.#change.bind(this); - this.#config.isPassword = this.#config.type === 'password'; + this.#config.isPassword = this.#config.type === "password"; } get self() { @@ -38,13 +38,20 @@ export class Input { } throwError(message) { - this.self.dataset.error = message + this.self.classList.remove("success"); + this.self.classList.add("error"); + this.self.querySelector(".errors-container").innerText = message; } - #change(){ - AppEventMaker.notify(inputEvents.INPUT_CHANGE, this.#config.id); + cleanError() { + this.self.classList.remove("error"); + this.self.querySelector(".errors-container").innerText = ""; } + #change = () => { + AppEventMaker.notify(inputEvents.INPUT_CHANGE, this.#config.id); + }; + #showPassword() { if (this.#input.type === "password") { this.#input.type = "text"; @@ -57,16 +64,18 @@ export class Input { #addEventListeners() { if(this.#config.isPassword){ - this.#image.addEventListener('click', this.#listeners.showPassword); + this.#image.addEventListener("click", this.#listeners.showPassword); } - this.#input.addEventListener('change', this.#listeners.change); + + this.#input.addEventListener("input", this.#listeners.change); } #removeEventListeners() { if (this.#config.isPassword){ - this.#image.removeEventListener('click', this.#listeners.showPassword); + this.#image.removeEventListener("click", this.#listeners.showPassword); } - this.#input.removeEventListener('change', this.#listeners.change); + + this.#input.removeEventListener("input", this.#listeners.change); } remove(){ @@ -74,11 +83,11 @@ export class Input { } render() { - const template = Handlebars.templates["input.hbs"]; + const template = window.Handlebars.templates["input.hbs"]; if(this.self === null){ this.#parent.insertAdjacentHTML( - 'beforeend', + "beforeend", template(this.#config) ); diff --git a/public/src/components/link/link.js b/public/src/components/link/link.js index 6ddcd2b8..2405ae72 100644 --- a/public/src/components/link/link.js +++ b/public/src/components/link/link.js @@ -1,4 +1,4 @@ -import "../../../build/link.js" +import "../../../build/link.js"; import {router} from "../../modules/router.js"; import {create_UUID} from "../../shared/uuid.js"; @@ -14,7 +14,7 @@ export class Link { this.#parent = parent; - this.#props.id = this.id ; + this.#props.id = this.id; this.#props.text = config.text; this.#props.href = config.href; } @@ -24,22 +24,20 @@ export class Link { } handleClick = (e) => { - e.preventDefault() - router.redirect(this.#props.href) - } + e.preventDefault(); + router.redirect(this.#props.href); + }; #addListeners () { - this.self.addEventListener("click", this.handleClick) + this.self.addEventListener("click", this.handleClick); } - render() { - this.#parent.insertAdjacentHTML( - 'beforeend', - window.Handlebars.templates['link.hbs'](this.#props) + "beforeend", + window.Handlebars.templates["link.hbs"](this.#props) ); - this.#addListeners() + this.#addListeners(); } } diff --git a/public/src/components/logo/logo.css b/public/src/components/logo/logo.css new file mode 100644 index 00000000..5c072f49 --- /dev/null +++ b/public/src/components/logo/logo.css @@ -0,0 +1,8 @@ +#logo{ + cursor: pointer; +} + +#logo img { + width: 36px; + height: 36px; +} \ No newline at end of file diff --git a/public/src/components/logo/logo.hbs b/public/src/components/logo/logo.hbs new file mode 100644 index 00000000..c78e6e92 --- /dev/null +++ b/public/src/components/logo/logo.hbs @@ -0,0 +1,3 @@ +
+ +
\ No newline at end of file diff --git a/public/src/components/logo/logo.js b/public/src/components/logo/logo.js new file mode 100644 index 00000000..97ba3e08 --- /dev/null +++ b/public/src/components/logo/logo.js @@ -0,0 +1,37 @@ +import {Image} from "../image/image.js"; +import "../../../build/logo.js"; +import {AppUserStore} from "../../stores/user/userStore.js"; +import {router} from "../../modules/router.js"; + +export class Logo { + #parent; + #config; + + #img; + + constructor(parent, config) { + this.#parent = parent; + this.#config = config; + } + + get self(){ + return document.getElementById(this.#config.id); + } + + #handleClick() { + const href = AppUserStore.IsAuthenticated() ? "/notes" : "/"; + router.redirect(href); + } + + render() { + this.#parent.insertAdjacentHTML( + "afterbegin", + window.Handlebars.templates["logo.hbs"](this.#config) + ); + + this.self.addEventListener("click", this.#handleClick) + + this.#img = new Image(this.self, this.#config.img); + this.#img.render(); + } +} \ No newline at end of file diff --git a/public/src/components/note-editor/note-editor.css b/public/src/components/note-editor/note-editor.css index e03eb6c1..b01c6af5 100644 --- a/public/src/components/note-editor/note-editor.css +++ b/public/src/components/note-editor/note-editor.css @@ -1,7 +1,54 @@ #note-editor { width: 500px; - height: 400px; + height: 500px; border-radius: 15px; background: #fff; box-shadow: rgba(149, 157, 165, 0.2) 0px 8px 24px; + display: none; + flex-direction: column; + padding: 20px; + gap: 10px; + position: relative; +} + +#note-editor.active { + display: flex; +} + +#note-editor h2 { + font-size: 24px; +} + +#note-editor span { + font-size: 16px; + color: #4d4e53; +} + +#note-editor .note-content { + padding-right: 10px; + overflow-y: scroll; + height: 100%; +} + +#note-editor .note-content::-webkit-scrollbar-track { + box-shadow: inset 0 0 4px rgba(0, 0, 0, 0.2); + border-radius: 10px; +} + +#note-editor .note-content::-webkit-scrollbar { + width: 6px; +} + +#note-editor .note-content::-webkit-scrollbar-thumb { + border-radius: 10px; + box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.1); +} + +#note-editor #close-editor-btn { + position: absolute; + right: 10px; + top: 10px; + width: 26px; + height: 26px; + cursor: pointer; } \ No newline at end of file diff --git a/public/src/components/note-editor/note-editor.hbs b/public/src/components/note-editor/note-editor.hbs index a6db875a..f3768e92 100644 --- a/public/src/components/note-editor/note-editor.hbs +++ b/public/src/components/note-editor/note-editor.hbs @@ -1,3 +1,8 @@ -
+
+
+
+
+ +
\ No newline at end of file diff --git a/public/src/components/note-editor/note-editor.js b/public/src/components/note-editor/note-editor.js index 9196e078..47314a50 100644 --- a/public/src/components/note-editor/note-editor.js +++ b/public/src/components/note-editor/note-editor.js @@ -1,19 +1,66 @@ -import "../../../build/note-editor.js" +import "../../../build/note-editor.js"; +import {AppEventMaker} from "../../modules/eventMaker.js"; +import {noteEvents} from "../../pages/notes/events.js"; +import {Image} from "../image/image.js" export class NoteEditor{ #parent; - #config; + #active; + constructor(parent, config) { this.#parent = parent; this.#config = config; + + this.#active = false; + } + + get self() { + return document.getElementById(this.#config.id); + } + + #onNoteSelect (note) { + this.self.querySelector(".note-title").innerHTML = ""; + this.self.querySelector(".note-content").innerHTML = ""; + + const title = document.createElement("h2"); + title.innerText = note.data.title; + this.self.querySelector(".note-title").appendChild(title); + + const content = document.createElement("span"); + content.innerText = note.data.content; + this.self.querySelector(".note-content").appendChild(content); + + this.self.classList.add("active"); + } + + #closeEditor = () => { + this.self.classList.remove("active"); + } + + #subscribeToEvents() { + AppEventMaker.subscribe(noteEvents.NOTE_SELECTED, (e) => this.#onNoteSelect(e)); + } + + #unsubscribeToEvents() { + AppEventMaker.unsubscribe(noteEvents.NOTE_SELECTED,(e) => this.#onNoteSelect(e)); + } + + remove() { + this.#unsubscribeToEvents(); } render() { - const tmp = document.createElement('div'); - const template = Handlebars.templates["note-editor.hbs"]; - tmp.innerHTML = template(this.#config.mainPage); - this.#parent.appendChild(tmp.firstElementChild); + this.#parent.insertAdjacentHTML( + "beforeend", + window.Handlebars.templates["note-editor.hbs"](this.#config) + ); + + const closeBtn = new Image(this.self, this.#config.closeBtn) + closeBtn.render() + closeBtn.self.addEventListener("click", this.#closeEditor) + + this.#subscribeToEvents(); } } diff --git a/public/src/components/note/note.css b/public/src/components/note/note.css index a120ddf9..7923cb9e 100644 --- a/public/src/components/note/note.css +++ b/public/src/components/note/note.css @@ -7,4 +7,10 @@ border-radius: 15px; padding: 15px; width: 300px; + cursor: pointer; + transition: 0.5s; +} + +.note-container:hover { + transform: scale(1.1); } \ No newline at end of file diff --git a/public/src/components/note/note.hbs b/public/src/components/note/note.hbs index 05657f60..372f40d0 100644 --- a/public/src/components/note/note.hbs +++ b/public/src/components/note/note.hbs @@ -1,4 +1,4 @@ -
+

{{title}}

{{content}}

\ No newline at end of file diff --git a/public/src/components/note/note.js b/public/src/components/note/note.js index 51948661..29d3a24b 100644 --- a/public/src/components/note/note.js +++ b/public/src/components/note/note.js @@ -1,19 +1,42 @@ -import "../../../build/note.js" +import "../../../build/note.js"; +import {truncate} from "../../modules/utils.js"; +import {AppEventMaker} from "../../modules/eventMaker.js"; +import {noteEvents} from "../../pages/notes/events.js"; export class Note { #parent; - + #props; #config; - constructor(parent, config) { + #selectNote; + + constructor(parent, config, selectNote) { this.#parent = parent; this.#config = config; + this.#selectNote = selectNote; + this.#props = { + id: this.#config.id, + title: this.#config.data.title, + content: truncate(this.#config.data.content, 50) + }; + } + + get self() { + return document.getElementById(this.#config.id); } render() { - const tmp = document.createElement('div'); - const template = Handlebars.templates["note.hbs"]; - tmp.innerHTML = template(this.#config.note); - this.#parent.appendChild(tmp.firstElementChild); + this.#parent.insertAdjacentHTML( + "beforeend", + window.Handlebars.templates["note.hbs"](this.#props) + ); + + this.self.addEventListener("click", () => { + console.log("click"); + console.log(this.#config.id); + AppEventMaker.notify(noteEvents.NOTE_SELECTED, this.#config); + }); + + console.log(this.#props); } } \ No newline at end of file diff --git a/public/src/components/notes/notes.js b/public/src/components/notes/notes.js deleted file mode 100644 index 2bdce11b..00000000 --- a/public/src/components/notes/notes.js +++ /dev/null @@ -1,34 +0,0 @@ -import {Note} from "../note/note.js"; -import "../../../build/notes.js" -import {AppNoteRequests} from "../../modules/ajax.js"; - -export class NotesContainer { - #parent; - - #config; - - constructor(parent, config) { - this.#parent = parent; - this.#config = config; - } - - #renderNotes = (self, notes) => { - for (const note of notes) { - const noteClass = new Note(self, {note: {title: note.data.title, content: note.data.content}}); - noteClass.render(); - } - } - - render() { - const tmp = document.createElement('div'); - const template = Handlebars.templates["notes.hbs"]; - tmp.innerHTML = template(this.#config.notes); - this.#parent.appendChild(tmp.firstElementChild); - - const self = document.getElementById('notes-container'); - - AppNoteRequests.GetAll().then(resp => { - this.#renderNotes(self, resp) - }) - } -} diff --git a/public/src/components/settings-panel/settings-panel.js b/public/src/components/settings-panel/settings-panel.js index d272fbf2..20c22785 100644 --- a/public/src/components/settings-panel/settings-panel.js +++ b/public/src/components/settings-panel/settings-panel.js @@ -1,12 +1,9 @@ -import '../../../build/settings-panel.js' +import "../../../build/settings-panel.js"; import {Image} from "../image/image.js"; import {Button} from "../button/button.js"; import {Span} from "../span/span.js"; import {AppUserStore, UserActions} from "../../stores/user/userStore.js"; import {AppDispatcher} from "../../modules/dispathcer.js"; -import {router} from "../../modules/router.js"; -import {AppEventMaker} from "../../modules/eventMaker.js"; -import {UserStoreEvents} from "../../stores/user/events.js"; export class SettingsPanel { #parent; @@ -22,53 +19,53 @@ export class SettingsPanel { } get button() { - return document.getElementById("settings-button") + return document.getElementById("settings-button"); } get panel(){ - return document.getElementById(this.#config.panel.id) + return document.getElementById(this.#config.panel.id); } remove() { - this.#removeEventListeners() - this.self.remove() + this.#removeEventListeners(); + this.self.remove(); } handleLogout() { AppDispatcher.dispatch({ type: UserActions.LOGOUT - }) + }); } handleClick = (e) => { - e.preventDefault() - this.self.classList.toggle("show") - } + e.preventDefault(); + this.self.classList.toggle("show"); + }; #addEventListeners(){ - this.button.addEventListener('click', this.handleClick); + this.button.addEventListener("click", this.handleClick); } #removeEventListeners(){ - this.button.removeEventListener('click', this.handleClick); + this.button.removeEventListener("click", this.handleClick); } render(){ this.#parent.insertAdjacentHTML( - 'beforeend', - Handlebars.templates['settings-panel.hbs'](this.#config) - ) + "beforeend", + window.Handlebars.templates["settings-panel.hbs"](this.#config) + ); - const avatar = new Image(this.panel, this.#config.panel.avatar) - avatar.render() - avatar.updateImage(AppUserStore.avatar) + const avatar = new Image(this.panel, this.#config.panel.avatar); + avatar.render(); + avatar.updateImage(AppUserStore.avatar); - const span = new Span(this.panel, this.#config.panel.username) - span.render() - span.setText(AppUserStore.username) + const span = new Span(this.panel, this.#config.panel.username); + span.render(); + span.setText(AppUserStore.username); - const logoutBtn = new Button(this.panel, this.#config.panel.logoutBtn, this.handleLogout) - logoutBtn.render() + const logoutBtn = new Button(this.panel, this.#config.panel.logoutBtn, this.handleLogout); + logoutBtn.render(); this.#addEventListeners(); } diff --git a/public/src/components/span/span.js b/public/src/components/span/span.js index 59c6baea..afcc188c 100644 --- a/public/src/components/span/span.js +++ b/public/src/components/span/span.js @@ -1,4 +1,4 @@ -import "../../../build/span.js" +import "../../../build/span.js"; export class Span { #parent; @@ -14,14 +14,14 @@ export class Span { } setText(text) { - console.log(text) - this.self.innerHTML = text + console.log(text); + this.self.innerHTML = text; } render() { this.#parent.insertAdjacentHTML( - 'beforeend', - Handlebars.templates['span.hbs'](this.#config) - ) + "beforeend", + window.Handlebars.templates["span.hbs"](this.#config) + ); } } \ No newline at end of file diff --git a/public/src/components/wrapper/wrapper.js b/public/src/components/wrapper/wrapper.js index eb2e2e6a..8c6463ed 100644 --- a/public/src/components/wrapper/wrapper.js +++ b/public/src/components/wrapper/wrapper.js @@ -1,4 +1,4 @@ -import "../../../build/wrapper.js" +import "../../../build/wrapper.js"; export class Wrapper { #parent; @@ -10,11 +10,11 @@ export class Wrapper { } get self() { - return document.getElementById(`${this.#config.id}`) + return document.getElementById(`${this.#config.id}`); } render() { const template = window.Handlebars.templates["wrapper.hbs"]; - this.#parent.insertAdjacentHTML('afterbegin', template(this.#config)); + this.#parent.insertAdjacentHTML("afterbegin", template(this.#config)); } } \ No newline at end of file diff --git a/public/src/modules/ajax.js b/public/src/modules/ajax.js index 8183f8b9..852fde37 100644 --- a/public/src/modules/ajax.js +++ b/public/src/modules/ajax.js @@ -1,62 +1,71 @@ -const baseUrl = "http://you-note.ru:8080/api" +import {AppEventMaker} from "./eventMaker.js"; +import {UserStoreEvents} from "../stores/user/events.js"; + +const isDebug = false; + +const baseUrl = `http://${isDebug ? "127.0.0.1" : "you-note.ru"}:8080/api`; const methods = { - POST: 'POST', - GET: 'GET', - DELETE: 'DELETE', - PUT: 'PUT' -} + POST: "POST", + GET: "GET", + DELETE: "DELETE", + PUT: "PUT" +}; -let JWT = null +let JWT = null; -JWT = window.localStorage.getItem('Authorization') +JWT = window.localStorage.getItem("Authorization"); const baseRequest = async (method, url, data = null) => { const options = { method: method, - mode: 'cors', - credentials: 'include', + mode: "cors", + credentials: "include", headers: { - 'Content-Type': 'application/json', - 'Accept': 'application/json', + "Content-Type": "application/json", + "Accept": "application/json", } - } + }; if (JWT !== null) { options.headers.Authorization = JWT; } if (data !== null) { - options.body = JSON.stringify(data) + options.body = JSON.stringify(data); } try{ - const response = await fetch(baseUrl + url, options) - let body = null + const response = await fetch(baseUrl + url, options); + let body = null; try { - body = await response.json() + body = await response.json(); } catch (err) { - console.log("no body") + console.log("no body"); } - if (response.headers.get('Authorization') !== null) { - JWT = response.headers.get('Authorization'); - window.localStorage.setItem('Authorization', JWT) + if (response.headers.get("Authorization") !== null) { + JWT = response.headers.get("Authorization"); + window.localStorage.setItem("Authorization", JWT); } - return {status: response.status, body} + return {status: response.status, body}; } catch (err) { - console.log(err) - return {status: 503, body: {message: err}} + console.log(err); + return {status: 503, body: {message: err}}; } -} +}; class AuthRequests { - #baseUrl = '/auth' + #baseUrl = "/auth"; Login = async (username, password) => { const {status, body} = await baseRequest( - methods.POST,this.#baseUrl + '/login', + methods.POST,this.#baseUrl + "/login", {username, password} - ) + ); + + if (status === 401) { + AppEventMaker.notify(UserStoreEvents.INVALID_LOGIN_OR_PASSWORD); + } if (status === 200) { return { @@ -64,17 +73,21 @@ class AuthRequests { username: body.username, create_time: body.create_time, image_path: body.image_path - } - } else { - throw Error(body.message) + }; } - } + + throw Error(body.message); + }; SignUp = async (username, password) => { const {status, body} = await baseRequest( - methods.POST,this.#baseUrl + '/signup', + methods.POST,this.#baseUrl + "/signup", {username, password} - ) + ); + + if (status === 400) { + AppEventMaker.notify(UserStoreEvents.LOGIN_ALREADY_EXIST); + } if (status === 201) { return { @@ -82,61 +95,61 @@ class AuthRequests { username: body.username, create_time: body.create_time, image_path: body.image_path - } - } else { - throw Error(body.message) + }; } - } + + throw Error(body.message); + }; Logout = async () => { const {status, body} = await baseRequest( - methods.DELETE,this.#baseUrl + '/logout' - ) + methods.DELETE,this.#baseUrl + "/logout" + ); - console.log(status) + console.log(status); if (status === 204){ - console.log("logged out") - JWT = null + console.log("logged out"); + JWT = null; return { - message: 'ok' - } + message: "ok" + }; } else { - throw Error(body.message) + throw Error(body.message); } - } + }; CheckUser = async () => { - const {status, body} = await baseRequest(methods.GET, this.#baseUrl + '/check_user') + const {status, body} = await baseRequest(methods.GET, this.#baseUrl + "/check_user"); if (status === 200) { return body; } else { - throw Error('not authorized'); + throw Error("not authorized"); } - } + }; } class NoteRequests { - #baseUrl = '/note' + #baseUrl = "/note"; GetAll = async () => { const {status, body} = await baseRequest( - methods.GET,this.#baseUrl + '/get_all' - ) + methods.GET,this.#baseUrl + "/get_all" + ); if (status === 200) { for (const elem of body) { - const decoded = atob(elem.data) + const decoded = atob(elem.data); const bytes = Uint8Array.from(decoded, (m) => m.codePointAt(0)); - elem.data = JSON.parse(new TextDecoder().decode(bytes)) + elem.data = JSON.parse(new TextDecoder().decode(bytes)); } - console.log(body) - return body + console.log(body); + return body; } else { - throw Error(body.message) + throw Error(body.message); } - } + }; } export const AppAuthRequests = new AuthRequests(); diff --git a/public/src/modules/dispathcer.js b/public/src/modules/dispathcer.js index 159c9b0c..38cb803d 100644 --- a/public/src/modules/dispathcer.js +++ b/public/src/modules/dispathcer.js @@ -12,7 +12,7 @@ class Dispatcher{ dispatch(action){ this.#callbacks.forEach((callback) => { callback(action); - }) + }); } } diff --git a/public/src/modules/eventMaker.js b/public/src/modules/eventMaker.js index d9d369cb..2a5af1ac 100644 --- a/public/src/modules/eventMaker.js +++ b/public/src/modules/eventMaker.js @@ -24,7 +24,7 @@ class EventMaker{ this.#eventsMap[event].forEach((listener) => { listener.apply(this, args); - }) + }); } } diff --git a/public/src/modules/router.js b/public/src/modules/router.js index d880dfa8..b9367061 100644 --- a/public/src/modules/router.js +++ b/public/src/modules/router.js @@ -19,61 +19,53 @@ class Router { } init(root, config){ - const homePage = new Home(root, config.homePage) - this.registerPage(homePage) + const homePage = new Home(root, config.homePage); + this.registerPage(homePage); - const notesPage = new NotesPage(root, config.notesPage) - this.registerPage(notesPage) + const notesPage = new NotesPage(root, config.notesPage); + this.registerPage(notesPage); - const loginPage = new LoginPage(root, config.loginPage) - this.registerPage(loginPage) + const loginPage = new LoginPage(root, config.loginPage); + this.registerPage(loginPage); - const registerPage = new RegisterPage(root, config.registerPage) - this.registerPage(registerPage) + const registerPage = new RegisterPage(root, config.registerPage); + this.registerPage(registerPage); - const notFoundPage = new NotFoundPage(root, config.notFoundPage) - this.registerPage(notFoundPage) - - console.log("dispatching") + const notFoundPage = new NotFoundPage(root, config.notFoundPage); + this.registerPage(notFoundPage); + console.log("dispatching"); AppDispatcher.dispatch({type: UserActions.CHECK_USER}); + this.redirect(this.#currentUrl); } registerPage(page) { - this.#pages[page.href] = page + this.#pages[page.href] = page; } redirect(href) { - console.log("redirect " + href) - - if (href === "") href = "/" + console.log("redirect " + href); - const page = this.#pages[href] + if (href === "") href = "/"; - console.log(page) + const page = this.#pages[href]; if (page === undefined) { - this.redirect("/404") + this.redirect("/404"); return; } - console.log(page.href) if (page.needAuth === true && !AppUserStore.IsAuthenticated()) { - this.redirect("/") - return - } - - if (page.needAuth === false && AppUserStore.IsAuthenticated()) { - this.redirect("/") - return + this.redirect("/"); + return; } + + this.#currentPage?.remove(); + page.render(); + this.#currentPage = page; + history.pushState(null, null, page.href); - this.#currentPage?.remove() - page.render() - this.#currentPage = page - history.pushState(null, null, page.href) - - AppEventMaker.notify(UserActions.CHANGE_PAGE, href) + AppEventMaker.notify(UserActions.CHANGE_PAGE, href); } parseUrl() { @@ -81,4 +73,4 @@ class Router { } } -export const router = new Router() +export const router = new Router(); diff --git a/public/src/modules/utils.js b/public/src/modules/utils.js new file mode 100644 index 00000000..a2d8637b --- /dev/null +++ b/public/src/modules/utils.js @@ -0,0 +1,3 @@ +export function truncate(str, n){ + return (str.length > n) ? str.slice(0, n-1) + "..." : str; +} \ No newline at end of file diff --git a/public/src/pages/home/home.css b/public/src/pages/home/home.css index 5b15f44d..2c94292e 100644 --- a/public/src/pages/home/home.css +++ b/public/src/pages/home/home.css @@ -81,7 +81,7 @@ } .home .second { - background: #56A6F0; + background: #007FFF; display: flex; justify-content: center; align-items: center; diff --git a/public/src/pages/home/home.hbs b/public/src/pages/home/home.hbs index 8f9d6ab4..fffcaa6a 100644 --- a/public/src/pages/home/home.hbs +++ b/public/src/pages/home/home.hbs @@ -5,7 +5,7 @@

Повысьте свою продуктивность с помощью наших заметок!

- +
diff --git a/public/src/pages/home/home.js b/public/src/pages/home/home.js index 27179eff..c90b23fc 100644 --- a/public/src/pages/home/home.js +++ b/public/src/pages/home/home.js @@ -1,35 +1,15 @@ -import "../../../build/home.js" +import "../../../build/home.js"; import {AppUserStore} from "../../stores/user/userStore.js"; import {router} from "../../modules/router.js"; import {Button} from "../../components/button/button.js"; +import Page from "../page.js"; -export default class Home { - #parent; - #config; - - constructor(parent, config) { - this.#parent = parent; - this.#config = config; - } - - get href () { - return this.#config.href; - } - - get self () { - return document.getElementById(this.#config.id); - } - - remove(){ - console.log("Home remove") - this.self.remove() - } - - +export default class Home extends Page { handleButtonClick = () => { - const href = AppUserStore.IsAuthenticated() ? "/notes" : "/login" - router.redirect(href) - } + const href = AppUserStore.IsAuthenticated() ? "/notes" : "/login"; + router.redirect(href); + }; + createObserver() { let observer = new IntersectionObserver( @@ -48,19 +28,19 @@ export default class Home { targetElements.forEach((targetElement) => { observer.observe(targetElement); }); - }; + } render() { - console.log("Home page render") - - this.#parent.insertAdjacentHTML( - 'afterbegin', - window.Handlebars.templates['home.hbs'](this.#config) + this.parent.insertAdjacentHTML( + "afterbegin", + window.Handlebars.templates["home.hbs"](this.config) ); - const link = new Button(this.self.querySelector(".left-container"), this.#config.linkToLoginPage, this.handleButtonClick) - link.render() + const link = new Button(this.self.querySelector(".left-container"), this.config.linkToLoginPage, this.handleButtonClick); + link.render(); + + this.createObserver(); - this.createObserver() + document.title = "Главная"; } } diff --git a/public/src/pages/login/login.css b/public/src/pages/login/login.css index 5537f204..70a6b262 100644 --- a/public/src/pages/login/login.css +++ b/public/src/pages/login/login.css @@ -8,7 +8,7 @@ } .login-form { - min-width: 300px; + width: 300px; padding: 20px; border-radius: 20px; display: flex; @@ -16,11 +16,11 @@ align-items: center; justify-content: center; background: #ffffff; - width: 300px; - gap: 20px; + gap: 15px; box-shadow: 0 4px 30px -10px rgba(0, 0, 0, 0.2); } -.login-window > div { - margin: 10px; -} \ No newline at end of file +.login-form .login-title { + margin-bottom: 10px; +} + diff --git a/public/src/pages/login/login.hbs b/public/src/pages/login/login.hbs index 5e6244de..e3762b38 100644 --- a/public/src/pages/login/login.hbs +++ b/public/src/pages/login/login.hbs @@ -1,5 +1,5 @@ diff --git a/public/src/pages/login/login.js b/public/src/pages/login/login.js index c6fcf510..8e0bee66 100644 --- a/public/src/pages/login/login.js +++ b/public/src/pages/login/login.js @@ -1,4 +1,4 @@ -import "../../../build/login.js" +import "../../../build/login.js"; import {Input} from "../../components/input/input.js"; import {Button} from "../../components/button/button.js"; import {inputEvents} from "../../components/input/events.js"; @@ -8,56 +8,36 @@ import {UserActions} from "../../stores/user/userStore.js"; import {Link} from "../../components/link/link.js"; import {ValidateLogin, ValidatePassword} from "../../shared/validation.js"; import {router} from "../../modules/router.js"; +import Page from "../page.js"; import {UserStoreEvents} from "../../stores/user/events.js"; -export default class LoginPage { - #parent; - #config; - - needAuth; - +export default class LoginPage extends Page { #loginInput; #passwordInput; #link; #submitBtn; - constructor(parent, config) { - this.#parent = parent; - this.#config = config; - - this.needAuth = false; - } - - get href () { - return this.#config.href; - } - get form () { - return document.getElementById(this.#config.form.id) - } - - get self () { - return document.getElementById(this.#config.id) + return document.getElementById(this.config.form.id); } validateData = () => { - const validateLogin = this.#validateLogin() - const validatePassword = this.#validatePassword() + const validateLogin = this.#validateLogin(); + const validatePassword = this.#validatePassword(); if (validateLogin && validatePassword) { - AppDispatcher.dispatch({ type: UserActions.LOGIN, payload: { login: this.#loginInput.value, password: this.#passwordInput.value } - }) + }); } - } + }; #successfulLogin = () => { - router.redirect("/") - } + router.redirect("/"); + }; #inputEventHandler = (id) => { if(id === this.#passwordInput.id){ @@ -65,22 +45,27 @@ export default class LoginPage { } else if (id === this.#loginInput.id){ this.#validateLogin(); } + }; + + #throwIncorrectData = () => { + this.#loginInput.throwError("Неправильный логин или пароль!"); + this.#passwordInput.throwError("Неправильный логин или пароль!"); } #subscribeToEvents(){ - console.log("subscribeToEvents") - // AppEventMaker.subscribe(UserStoreEvents.SUCCESSFUL_LOGIN, this.#successfulLogin) + console.log("subscribeToEvents"); AppEventMaker.subscribe(inputEvents.INPUT_CHANGE, this.#inputEventHandler); + AppEventMaker.subscribe(UserStoreEvents.INVALID_LOGIN_OR_PASSWORD, this.#throwIncorrectData); } #unsubscribeToEvents(){ - console.log("unsubscribeToEvents login") - // AppEventMaker.unsubscribe(UserStoreEvents.SUCCESSFUL_LOGIN, this.#successfulLogin) + console.log("unsubscribeToEvents login"); AppEventMaker.unsubscribe(inputEvents.INPUT_CHANGE, this.#inputEventHandler); + AppEventMaker.unsubscribe(UserStoreEvents.INVALID_LOGIN_OR_PASSWORD, this.#throwIncorrectData); } #validatePassword(){ - console.log("password validation started") + console.log("password validation started"); delete this.#passwordInput.self.dataset.error; @@ -88,27 +73,33 @@ export default class LoginPage { const validationResult = ValidatePassword(value); - if (!validationResult.result){ + if (validationResult.result) { + this.#passwordInput.cleanError(); + this.#passwordInput.self.classList.add("success"); + } else { this.#passwordInput.throwError(validationResult.message); } - return validationResult.result + return validationResult.result; } #validateLogin(){ - console.log("login validation started") + console.log("login validation started"); - delete this.#loginInput.self.dataset.error + delete this.#loginInput.self.dataset.error; - const value = this.#loginInput.value + const value = this.#loginInput.value; const validationResult = ValidateLogin(value); - if (!validationResult.result){ + if (validationResult.result) { + this.#loginInput.cleanError(); + this.#loginInput.self.classList.add("success"); + } else { this.#loginInput.throwError(validationResult.message); } - return validationResult.result + return validationResult.result; } remove(){ @@ -116,29 +107,31 @@ export default class LoginPage { this.#submitBtn.remove(); this.#passwordInput.remove(); this.#loginInput.remove(); - this.self.remove() + this.self.remove(); } render() { - console.log("loginPage render") + console.log("loginPage render"); - this.#parent.insertAdjacentHTML( - 'beforeend', - window.Handlebars.templates['login.hbs'](this.#config) + this.parent.insertAdjacentHTML( + "beforeend", + window.Handlebars.templates["login.hbs"](this.config) ); - this.#loginInput = new Input(this.form, this.#config.form.inputs.login); + this.#loginInput = new Input(this.form, this.config.form.inputs.login); this.#loginInput.render(); - this.#passwordInput = new Input(this.form, this.#config.form.inputs.password); + this.#passwordInput = new Input(this.form, this.config.form.inputs.password); this.#passwordInput.render(); - this.#link = new Link(this.form, this.#config.form.links.registerPage); + this.#link = new Link(this.form, this.config.form.links.registerPage); this.#link.render(); - this.#submitBtn = new Button(this.form, this.#config.form.buttons.submitBtn, this.validateData); + this.#submitBtn = new Button(this.form, this.config.form.buttons.submitBtn, this.validateData); this.#submitBtn.render(); this.#subscribeToEvents(); + + document.title = "Вход"; } } diff --git a/public/src/pages/notFound/not-found.js b/public/src/pages/notFound/not-found.js index e95b5de5..db82529f 100644 --- a/public/src/pages/notFound/not-found.js +++ b/public/src/pages/notFound/not-found.js @@ -1,41 +1,24 @@ -import "../../../build/not-found.js" +import "../../../build/not-found.js"; import {router} from "../../modules/router.js"; import {Button} from "../../components/button/button.js"; +import Page from "../page.js"; -export default class NotFoundPage { - #parent; - #config; - - constructor(parent, config) { - this.#parent = parent; - this.#config = config; - } - - get href () { - return this.#config.href; - } - - get self () { - return document.getElementById(this.#config.id); - } - - remove() { - this.self.remove() - } - +export default class NotFoundPage extends Page { handleButtonClick = () => { - router.redirect("/") - } + router.redirect("/"); + }; render() { - console.log("404 page render") + console.log("404 page render"); - this.#parent.insertAdjacentHTML( - 'afterbegin', - window.Handlebars.templates['not-found.hbs'](this.#config) + this.parent.insertAdjacentHTML( + "afterbegin", + window.Handlebars.templates["not-found.hbs"](this.config) ); - const link = new Button(this.self, this.#config.link, this.handleButtonClick) - link.render() + const link = new Button(this.self, this.config.link, this.handleButtonClick); + link.render(); + + document.title = "404"; } } diff --git a/public/src/pages/notes/events.js b/public/src/pages/notes/events.js new file mode 100644 index 00000000..a97292bb --- /dev/null +++ b/public/src/pages/notes/events.js @@ -0,0 +1,3 @@ +export const noteEvents = { + NOTE_SELECTED: "NOTE_SELECTED" +}; \ No newline at end of file diff --git a/public/src/pages/notes/notes.css b/public/src/pages/notes/notes.css index 4a395e5a..e04f47e2 100644 --- a/public/src/pages/notes/notes.css +++ b/public/src/pages/notes/notes.css @@ -4,14 +4,18 @@ display: flex; justify-content: center; align-items: center; - gap: 20px; background: #dfe9f5; } -#notes-container { +.wrapper { display: flex; - justify-content: center; + gap: 25px; +} + +.notes-container { + display: flex; + justify-content: flex-start; align-items: center; flex-direction: column; - gap: 20px; + gap: 26px; } \ No newline at end of file diff --git a/public/src/pages/notes/notes.hbs b/public/src/pages/notes/notes.hbs index c78e6e92..ec55f85d 100644 --- a/public/src/pages/notes/notes.hbs +++ b/public/src/pages/notes/notes.hbs @@ -1,3 +1,7 @@
+
+
+
+
\ No newline at end of file diff --git a/public/src/pages/notes/notes.js b/public/src/pages/notes/notes.js index dce80cca..86f36eb2 100644 --- a/public/src/pages/notes/notes.js +++ b/public/src/pages/notes/notes.js @@ -1,53 +1,56 @@ -import "../../../build/notes.js" +import "../../../build/notes.js"; import {Note} from "../../components/note/note.js"; import {AppNoteRequests} from "../../modules/ajax.js"; +import Page from "../page.js"; +import {NoteEditor} from "../../components/note-editor/note-editor.js"; -export default class NotesPage { - #parent; - #config; +export default class NotesPage extends Page { + #notesContainer; - constructor(parent, config) { - this.#parent = parent; - this.#config = config; - } - - get self () { - return document.getElementById(this.#config.id); - } - - get href () { - return this.#config.href; - } - - get needAuth () { - return this.#config.needAuth; - } - - remove() { - this.self.remove() - } + #notesEditor; - #renderNotes = (self, notes) => { + #renderNotes = (notes) => { for (const note of notes) { - const noteClass = new Note(self, {note: {title: note.data.title, content: note.data.content}}); + const noteClass = new Note(this.#notesContainer, note); noteClass.render(); } + }; + + remove() { + this.#notesEditor.remove(); + super.remove(); } render() { - this.#parent.insertAdjacentHTML( - 'afterbegin', - window.Handlebars.templates['notes.hbs'](this.#config) + this.parent.insertAdjacentHTML( + "afterbegin", + window.Handlebars.templates["notes.hbs"](this.config) ); - const notesContainer = document.createElement("div"); - notesContainer.id = "notes-container" + this.#notesContainer = document.querySelector(".notes-container"); + + let notes = AppNoteRequests.GetAll() + if (notes.length > 0) { + notes.then(resp => { + this.#renderNotes(resp); + }); + } else { + const emptyNoteData = { + id: "empty-note", + data: { + title: "У вас пока нет заметок :(", + content: "" + } + } + let emptyNote = new Note(this.#notesContainer, emptyNoteData); + emptyNote.render() + } - this.self.appendChild(notesContainer) - AppNoteRequests.GetAll().then(resp => { - this.#renderNotes(notesContainer, resp) - }) - } + this.#notesEditor = new NoteEditor(this.self.querySelector(".wrapper"), this.config.noteEditor); + this.#notesEditor.render(); + + document.title = "Заметки"; + } } \ No newline at end of file diff --git a/public/src/pages/page.js b/public/src/pages/page.js new file mode 100644 index 00000000..3ebf2e7e --- /dev/null +++ b/public/src/pages/page.js @@ -0,0 +1,37 @@ +export default class Page { + #parent; + #config; + + constructor(parent, config) { + this.#parent = parent; + this.#config = config; + } + + get href () { + return this.#config.href; + } + + get self () { + return document.getElementById(this.#config.id); + } + + get config() { + return this.#config; + } + + get parent() { + return this.#parent; + } + + get needAuth() { + return this.#config.needAuth; + } + + remove(){ + this.self.remove(); + } + + render() { + + } +} diff --git a/public/src/pages/register/register.css b/public/src/pages/register/register.css index a5c6e586..8c2657c3 100644 --- a/public/src/pages/register/register.css +++ b/public/src/pages/register/register.css @@ -8,7 +8,7 @@ } .register-form { - min-width: 300px; + width: 300px; padding: 20px; border-radius: 20px; display: flex; @@ -16,10 +16,10 @@ align-items: center; justify-content: center; background: #ffffff; - gap: 20px; + gap: 15px; box-shadow: 0 4px 30px -10px rgba(0, 0, 0, 0.2); } -.register-window > div { - margin: 10px; +.register-form .login-title { + margin-bottom: 10px; } \ No newline at end of file diff --git a/public/src/pages/register/register.hbs b/public/src/pages/register/register.hbs index 304f6d20..19460106 100644 --- a/public/src/pages/register/register.hbs +++ b/public/src/pages/register/register.hbs @@ -1,5 +1,5 @@
-

Форма регистрации

+

Регистрация

diff --git a/public/src/pages/register/register.js b/public/src/pages/register/register.js index 26d4de93..e6780d7b 100644 --- a/public/src/pages/register/register.js +++ b/public/src/pages/register/register.js @@ -1,4 +1,4 @@ -import "../../../build/register.js" +import "../../../build/register.js"; import {Input} from "../../components/input/input.js"; import {Link} from "../../components/link/link.js"; import {Button} from "../../components/button/button.js"; @@ -7,42 +7,23 @@ import {AppEventMaker} from "../../modules/eventMaker.js"; import {inputEvents} from "../../components/input/events.js"; import {AppDispatcher} from "../../modules/dispathcer.js"; import {UserActions} from "../../stores/user/userStore.js"; -import {router} from "../../modules/router.js"; - -export default class RegisterPage { - #parent; - #config; - - needAuth; +import Page from "../page.js"; +import {UserStoreEvents} from "../../stores/user/events.js"; +export default class RegisterPage extends Page { #loginInput; #passwordInput; #repeatPasswordInput; #link; #submitBtn; - - constructor(parent, config) { - this.#parent = parent; - this.#config = config; - - this.needAuth = false; - } - - get href () { - return this.#config.href; - } - + get form () { - return document.getElementById(this.#config.form.id) - } - - get self () { - return document.getElementById(this.#config.id) + return document.getElementById(this.config.form.id); } validateData = () => { - const validateLogin = this.#validateLogin() - const validatePassword = this.#validatePassword() + const validateLogin = this.#validateLogin(); + const validatePassword = this.#validatePassword(); if (validateLogin && validatePassword) { AppDispatcher.dispatch({ @@ -51,9 +32,9 @@ export default class RegisterPage { login: this.#loginInput.value, password: this.#passwordInput.value } - }) + }); } - } + }; #validateLogin(){ delete this.#loginInput.self.dataset.error; @@ -66,12 +47,15 @@ export default class RegisterPage { this.#loginInput.throwError(validationResult.message); } + if (validationResult.result) { + this.#loginInput.cleanError(); + this.#loginInput.self.classList.add("success"); + } + return validationResult.result; } #validatePassword(){ - delete this.#passwordInput.self.dataset.error; - const value = this.#passwordInput.value; const validationResult = ValidatePassword(value); @@ -79,11 +63,21 @@ export default class RegisterPage { if (!validationResult.result){ this.#passwordInput.throwError(validationResult.message); this.#repeatPasswordInput.throwError(validationResult.message); + return false; } if (this.#passwordInput.value !== this.#repeatPasswordInput.value) { this.#passwordInput.throwError("Пароли не совпадают"); this.#repeatPasswordInput.throwError("Пароли не совпадают"); + return false; + } + + if (validationResult.result) { + this.#passwordInput.cleanError(); + this.#passwordInput.self.classList.add("success"); + + this.#repeatPasswordInput.cleanError(); + this.#repeatPasswordInput.self.classList.add("success"); } return validationResult.result; @@ -97,59 +91,79 @@ export default class RegisterPage { } else if (id === this.#repeatPasswordInput.id){ this.#validateRepeatPassword(); } - } + }; #validateRepeatPassword(){ - delete this.#repeatPasswordInput.self.dataset.error; - const value = this.#repeatPasswordInput.value; const validationResult = ValidatePassword(value); if (!validationResult.result){ + this.#passwordInput.throwError(validationResult.message); this.#repeatPasswordInput.throwError(validationResult.message); + return false; + } + + if (this.#passwordInput.value !== this.#repeatPasswordInput.value) { + this.#passwordInput.throwError("Пароли не совпадают"); + this.#repeatPasswordInput.throwError("Пароли не совпадают"); + return false; + } + + if (validationResult.result) { + this.#passwordInput.cleanError(); + this.#passwordInput.self.classList.add("success"); + + this.#repeatPasswordInput.cleanError(); + this.#repeatPasswordInput.self.classList.add("success"); } return validationResult.result; } + #throwLoginAlreadyExistError = () => { + this.#loginInput.throwError("Пользователь с таким логином уже существует!"); + }; + #subscribeToEvents(){ AppEventMaker.subscribe(inputEvents.INPUT_CHANGE, this.#inputEventHandler); + AppEventMaker.subscribe(UserStoreEvents.LOGIN_ALREADY_EXIST, this.#throwLoginAlreadyExistError); } #unsubscribeToEvents(){ AppEventMaker.unsubscribe(inputEvents.INPUT_CHANGE, this.#inputEventHandler); + AppEventMaker.unsubscribe(UserStoreEvents.LOGIN_ALREADY_EXIST, this.#throwLoginAlreadyExistError); } remove(){ this.#unsubscribeToEvents(); - this.self.remove() + this.self.remove(); } render() { - this.#parent.insertAdjacentHTML( - 'beforeend', - window.Handlebars.templates['register.hbs'](this.#config.form) + this.parent.insertAdjacentHTML( + "beforeend", + window.Handlebars.templates["register.hbs"](this.config.form) ); - const self = document.getElementById("register-page") // Не понятно зачем это, анализатор ругается - - this.#loginInput = new Input(this.form, this.#config.form.inputs.login); + this.#loginInput = new Input(this.form, this.config.form.inputs.login); this.#loginInput.render(); - this.#passwordInput = new Input(this.form, this.#config.form.inputs.password); + this.#passwordInput = new Input(this.form, this.config.form.inputs.password); this.#passwordInput.render(); - this.#repeatPasswordInput = new Input(this.form, this.#config.form.inputs.repeatPassword); + this.#repeatPasswordInput = new Input(this.form, this.config.form.inputs.repeatPassword); this.#repeatPasswordInput.render(); - this.#link = new Link(this.form, this.#config.form.links.loginPage); + this.#link = new Link(this.form, this.config.form.links.loginPage); this.#link.render(); - this.#submitBtn = new Button(this.form, this.#config.form.buttons.submitBtn, this.validateData); + this.#submitBtn = new Button(this.form, this.config.form.buttons.submitBtn, this.validateData); this.#submitBtn.render(); this.#subscribeToEvents(); + + document.title = "Регистрация"; } } diff --git a/public/src/shared/uuid.js b/public/src/shared/uuid.js index f237adc9..5badf155 100644 --- a/public/src/shared/uuid.js +++ b/public/src/shared/uuid.js @@ -1,10 +1,10 @@ export const create_UUID = () => { - return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { + return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function(c) { const r = Math.random() * 16 | 0, - v = c === 'x' ? r : (r & 0x3 | 0x8); + v = c === "x" ? r : (r & 0x3 | 0x8); return v.toString(16); }); -} +}; // return "10000000-1000-4000-8000-100000000000".replace(/[018]/g, c => // (c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16) diff --git a/public/src/shared/validation.js b/public/src/shared/validation.js index 91ebe2b7..2b7f5974 100644 --- a/public/src/shared/validation.js +++ b/public/src/shared/validation.js @@ -8,47 +8,53 @@ export const ValidatePassword = (value) => { } } + let regExp = /[a-zA-Z]/g; + if (!regExp.test(value)) + { + return ValidationResult(false, "Пароль должен содержать хотя бы одну букву!"); + } + if (value === "") { - return ValidationResult(false, "Пароль не может быть пустым!") + return ValidationResult(false, "Пароль не может быть пустым!"); } if (value.length < 8){ - return ValidationResult(false, "Пароль должен быть не менее 8 символов!") + return ValidationResult(false, "Пароль должен быть не менее 8 символов!"); } if (value.length > 20){ - return ValidationResult(false, "Пароль должен быть короче 20 символов!") + return ValidationResult(false, "Пароль должен быть короче 20 символов!"); } - return ValidationResult(true) -} + return ValidationResult(true); +}; export const ValidateLogin = (value) => { for (let index = 0; index < value.length; ++index){ if (!(value.charCodeAt(index) >= 97 && value.charCodeAt(index) <= 122 || value.charCodeAt(index) >= 64 && value.charCodeAt(index) <= 90 || value.charCodeAt(index) >= 48 && value.charCodeAt(index) <= 57 )){ - return ValidationResult(false, "Логин должен содержать только латинские символы или цифры") + return ValidationResult(false, "Логин должен содержать только латинские символы или цифры"); } } if (value === "") { - return ValidationResult(false, "Логин не может быть пустым!") + return ValidationResult(false, "Логин не может быть пустым!"); } if (value.length < 4){ - return ValidationResult(false, "Логин должен быть не менее 4 символов!") + return ValidationResult(false, "Логин должен быть не менее 4 символов!"); } if (value.length > 12){ - return ValidationResult(false, "Логин должен быть короче 12 символов!") + return ValidationResult(false, "Логин должен быть короче 12 символов!"); } - return ValidationResult(true) -} + return ValidationResult(true); +}; const ValidationResult = (result, message = null) => { - return {result, message} -} \ No newline at end of file + return {result, message}; +}; \ No newline at end of file diff --git a/public/src/stores/user/events.js b/public/src/stores/user/events.js index 60741433..2de63f1e 100644 --- a/public/src/stores/user/events.js +++ b/public/src/stores/user/events.js @@ -1,5 +1,7 @@ export const UserStoreEvents = { - SUCCESSFUL_LOGIN: 'SUCCESSFUL_LOGIN', + SUCCESSFUL_LOGIN: "SUCCESSFUL_LOGIN", LOGOUT: "LOGOUT", - CHANGE_PAGE: "CHANGE_PAGE" -} \ No newline at end of file + CHANGE_PAGE: "CHANGE_PAGE", + LOGIN_ALREADY_EXIST: "LOGIN_ALREADY_EXIST", + INVALID_LOGIN_OR_PASSWORD: "INVALID_LOGIN_OR_PASSWORD" +}; \ No newline at end of file diff --git a/public/src/stores/user/userStore.js b/public/src/stores/user/userStore.js index 6dc56b56..9254d1f7 100644 --- a/public/src/stores/user/userStore.js +++ b/public/src/stores/user/userStore.js @@ -6,10 +6,10 @@ import {AppAuthRequests} from "../../modules/ajax.js"; class UserStore { #state = { - username: 'YarikMix', - avatarUrl: '/src/assets/avatar.png', + username: "YarikMix", + avatarUrl: "/src/assets/avatar.png", isAuth: false - } + }; registerEvents(){ AppDispatcher.register(async (action) => { @@ -24,11 +24,11 @@ class UserStore { await this.register(action.payload); break; case UserActions.CHECK_USER: - console.log("action handled") + console.log("action handled"); await this.checkUser(); break; } - }) + }); } get username() { @@ -45,11 +45,11 @@ class UserStore { async login(credentials){ try { - const res = await AppAuthRequests.Login(credentials.login, credentials.password) + const res = await AppAuthRequests.Login(credentials.login, credentials.password); console.log("login successfull"); this.#state.isAuth = true; this.#state.username = res.username; - router.redirect('/notes'); + router.redirect("/notes"); AppEventMaker.notify(UserStoreEvents.SUCCESSFUL_LOGIN); } catch (err) { console.log(err); @@ -62,7 +62,7 @@ class UserStore { console.log("logout successful"); this.#state.isAuth = false; this.#state.username = ""; - router.redirect('/'); + router.redirect("/"); AppEventMaker.notify(UserStoreEvents.LOGOUT); } catch (err) { console.log(err); @@ -71,11 +71,11 @@ class UserStore { async register(credentials) { try { - const res = await AppAuthRequests.SignUp(credentials.login, credentials.password) + const res = await AppAuthRequests.SignUp(credentials.login, credentials.password); console.log("signup successfull"); this.#state.isAuth = true; this.#state.username = res.username; - router.redirect('/notes'); + router.redirect("/notes"); AppEventMaker.notify(UserStoreEvents.SUCCESSFUL_LOGIN); } catch (err) { console.log(err); @@ -84,15 +84,15 @@ class UserStore { async checkUser(){ try { - console.log("зареган") + console.log("зареган"); const res = await AppAuthRequests.CheckUser(); this.#state.isAuth = true; this.#state.username = res.username; - router.redirect("/notes") + // router.redirect("/notes") AppEventMaker.notify(UserStoreEvents.SUCCESSFUL_LOGIN); } catch (err) { - console.log("не зареган") - router.redirect("/") + console.log("не зареган"); + // router.redirect("/"); } } } @@ -105,4 +105,4 @@ export const UserActions = { LOGOUT: "LOGOUT", CHANGE_PAGE: "CHANGE_PAGE", CHECK_USER: "CHECK_USER" -} \ No newline at end of file +}; \ No newline at end of file