diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml new file mode 100644 index 0000000..bd4cbdb --- /dev/null +++ b/.github/workflows/main.yml @@ -0,0 +1,39 @@ +name: Build +on: + push: + branches: + - master + +jobs: + build: + strategy: + matrix: + os: [ubuntu-latest, windows-latest, macos-latest] + runs-on: ${{ matrix.os }} + + steps: + - name: Checkout Repository + uses: actions/checkout@v2 + + - name: Set Up Node.js + uses: actions/setup-node@v2 + with: + node-version: '16' + + - name: Install Dependencies of frontend + run: npm install + + - name: Install Typescript for frontend + run: npm install typescript --save-dev + + - name: Run Build for frontend + run: npm run build + + - name: Install Dependencies of backend + run: cd server && npm install + + - name: Install Typescript for backend + run: cd server && npm install typescript --save-dev + + - name: Run Start for backend + run: cd server && npm run start \ No newline at end of file diff --git a/.gitignore b/.gitignore index f4f0448..2e66220 100644 --- a/.gitignore +++ b/.gitignore @@ -17,4 +17,7 @@ dist/ # Avatar Folder server/Avatars/* -!server/Avatars/1.png \ No newline at end of file +!server/Avatars/1.png + +# Test Folder +test/* \ No newline at end of file diff --git a/server/src/SQL/dbInsert.ts b/server/src/SQL/dbInsert.ts index c467e6e..d9d0c72 100644 --- a/server/src/SQL/dbInsert.ts +++ b/server/src/SQL/dbInsert.ts @@ -83,5 +83,32 @@ export class DBInsert extends DatabaseConnector [] ); } + + async addColorScheme(textColor: string, backgroundColor: string): Promise{ + return this.executeQuery( + `INSERT INTO tb_colorscheme (cs_textColor, cs_backgroundColor) + VALUES (?, ?); + `, + [textColor, backgroundColor] + ); + } + + async addBigArea(name: string): Promise{ + return this.executeQuery( + `INSERT INTO tb_bigarea (ba_name) + VALUES (?); + `, + [name] + ); + } + + async addSubArea(name: string, bigAreaID: number, colorSchemeID: number): Promise{ + return this.executeQuery( + `INSERT INTO tb_subarea (aa_area, bigarea, aa_colorscheme) + VALUES (?, ?, ?); + `, + [name, bigAreaID, colorSchemeID] + ); + } } diff --git a/server/src/SQL/dbSelect.ts b/server/src/SQL/dbSelect.ts index 8ca7762..847a225 100644 --- a/server/src/SQL/dbSelect.ts +++ b/server/src/SQL/dbSelect.ts @@ -166,24 +166,103 @@ export class DBSelect extends DatabaseConnector ); } - async selectUserIDStatusByToken(token: string): Promise<{ u_id: number; u_active:number}[]> + async selectUserIDStatusByToken(token: string): Promise<{ u_id: number; u_active: number; }[]> { - return await this.executeQuery<{ u_id: number; u_active:number}>( + return await this.executeQuery<{ u_id: number; u_active: number; }>( `SELECT u_id, u_active FROM tb_user WHERE u_token = ?`, [token] ); } - async selectAllArticleArea(){ - return await this.executeQuery<{ba_name:string; aa_area:string;}>( - `SELECT tb_bigarea.ba_name, tb_subarea.aa_area + async selectAllArticleArea() + { + return await this.executeQuery<{ ba_id: number, ba_name: string, aa_id: number, aa_area: string, cs_id: number, cs_textColor: string, cs_backgroundColor: string; }>( + `SELECT tb_bigarea.ba_id, tb_bigarea.ba_name, tb_subarea.aa_id, tb_subarea.aa_area, tb_subarea.aa_colorscheme,tb_colorscheme.cs_id, tb_colorscheme.cs_textColor, tb_colorscheme.cs_backgroundColor FROM tb_bigarea LEFT JOIN tb_subarea ON tb_bigarea.ba_id = tb_subarea.bigarea AND tb_subarea.aa_alive = 1 + LEFT JOIN tb_colorscheme + ON tb_subarea.aa_colorscheme = tb_colorscheme.cs_id WHERE tb_bigarea.ba_alive = 1 - ORDER BY tb_bigarea.ba_id, tb_subarea.aa_id; + ORDER BY tb_bigarea.ba_id, tb_subarea.aa_id; + + ` ); } + async selectAllColorScheme() + { + return await this.executeQuery<{ cs_id: number; cs_textColor: string; cs_backgroundColor: string; }>( + `SELECT cs_id, cs_textColor, cs_backgroundColor FROM tb_colorscheme WHERE cs_alive = 1` + ); + } + + async checkColorSchemeConstraint(id: number) + { + return await this.executeQuery<{ aa_colorscheme: number; }>( + `SELECT tb_subarea.*, tb_colorscheme.* + FROM tb_subarea + LEFT JOIN tb_colorscheme ON tb_subarea.aa_colorscheme = tb_colorscheme.cs_id + WHERE tb_colorscheme.cs_id = ? AND tb_subarea.aa_alive = 1; + `, + [id] + ); + } + + async selectAllArticleBigArea() + { + return await this.executeQuery<{ ba_id: number; ba_name: string; }>( + `SELECT * FROM tb_bigarea WHERE ba_alive = 1` + ); + } + + async checkBigAreaConstraint(id: number) + { + return await this.executeQuery<{ aa_area: number; }>( + `SELECT tb_bigarea.ba_id + FROM tb_bigarea + JOIN tb_subarea ON tb_bigarea.ba_id = tb_subarea.bigarea + WHERE tb_subarea.aa_alive = 1 AND tb_bigarea.ba_id = ?; + `, + [id] + ); + } + + async selectBigAreaByID(id: number) + { + return await this.executeQuery<{ ba_id: number; ba_name: string; }>( + `SELECT * FROM tb_bigarea WHERE ba_id = ?`, + [id] + ); + } + + async selectSubAreaByID(id: number) + { + return await this.executeQuery<{ aa_id: number; aa_area: string; aa_colorscheme: number; }>( + `SELECT * FROM tb_subarea WHERE aa_id = ?`, + [id] + ); + } + + async selectColorSchemeByID(id: number) + { + return await this.executeQuery<{ cs_id: number; cs_textColor: string; cs_backgroundColor: string; }>( + `SELECT * FROM tb_colorscheme WHERE cs_id = ?`, + [id] + ); + } + + async checkSubAreaConstraint(id: number) + { + return await this.executeQuery<{ aa_area: number; }>( + `SELECT tb_subarea.aa_id + FROM tb_subarea + INNER JOIN tb_article + ON tb_subarea.aa_id = tb_article.article_area + WHERE tb_subarea.aa_id = ? AND tb_article.article_alive = 1; + `, + [id] + ); + } } diff --git a/server/src/SQL/dbUpdate.ts b/server/src/SQL/dbUpdate.ts index 7fd31af..6fefbcd 100644 --- a/server/src/SQL/dbUpdate.ts +++ b/server/src/SQL/dbUpdate.ts @@ -130,4 +130,57 @@ export class DBUpdate extends DatabaseConnector [id] ); } + + async deleteColorScheme(id:number): Promise + { + return this.executeQuery( + `UPDATE tb_colorscheme + SET cs_alive = 0 + WHERE cs_id = ?; + `, + [id] + ); + } + + async deleteBigArea(id:number): Promise + { + return this.executeQuery( + `UPDATE tb_bigarea + SET ba_alive = 0 + WHERE ba_id = ?; + `, + [id] + ); + } + async updateBigArea(id:number, name:string): Promise + { + return this.executeQuery( + `UPDATE tb_bigarea + SET ba_name = ? + WHERE ba_id = ?; + `, + [name, id] + ); + } + + async updateSubAreaData(id:number, name:string, bigAreaID:number, colorSchemeID:number): Promise + { + return this.executeQuery( + `UPDATE tb_subarea + SET aa_area = ?, bigarea = ?, aa_colorscheme = ? + WHERE aa_id = ?; + `, + [name, bigAreaID, colorSchemeID, id] + ); + } + + async deleteSubArea(id:number): Promise{ + return this.executeQuery( + `UPDATE tb_subarea + SET aa_alive = 0 + WHERE aa_id = ?; + `, + [id] + ); + } } diff --git a/server/src/Validators/inputControl.ts b/server/src/Validators/inputControl.ts index 72ca7ea..28ccabe 100644 --- a/server/src/Validators/inputControl.ts +++ b/server/src/Validators/inputControl.ts @@ -54,9 +54,9 @@ export class InputControl } validateUserDesc(userDesc: string): void { - if (userDesc.length > 100) + if (userDesc.length < 1 || userDesc.length > 100) { - throw new ValidationError(-400, 'Please input a correct user description, must be less than 100 characters.'); + throw new ValidationError(-400, 'Please input a correct user description, must be less than 100 characters or more than 1 character and contain only alphanumeric characters and spaces.'); } } @@ -72,9 +72,9 @@ export class InputControl { const domainRegex = /^(?:(?:[a-zA-Z0-9][a-zA-Z0-9\-]{0,61})?[a-zA-Z0-9]\.)+[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9]$/; - if (domainname.length > 254) + if (domainname.length < 1 || domainname.length > 254) { - throw new ValidationError(-400, 'Domain name is too long'); + throw new ValidationError(-400, 'Domain name is too long or too short'); } else if (!domainRegex.test(domainname)) { throw new ValidationError(-400, 'Domain name is invalid'); @@ -83,9 +83,9 @@ export class InputControl validateSMTPPassword(password: string): void { - if (password.length > 200) + if (password.length < 1 || password.length > 200) { - throw new ValidationError(-400, 'SMTP password is too long'); + throw new ValidationError(-400, 'SMTP password is too long or too short'); } } @@ -116,4 +116,22 @@ export class InputControl } } + validateBigAreaName(name: string): void + { + if (name.length < 1 || name.length > 20 || !/^[a-zA-Z0-9 ]*$/.test(name)) + { + throw new ValidationError(-400, 'Please input a correct big area name, must be less than 20 characters and contain only alphanumeric characters and spaces.'); + } + } + + validateSubAreaName(name: string): void + { + if (name.length < 1 || name.length > 20 || !/^[a-zA-Z0-9 ]*$/.test(name)) + { + throw new ValidationError(-400, 'Please input a correct sub area name, must be less than 20 characters and contain only alphanumeric characters and spaces.'); + } + } + + + } diff --git a/server/src/index.ts b/server/src/index.ts index 4c311dd..7147b07 100644 --- a/server/src/index.ts +++ b/server/src/index.ts @@ -21,6 +21,16 @@ import updateEmailSettings from './routes/UpdateEmailSettings'; import sendTestEmail from './routes/SendTestEmail'; import activateAccountRequest from './routes/ActivateAccountRequest'; import getAllArticleArea from './routes/GetAllArticleArea'; +import getAllColorScheme from './routes/GetAllColorScheme'; +import deleteColorScheme from './routes/DeleteColorScheme'; +import addColorScheme from './routes/AddColorScheme'; +import getAllArticleBigArea from './routes/GetAllArticleBigArea'; +import deleteBigArea from './routes/DeleteBigArea'; +import updateBigArea from './routes/UpdateBigArea'; +import addBigArea from './routes/AddBigArea'; +import updateSubArea from './routes/UpdateSubArea'; +import deleteSubArea from './routes/DeleteSubArea'; +import addSubArea from './routes/AddSubArea'; import { Init } from './Init'; const app = express(); @@ -28,7 +38,7 @@ const port = 3000; app.use(express.json()); -app.use('/avatars', express.static('Avatars')) +app.use('/avatars', express.static('Avatars')); app.use(cors()); @@ -52,7 +62,17 @@ const routers = [ updateEmailSettings, sendTestEmail, activateAccountRequest, - getAllArticleArea + getAllArticleArea, + getAllColorScheme, + deleteColorScheme, + addColorScheme, + getAllArticleBigArea, + deleteBigArea, + updateBigArea, + addBigArea, + updateSubArea, + deleteSubArea, + addSubArea ]; app.use('/', routers); diff --git a/server/src/routes/AddBigArea/addBigArea.ts b/server/src/routes/AddBigArea/addBigArea.ts new file mode 100644 index 0000000..1c15949 --- /dev/null +++ b/server/src/routes/AddBigArea/addBigArea.ts @@ -0,0 +1,77 @@ +import { AES_256_GCM } from "../../Crypto/AES-256-GCM"; +import { AES_256_GCMEncrypted } from "../../Crypto/interface"; +import { ReturnData } from "../../Return To Client"; +import { LoginData as LD, UserData as UD } from "../../Return To Client/interface"; +import { DBInsert } from "../../SQL/dbInsert"; +import { InputControl, ValidationError } from "../../Validators/inputControl"; + +export class AddBigArea +{ + + returnData = new ReturnData(); + async performAction(data: { UserData: LD, bigArea: string; }) + { + try + { + const decUserData: UD = this.decryptUserData(data.UserData.encUserData, data.UserData.userData.id.toString()); + const userData: UD = data.UserData.userData; + + if (JSON.stringify(decUserData) === JSON.stringify(userData) && userData.class === 3) + { + const dbInsert = new DBInsert(); + this.validation(data.bigArea); + const result = await dbInsert.addBigArea(data.bigArea); + if (result.affectedRows === 1) + { + const returnData = this.returnData.returnClientData(0, 'Success', { id: result.insertId }); + return returnData; + } else + { + const returnData = this.returnData.returnClientData(-400, 'Failed'); + return returnData; + } + + } else + { + const returnData = this.returnData.returnClientData(-101, 'User data not match'); + return returnData; + } + + + } catch (error) + { + if (error instanceof ValidationError) + { + // handle ValidationError + const returnData = this.returnData.returnClientData(error.code, error.message); + return returnData; + } + // invalid iv + else if (error instanceof Error && error.message.includes('Unsupported state or unable to authenticate data')) + { + const returnData = this.returnData.returnClientData(-102, 'Invalid IV or Auth Tag'); + return returnData; + } else + { + const returnData = this.returnData.returnClientData(-400, 'Error'); + console.log(error); + return returnData; + } + } + } + + + decryptUserData(encUserData: AES_256_GCMEncrypted, key: string) + { + const aes_256_GCM = new AES_256_GCM(); + const decUserData: UD = JSON.parse(aes_256_GCM.decrypt(encUserData.encryptedData, encUserData.iv, encUserData.tag, key)); + return decUserData; + } + + validation(name: string): void + { + const inputControl = new InputControl(); + inputControl.validateBigAreaName(name); + } + +} \ No newline at end of file diff --git a/server/src/routes/AddBigArea/index.ts b/server/src/routes/AddBigArea/index.ts new file mode 100644 index 0000000..d0b5835 --- /dev/null +++ b/server/src/routes/AddBigArea/index.ts @@ -0,0 +1,44 @@ +// src/routes/Register/index.ts +import express, { Request, Response } from 'express'; +import { LoginData as LD } from '../../Return To Client/interface'; +import { AddBigArea } from './addBigArea'; +const router = express.Router(); + +function transformUserData(body: any): LD +{ + return { + ...body.UserData, + encUserData: { + ...body.UserData.encUserData, + iv: Buffer.from(body.UserData.encUserData.iv.data), + encryptedData: Buffer.from(body.UserData.encUserData.encryptedData.data), + tag: Buffer.from(body.UserData.encUserData.tag.data) + } + } as LD; +} + +router.post('/addBigArea', async (req: Request, res: Response) => +{ + try + { + const body = req.body as { UserData: LD, name: string }; + if (body.UserData === undefined || body.name === undefined) + { + res.json({ code: -101, message: 'Data is not complete' }); + return; + } + + const addBigArea = new AddBigArea(); + const transformedUserData = transformUserData(body); + const data = { UserData: transformedUserData, bigArea: body.name}; + const returndata = await addBigArea.performAction(data); + res.json(returndata); + } catch (error) + { + res.json({ code: -101, message: 'Error' }); + return; + } + +}); + +export default router; \ No newline at end of file diff --git a/server/src/routes/AddColorScheme/addColorScheme.ts b/server/src/routes/AddColorScheme/addColorScheme.ts new file mode 100644 index 0000000..036eb09 --- /dev/null +++ b/server/src/routes/AddColorScheme/addColorScheme.ts @@ -0,0 +1,59 @@ +import { AES_256_GCM } from "../../Crypto/AES-256-GCM"; +import { AES_256_GCMEncrypted } from "../../Crypto/interface"; +import { ReturnData } from "../../Return To Client"; +import { LoginData as LD, UserData as UD } from "../../Return To Client/interface"; +import { DBInsert } from "../../SQL/dbInsert"; + +export class AddColorScheme{ + + returnData = new ReturnData(); + async performAction(data :{UserData: LD, textColor: string, backgroundColor: string}){ + try + { + const decUserData: UD = this.decryptUserData(data.UserData.encUserData, data.UserData.userData.id.toString()); + const userData: UD = data.UserData.userData; + + if (JSON.stringify(decUserData) === JSON.stringify(userData) && userData.class === 3) + { + const dbInsert = new DBInsert(); + const result = await dbInsert.addColorScheme(data.textColor, data.backgroundColor); + if(result.affectedRows === 1){ + const returnData = this.returnData.returnClientData(0, 'Success', {id: result.insertId}); + return returnData; + }else{ + const returnData = this.returnData.returnClientData(-101, 'Not Success'); + return returnData; + } + + } else + { + const returnData = this.returnData.returnClientData(-101, 'User data not match'); + return returnData; + } + + + } catch (error) + { + // invalid iv + if (error instanceof Error && error.message.includes('Unsupported state or unable to authenticate data')) + { + const returnData = this.returnData.returnClientData(-102, 'Invalid IV or Auth Tag'); + return returnData; + } else + { + const returnData = this.returnData.returnClientData(-400, 'Error'); + console.log(error); + return returnData; + } + } + } + + + decryptUserData(encUserData: AES_256_GCMEncrypted, key: string) + { + const aes_256_GCM = new AES_256_GCM(); + const decUserData: UD = JSON.parse(aes_256_GCM.decrypt(encUserData.encryptedData, encUserData.iv, encUserData.tag, key)); + return decUserData; + } + +} \ No newline at end of file diff --git a/server/src/routes/AddColorScheme/index.ts b/server/src/routes/AddColorScheme/index.ts new file mode 100644 index 0000000..2921ec9 --- /dev/null +++ b/server/src/routes/AddColorScheme/index.ts @@ -0,0 +1,44 @@ +// src/routes/Register/index.ts +import express, { Request, Response } from 'express'; +import { LoginData as LD } from '../../Return To Client/interface'; +import { AddColorScheme } from './addColorScheme'; +const router = express.Router(); + +function transformUserData(body: any): LD +{ + return { + ...body.UserData, + encUserData: { + ...body.UserData.encUserData, + iv: Buffer.from(body.UserData.encUserData.iv.data), + encryptedData: Buffer.from(body.UserData.encUserData.encryptedData.data), + tag: Buffer.from(body.UserData.encUserData.tag.data) + } + } as LD; +} + +router.post('/addColorScheme', async (req: Request, res: Response) => +{ + try + { + const body = req.body as { UserData: LD, textColor: string, backgroundColor: string }; + if (body.UserData === undefined || body.textColor === undefined || body.backgroundColor === undefined) + { + res.json({ code: -101, message: 'Data is not complete' }); + return; + } + + const addColorScheme = new AddColorScheme(); + const transformedUserData = transformUserData(body); + const data = { UserData: transformedUserData, textColor: body.textColor, backgroundColor: body.backgroundColor }; + const returndata = await addColorScheme.performAction(data); + res.json(returndata); + } catch (error) + { + res.json({ code: -101, message: 'Error' }); + return; + } + +}); + +export default router; \ No newline at end of file diff --git a/server/src/routes/AddSubArea/addSubArea.ts b/server/src/routes/AddSubArea/addSubArea.ts new file mode 100644 index 0000000..23c29de --- /dev/null +++ b/server/src/routes/AddSubArea/addSubArea.ts @@ -0,0 +1,77 @@ +import { AES_256_GCM } from "../../Crypto/AES-256-GCM"; +import { AES_256_GCMEncrypted } from "../../Crypto/interface"; +import { ReturnData } from "../../Return To Client"; +import { LoginData as LD, UserData as UD } from "../../Return To Client/interface"; +import { DBInsert } from "../../SQL/dbInsert"; +import { InputControl, ValidationError } from "../../Validators/inputControl"; + +export class AddSubArea{ + + returnData = new ReturnData(); + async performAction(data :{UserData: LD, name: string, bigAreaID: number, colorSchemeID: number}){ + try + { + const decUserData: UD = this.decryptUserData(data.UserData.encUserData, data.UserData.userData.id.toString()); + const userData: UD = data.UserData.userData; + + if (JSON.stringify(decUserData) === JSON.stringify(userData) && userData.class === 3) + { + this.validation(data.name); + const dbInsert = new DBInsert(); + + const result = await dbInsert.addSubArea(data.name, data.bigAreaID, data.colorSchemeID); + + if (result.affectedRows === 1) + { + const returnData = this.returnData.returnClientData(0, 'Success', {id: result.insertId}); + return returnData; + } else + { + const returnData = this.returnData.returnClientData(-101, 'Failed'); + return returnData; + } + + } else + { + const returnData = this.returnData.returnClientData(-101, 'User data not match'); + return returnData; + } + + + } catch (error) + { + if (error instanceof ValidationError) + { + // handle ValidationError + const returnData = this.returnData.returnClientData(error.code, error.message); + return returnData; + } + // invalid iv + else if (error instanceof Error && error.message.includes('Unsupported state or unable to authenticate data')) + { + const returnData = this.returnData.returnClientData(-102, 'Invalid IV or Auth Tag'); + return returnData; + } else + { + const returnData = this.returnData.returnClientData(-400, 'Error'); + console.log(error); + return returnData; + } + } + } + + + decryptUserData(encUserData: AES_256_GCMEncrypted, key: string) + { + const aes_256_GCM = new AES_256_GCM(); + const decUserData: UD = JSON.parse(aes_256_GCM.decrypt(encUserData.encryptedData, encUserData.iv, encUserData.tag, key)); + return decUserData; + } + + validation(name: string): void + { + const inputControl = new InputControl(); + inputControl.validateSubAreaName(name); + } + +} \ No newline at end of file diff --git a/server/src/routes/AddSubArea/index.ts b/server/src/routes/AddSubArea/index.ts new file mode 100644 index 0000000..6b4013e --- /dev/null +++ b/server/src/routes/AddSubArea/index.ts @@ -0,0 +1,44 @@ +// src/routes/Register/index.ts +import express, { Request, Response } from 'express'; +import { LoginData as LD } from '../../Return To Client/interface'; +import { AddSubArea } from './addSubArea'; +const router = express.Router(); + +function transformUserData(body: any): LD +{ + return { + ...body.UserData, + encUserData: { + ...body.UserData.encUserData, + iv: Buffer.from(body.UserData.encUserData.iv.data), + encryptedData: Buffer.from(body.UserData.encUserData.encryptedData.data), + tag: Buffer.from(body.UserData.encUserData.tag.data) + } + } as LD; +} + +router.post('/addSubArea', async (req: Request, res: Response) => +{ + try + { + const body = req.body as { UserData: LD, name: string, bigAreaID: number, colorSchemeID: number }; + if (body.UserData === undefined || body.name === undefined || body.bigAreaID === undefined || body.colorSchemeID === undefined) + { + res.json({ code: -101, message: 'Data is not complete' }); + return; + } + + const addSubArea = new AddSubArea(); + const transformedUserData = transformUserData(body); + const data = { UserData: transformedUserData, name: body.name, bigAreaID: body.bigAreaID, colorSchemeID: body.colorSchemeID}; + const returndata = await addSubArea.performAction(data); + res.json(returndata); + } catch (error) + { + res.json({ code: -101, message: 'Error' }); + return; + } + +}); + +export default router; \ No newline at end of file diff --git a/server/src/routes/DeleteBigArea/deleteBigArea.ts b/server/src/routes/DeleteBigArea/deleteBigArea.ts new file mode 100644 index 0000000..8a1385d --- /dev/null +++ b/server/src/routes/DeleteBigArea/deleteBigArea.ts @@ -0,0 +1,77 @@ +import { AES_256_GCM } from "../../Crypto/AES-256-GCM"; +import { AES_256_GCMEncrypted } from "../../Crypto/interface"; +import { ReturnData } from "../../Return To Client"; +import { LoginData as LD, UserData as UD } from "../../Return To Client/interface"; +import { DBSelect } from "../../SQL/dbSelect"; +import { DBUpdate } from "../../SQL/dbUpdate"; + +export class DeleteBigArea{ + returnData = new ReturnData(); + async performAction(data : {UserData: LD, id: number;}){ + try + { + const decUserData: UD = this.decryptUserData(data.UserData.encUserData, data.UserData.userData.id.toString()); + const userData: UD = data.UserData.userData; + + if (JSON.stringify(decUserData) === JSON.stringify(userData) && userData.class === 3) + { + const dbSelect = new DBSelect(); + + const bigArea = await dbSelect.selectBigAreaByID(data.id); + if(bigArea.length === 0){ + const returnData = this.returnData.returnClientData(-101, 'Big area not found'); + return returnData; + } + const Constraint = await dbSelect.checkBigAreaConstraint(data.id); + + if (Constraint.length) + { + const returnData = this.returnData.returnClientData(-101, 'Cannot delete big area that has sub area'); + return returnData; + } + + const dbUpdate = new DBUpdate(); + + const ResultSetHeader = await dbUpdate.deleteBigArea(data.id); + + if (ResultSetHeader.affectedRows === 1) + { + const returnData = this.returnData.returnClientData(0, 'Success'); + return returnData; + } else + { + const returnData = this.returnData.returnClientData(-101, 'Failed'); + return returnData; + } + + + } else + { + const returnData = this.returnData.returnClientData(-101, 'User data not match'); + return returnData; + } + + + } catch (error) + { + // invalid iv + if (error instanceof Error && error.message.includes('Unsupported state or unable to authenticate data')) + { + const returnData = this.returnData.returnClientData(-102, 'Invalid IV or Auth Tag'); + return returnData; + } else + { + const returnData = this.returnData.returnClientData(-400, 'Error'); + console.log(error); + return returnData; + } + } + } + + decryptUserData(encUserData: AES_256_GCMEncrypted, key: string) + { + const aes_256_GCM = new AES_256_GCM(); + const decUserData: UD = JSON.parse(aes_256_GCM.decrypt(encUserData.encryptedData, encUserData.iv, encUserData.tag, key)); + return decUserData; + } +} \ No newline at end of file diff --git a/server/src/routes/DeleteBigArea/index.ts b/server/src/routes/DeleteBigArea/index.ts new file mode 100644 index 0000000..a539586 --- /dev/null +++ b/server/src/routes/DeleteBigArea/index.ts @@ -0,0 +1,44 @@ +// src/routes/Register/index.ts +import express, { Request, Response } from 'express'; +import { LoginData as LD } from '../../Return To Client/interface'; +import { DeleteBigArea } from './deleteBigArea'; +const router = express.Router(); + +function transformUserData(body: any): LD +{ + return { + ...body.UserData, + encUserData: { + ...body.UserData.encUserData, + iv: Buffer.from(body.UserData.encUserData.iv.data), + encryptedData: Buffer.from(body.UserData.encUserData.encryptedData.data), + tag: Buffer.from(body.UserData.encUserData.tag.data) + } + } as LD; +} + +router.post('/deleteBigArea', async (req: Request, res: Response) => +{ + try + { + const body = req.body as { UserData: LD, id: number; }; + if (body.UserData === undefined || body.id === undefined) + { + res.json({ code: -101, message: 'Data is not complete' }); + return; + } + + const deleteBigArea = new DeleteBigArea(); + const transformedUserData = transformUserData(body); + const data = { UserData: transformedUserData, id: body.id }; + const returndata = await deleteBigArea.performAction(data); + res.json(returndata); + } catch (error) + { + res.json({ code: -101, message: 'Error' }); + return; + } + +}); + +export default router; \ No newline at end of file diff --git a/server/src/routes/DeleteColorScheme/deleteColorScheme.ts b/server/src/routes/DeleteColorScheme/deleteColorScheme.ts new file mode 100644 index 0000000..968e33e --- /dev/null +++ b/server/src/routes/DeleteColorScheme/deleteColorScheme.ts @@ -0,0 +1,70 @@ +import { AES_256_GCM } from "../../Crypto/AES-256-GCM"; +import { AES_256_GCMEncrypted } from "../../Crypto/interface"; +import { ReturnData } from "../../Return To Client"; +import { LoginData as LD, UserData as UD } from "../../Return To Client/interface"; +import { DBSelect } from "../../SQL/dbSelect"; +import { DBUpdate } from "../../SQL/dbUpdate"; + +export class DeleteColorScheme{ + returnData = new ReturnData(); + async performAction(data : {UserData: LD, id: number;}){ + try + { + const decUserData: UD = this.decryptUserData(data.UserData.encUserData, data.UserData.userData.id.toString()); + const userData: UD = data.UserData.userData; + + if (JSON.stringify(decUserData) === JSON.stringify(userData) && userData.class === 3) + { + const dbSelect = new DBSelect(); + + const dbUpdate = new DBUpdate(); + + const ColorScheme = await dbSelect.checkColorSchemeConstraint(data.id); + if(ColorScheme.length > 0){ + const returnData = this.returnData.returnClientData(-101, 'You can not delete this color scheme because it is used in some articles'); + return returnData; + } + + const result = await dbUpdate.deleteColorScheme(data.id); + + if (result.affectedRows === 1) + { + const returnData = this.returnData.returnClientData(0, 'Delete success'); + return returnData; + } else + { + const returnData = this.returnData.returnClientData(-101, 'Delete failed'); + return returnData; + } + + } else + { + const returnData = this.returnData.returnClientData(-101, 'User data not match'); + return returnData; + } + + + } catch (error) + { + // invalid iv + if (error instanceof Error && error.message.includes('Unsupported state or unable to authenticate data')) + { + const returnData = this.returnData.returnClientData(-102, 'Invalid IV or Auth Tag'); + return returnData; + } else + { + const returnData = this.returnData.returnClientData(-400, 'Error'); + console.log(error); + return returnData; + } + } + } + + decryptUserData(encUserData: AES_256_GCMEncrypted, key: string) + { + const aes_256_GCM = new AES_256_GCM(); + const decUserData: UD = JSON.parse(aes_256_GCM.decrypt(encUserData.encryptedData, encUserData.iv, encUserData.tag, key)); + return decUserData; + } + +} \ No newline at end of file diff --git a/server/src/routes/DeleteColorScheme/index.ts b/server/src/routes/DeleteColorScheme/index.ts new file mode 100644 index 0000000..f2a60fe --- /dev/null +++ b/server/src/routes/DeleteColorScheme/index.ts @@ -0,0 +1,44 @@ +// src/routes/Register/index.ts +import express, { Request, Response } from 'express'; +import { LoginData as LD } from '../../Return To Client/interface'; +import { DeleteColorScheme } from './deleteColorScheme'; +const router = express.Router(); + +function transformUserData(body: any): LD +{ + return { + ...body.UserData, + encUserData: { + ...body.UserData.encUserData, + iv: Buffer.from(body.UserData.encUserData.iv.data), + encryptedData: Buffer.from(body.UserData.encUserData.encryptedData.data), + tag: Buffer.from(body.UserData.encUserData.tag.data) + } + } as LD; +} + +router.post('/deleteColorScheme', async (req: Request, res: Response) => +{ + try + { + const body = req.body as { UserData: LD, id: number; }; + if (body.UserData === undefined || body.id === undefined) + { + res.json({ code: -101, message: 'Data is not complete' }); + return; + } + + const deleteColorScheme = new DeleteColorScheme(); + const transformedUserData = transformUserData(body); + const data = { UserData: transformedUserData, id: body.id }; + const returndata = await deleteColorScheme.performAction(data); + res.json(returndata); + } catch (error) + { + res.json({ code: -101, message: 'Error' }); + return; + } + +}); + +export default router; \ No newline at end of file diff --git a/server/src/routes/DeleteSubArea/deleteSubArea.ts b/server/src/routes/DeleteSubArea/deleteSubArea.ts new file mode 100644 index 0000000..e88060f --- /dev/null +++ b/server/src/routes/DeleteSubArea/deleteSubArea.ts @@ -0,0 +1,79 @@ +import { AES_256_GCM } from "../../Crypto/AES-256-GCM"; +import { AES_256_GCMEncrypted } from "../../Crypto/interface"; +import { ReturnData } from "../../Return To Client"; +import { LoginData as LD, UserData as UD } from "../../Return To Client/interface"; +import { DBSelect } from "../../SQL/dbSelect"; +import { DBUpdate } from "../../SQL/dbUpdate"; + +export class DeleteSubArea{ + returnData = new ReturnData(); + async performAction(data : {UserData: LD, id: number;}){ + try + { + const decUserData: UD = this.decryptUserData(data.UserData.encUserData, data.UserData.userData.id.toString()); + const userData: UD = data.UserData.userData; + + if (JSON.stringify(decUserData) === JSON.stringify(userData) && userData.class === 3) + { + const dbSelect = new DBSelect(); + + const dbUpdate = new DBUpdate(); + + const subArea = await dbSelect.selectSubAreaByID(data.id); + + const constrait = await dbSelect.checkSubAreaConstraint(data.id); + if(constrait.length !== 0){ + const returnData = this.returnData.returnClientData(-101, 'You cannot delete this sub area because it is used by some articles'); + return returnData; + } + + if(subArea.length === 0) + { + const returnData = this.returnData.returnClientData(-101, 'Data not found'); + return returnData; + } + + const result = await dbUpdate.deleteSubArea(data.id); + + if(result.affectedRows === 1) + { + const returnData = this.returnData.returnClientData(0, 'Success'); + return returnData; + } else + { + const returnData = this.returnData.returnClientData(-101, 'Data not found'); + return returnData; + } + + } else + { + const returnData = this.returnData.returnClientData(-101, 'User data not match'); + return returnData; + } + + + } catch (error) + { + // invalid iv + if (error instanceof Error && error.message.includes('Unsupported state or unable to authenticate data')) + { + const returnData = this.returnData.returnClientData(-102, 'Invalid IV or Auth Tag'); + return returnData; + } else + { + const returnData = this.returnData.returnClientData(-400, 'Error'); + console.log(error); + return returnData; + } + } + } + + decryptUserData(encUserData: AES_256_GCMEncrypted, key: string) + { + const aes_256_GCM = new AES_256_GCM(); + const decUserData: UD = JSON.parse(aes_256_GCM.decrypt(encUserData.encryptedData, encUserData.iv, encUserData.tag, key)); + return decUserData; + } + + +} \ No newline at end of file diff --git a/server/src/routes/DeleteSubArea/index.ts b/server/src/routes/DeleteSubArea/index.ts new file mode 100644 index 0000000..a0572d9 --- /dev/null +++ b/server/src/routes/DeleteSubArea/index.ts @@ -0,0 +1,44 @@ +// src/routes/Register/index.ts +import express, { Request, Response } from 'express'; +import { LoginData as LD } from '../../Return To Client/interface'; +import { DeleteSubArea } from './deleteSubArea'; +const router = express.Router(); + +function transformUserData(body: any): LD +{ + return { + ...body.UserData, + encUserData: { + ...body.UserData.encUserData, + iv: Buffer.from(body.UserData.encUserData.iv.data), + encryptedData: Buffer.from(body.UserData.encUserData.encryptedData.data), + tag: Buffer.from(body.UserData.encUserData.tag.data) + } + } as LD; +} + +router.post('/deleteSubArea', async (req: Request, res: Response) => +{ + try + { + const body = req.body as { UserData: LD, id: number; }; + if (body.UserData === undefined || body.id === undefined) + { + res.json({ code: -101, message: 'Data is not complete' }); + return; + } + + const deleteSubArea = new DeleteSubArea(); + const transformedUserData = transformUserData(body); + const data = { UserData: transformedUserData, id: body.id }; + const returndata = await deleteSubArea.performAction(data); + res.json(returndata); + } catch (error) + { + res.json({ code: -101, message: 'Error' }); + return; + } + +}); + +export default router; \ No newline at end of file diff --git a/server/src/routes/GetAllArticleArea/getAllArea.ts b/server/src/routes/GetAllArticleArea/getAllArea.ts index 8317ecd..af297f0 100644 --- a/server/src/routes/GetAllArticleArea/getAllArea.ts +++ b/server/src/routes/GetAllArticleArea/getAllArea.ts @@ -1,6 +1,6 @@ import { ReturnData } from "../../Return To Client"; import { DBSelect } from "../../SQL/dbSelect"; -import { ArticleArea } from "./interface"; +import { ArticleArea, Subarea } from "./interface"; export class GetAllArticleArea { @@ -10,22 +10,39 @@ export class GetAllArticleArea try { const dbSelect = new DBSelect(); - const result = await dbSelect.selectAllArticleArea(); - const groupedResult: { [key: string]: string[] } = {}; - result.forEach(item => { - if (!groupedResult[item.ba_name]) { - groupedResult[item.ba_name] = [item.aa_area]; - } else { - groupedResult[item.ba_name].push(item.aa_area); + const result = await dbSelect.selectAllArticleArea(); + + const groupedResult: { [key: number]: Subarea[]; } = {}; + result.forEach(item => + { + const subarea: Subarea = { + subAreaID: item.aa_id, + subareaName: item.aa_area, + colorscheme: item.cs_id !== null && item.cs_textColor !== null && item.cs_backgroundColor !== null ? { + id: item.cs_id, + textColor: item.cs_textColor, + backgroundColor: item.cs_backgroundColor + } : null + }; + if (!groupedResult[item.ba_id]) + { + groupedResult[item.ba_id] = [subarea]; + } else + { + groupedResult[item.ba_id].push(subarea); } }); - const finalResult:ArticleArea[] = Object.keys(groupedResult).map(key => { + const finalResult: ArticleArea[] = Object.keys(groupedResult).map(key => + { + const foundItem = result.find(item => item.ba_id === parseInt(key)); + const bigAreaName = foundItem ? foundItem.ba_name : 'Not found'; return { - AreaName: key, - subArea: groupedResult[key] + bigareaID: parseInt(key), + bigAreaName: bigAreaName, + subarea: groupedResult[parseInt(key)] }; }); const returnData = this.returnData.returnClientData(0, 'Sucessful', finalResult); diff --git a/server/src/routes/GetAllArticleArea/interface.ts b/server/src/routes/GetAllArticleArea/interface.ts index 6cbdda9..197f572 100644 --- a/server/src/routes/GetAllArticleArea/interface.ts +++ b/server/src/routes/GetAllArticleArea/interface.ts @@ -1,4 +1,17 @@ +export interface Subarea { + subAreaID: number | null; + subareaName: string | null; + colorscheme: ColorScheme | null; +} + export interface ArticleArea { - AreaName: string; - subArea: (string | null)[]; + bigareaID: number; + bigAreaName:string; + subarea: Subarea[]; } + +interface ColorScheme { + id: number; + textColor: string; + backgroundColor: string; +} \ No newline at end of file diff --git a/server/src/routes/GetAllArticleBigArea/GetAllArticleBigArea.ts b/server/src/routes/GetAllArticleBigArea/GetAllArticleBigArea.ts new file mode 100644 index 0000000..8c272f3 --- /dev/null +++ b/server/src/routes/GetAllArticleBigArea/GetAllArticleBigArea.ts @@ -0,0 +1,32 @@ +import { ReturnData } from "../../Return To Client"; +import { DBSelect } from "../../SQL/dbSelect"; + +export class GetAllArticleBigArea +{ + returnData = new ReturnData(); + async performAction() + { + try + { + const dbSelect = new DBSelect(); + const result = await dbSelect.selectAllArticleBigArea(); + + const returnResult = result.map((item) => + { + return { + id: item.ba_id, + name: item.ba_name + }; + + }); + + const returnData = this.returnData.returnClientData(0, 'Sucessful', returnResult); + return returnData; + } catch (error) + { + const returnData = this.returnData.returnClientData(-400, 'Error'); + console.log(error); + return returnData; + } + } +} \ No newline at end of file diff --git a/server/src/routes/GetAllArticleBigArea/index.ts b/server/src/routes/GetAllArticleBigArea/index.ts new file mode 100644 index 0000000..20fc0af --- /dev/null +++ b/server/src/routes/GetAllArticleBigArea/index.ts @@ -0,0 +1,25 @@ +// src/routes/Login/index.ts +import express, { Request, Response } from 'express'; +import { GetAllArticleBigArea } from './GetAllArticleBigArea'; +const router = express.Router(); + +router.post('/getAllArticleBigArea', async (req: Request, res: Response) => +{ + try + { + const getAllArticleBigArea = new GetAllArticleBigArea(); + + const result = await getAllArticleBigArea.performAction(); + + res.json(result); + + } catch (error) + { + res.json({ code: -101, message: 'Error' }); + return; + } + + + +}); +export default router; diff --git a/server/src/routes/GetAllColorScheme/getAllColorScheme.ts b/server/src/routes/GetAllColorScheme/getAllColorScheme.ts new file mode 100644 index 0000000..043a752 --- /dev/null +++ b/server/src/routes/GetAllColorScheme/getAllColorScheme.ts @@ -0,0 +1,40 @@ +import { ReturnData } from "../../Return To Client"; +import { DBSelect } from "../../SQL/dbSelect"; + +export class GetAllColorScheme +{ + private returnData = new ReturnData(); + + async performAction() + { + try + { + const dbSelect = new DBSelect(); + const colorSchemeData = await dbSelect.selectAllColorScheme(); + if(colorSchemeData.length === 0){ + const returnData = this.returnData.returnClientData(-101, 'Color Scheme not found'); + return returnData + } else { + const reformColorSchemeData = colorSchemeData.map((colorScheme) => { + return { + id: colorScheme.cs_id, + textColor: colorScheme.cs_textColor, + backgroundColor: colorScheme.cs_backgroundColor + } + }) + const returnData = this.returnData.returnClientData(0, 'sucess', reformColorSchemeData); + return returnData + } + + } catch (error) + { + + // handle common error + const returnData = this.returnData.returnClientData(-400, 'Error', []); + console.log(error); + return returnData; + + } + + } +} \ No newline at end of file diff --git a/server/src/routes/GetAllColorScheme/index.ts b/server/src/routes/GetAllColorScheme/index.ts new file mode 100644 index 0000000..fae207a --- /dev/null +++ b/server/src/routes/GetAllColorScheme/index.ts @@ -0,0 +1,24 @@ +// src/routes/Login/index.ts +import express, { Request, Response } from 'express'; +import { GetAllColorScheme } from './getAllColorScheme'; +const router = express.Router(); + +router.post('/getAllColorScheme', async (req: Request, res: Response) => +{ + try + { + const getAllColorScheme = new GetAllColorScheme(); + + const returnData = await getAllColorScheme.performAction(); + res.json(returnData); + + } catch (error) + { + res.json({ code: -101, message: 'Error' }); + return; + } + + + +}); +export default router; diff --git a/server/src/routes/GetUserProfileData/normalRequestUserProfile.ts b/server/src/routes/GetUserProfileData/normalRequestUserProfile.ts index 299e9dd..74730ef 100644 --- a/server/src/routes/GetUserProfileData/normalRequestUserProfile.ts +++ b/server/src/routes/GetUserProfileData/normalRequestUserProfile.ts @@ -5,15 +5,8 @@ import { ValidationError } from '../../Validators/inputControl'; export class NormalRequestUserProfile { + private returnData = new ReturnData(); - - - private returnData: ReturnData; - - constructor() - { - this.returnData = new ReturnData(); - } async requestUserProfile(id: { id: string; }): Promise { try diff --git a/server/src/routes/UpdateBigArea/index.ts b/server/src/routes/UpdateBigArea/index.ts new file mode 100644 index 0000000..cc2c014 --- /dev/null +++ b/server/src/routes/UpdateBigArea/index.ts @@ -0,0 +1,43 @@ +// src/routes/Register/index.ts +import express, { Request, Response } from 'express'; +import { LoginData as LD } from '../../Return To Client/interface'; +import { UpdateBigArea } from './updateBigArea'; +const router = express.Router(); + +function transformUserData(body: any): LD +{ + return { + ...body.UserData, + encUserData: { + ...body.UserData.encUserData, + iv: Buffer.from(body.UserData.encUserData.iv.data), + encryptedData: Buffer.from(body.UserData.encUserData.encryptedData.data), + tag: Buffer.from(body.UserData.encUserData.tag.data) + } + } as LD; +} + +router.post('/updateBigArea', async (req: Request, res: Response) => +{ + try + { + const body = req.body as { UserData: LD, id:number, name:string; }; + if (body.UserData === undefined || body.id === undefined || body.name === undefined) + { + res.json({ code: -101, message: 'Data is not complete' }); + return; + } + const updateBigArea = new UpdateBigArea(); + const transformedUserData = transformUserData(body); + const data = { UserData: transformedUserData, id: body.id, name: body.name }; + const returndata = await updateBigArea.performAction(data); + res.json(returndata); + } catch (error) + { + res.json({ code: -101, message: 'Error' }); + return; + } + +}); + +export default router; \ No newline at end of file diff --git a/server/src/routes/UpdateBigArea/updateBigArea.ts b/server/src/routes/UpdateBigArea/updateBigArea.ts new file mode 100644 index 0000000..a7b9eec --- /dev/null +++ b/server/src/routes/UpdateBigArea/updateBigArea.ts @@ -0,0 +1,84 @@ +import { AES_256_GCM } from "../../Crypto/AES-256-GCM"; +import { AES_256_GCMEncrypted } from "../../Crypto/interface"; +import { ReturnData } from "../../Return To Client"; +import { LoginData as LD, ReturnClientData, UserData as UD } from "../../Return To Client/interface"; +import { DBSelect } from "../../SQL/dbSelect"; +import { DBUpdate } from "../../SQL/dbUpdate"; +import { InputControl, ValidationError } from "../../Validators/inputControl"; +import { setting_emailFromUser } from "../UpdateEmailSettings/interface"; +export class UpdateBigArea{ + + private returnData = new ReturnData(); + + async performAction(data: { UserData: LD, id: number, name:string }): Promise + { + try + { + const decUserData: UD = this.decryptUserData(data.UserData.encUserData, data.UserData.userData.id.toString()); + const userData: UD = data.UserData.userData; + if (JSON.stringify(decUserData) === JSON.stringify(userData) && userData.class === 3) + { + this.validation( data.id, data.name); + const dbSelect = new DBSelect(); + const bigArea = await dbSelect.selectBigAreaByID(data.id); + if (bigArea.length === 0) + { + const returnData = this.returnData.returnClientData(-101, 'Big area not exist'); + return returnData; + } + const dbUpdate = new DBUpdate(); + const ResultSetHeader = await dbUpdate.updateBigArea(data.id, data.name); + if (ResultSetHeader.affectedRows === 1) + { + const returnData = this.returnData.returnClientData(0, 'Success'); + return returnData; + } else + { + const returnData = this.returnData.returnClientData(-101, 'Update failed'); + return returnData; + } + + } else + { + const returnData = this.returnData.returnClientData(-101, 'User data not match'); + return returnData; + } + + + } catch (error) + { + if (error instanceof ValidationError) + { + const returnData = this.returnData.returnClientData(error.code, error.message, []); + return returnData; + } + // invalid iv + if (error instanceof Error && error.message.includes('Unsupported state or unable to authenticate data')) + { + const returnData = this.returnData.returnClientData(-102, 'Invalid IV or Auth Tag'); + return returnData; + } else + { + const returnData = this.returnData.returnClientData(-400, 'Error'); + return returnData; + } + } + } + + decryptUserData(encUserData: AES_256_GCMEncrypted, key: string) + { + const aes_256_GCM = new AES_256_GCM(); + const decUserData: UD = JSON.parse(aes_256_GCM.decrypt(encUserData.encryptedData, encUserData.iv, encUserData.tag, key)); + return decUserData; + } + + validation(id:number, name:string): void + { + const inputControl = new InputControl(); + inputControl.validateCommonSQLID(id); + inputControl.validateBigAreaName(name); + + } + + +} \ No newline at end of file diff --git a/server/src/routes/UpdateSubArea/index.ts b/server/src/routes/UpdateSubArea/index.ts new file mode 100644 index 0000000..663253e --- /dev/null +++ b/server/src/routes/UpdateSubArea/index.ts @@ -0,0 +1,44 @@ +// src/routes/Register/index.ts +import express, { Request, Response } from 'express'; +import { LoginData as LD } from '../../Return To Client/interface'; +import { UpdateSubArea } from './updateSubArea'; +const router = express.Router(); + +function transformUserData(body: any): LD +{ + return { + ...body.UserData, + encUserData: { + ...body.UserData.encUserData, + iv: Buffer.from(body.UserData.encUserData.iv.data), + encryptedData: Buffer.from(body.UserData.encUserData.encryptedData.data), + tag: Buffer.from(body.UserData.encUserData.tag.data) + } + } as LD; +} + +router.post('/updateSubArea', async (req: Request, res: Response) => +{ + try + { + const body = req.body as { UserData: LD, id:number, name:string, bigAreaID:number, colorSchemeID:number }; + console.log(body); + if (body.UserData === undefined || body.id === undefined || body.name === undefined || body.bigAreaID === undefined || body.colorSchemeID === undefined) + { + res.json({ code: -101, message: 'Data is not complete' }); + return; + } + const updateSubArea = new UpdateSubArea(); + const transformedUserData = transformUserData(body); + const data = { UserData: transformedUserData, id: body.id, name: body.name, bigAreaID: body.bigAreaID, colorSchemeID: body.colorSchemeID }; + const returndata = await updateSubArea.performAction(data); + res.json(returndata); + } catch (error) + { + res.json({ code: -101, message: 'Error' }); + return; + } + +}); + +export default router; \ No newline at end of file diff --git a/server/src/routes/UpdateSubArea/updateSubArea.ts b/server/src/routes/UpdateSubArea/updateSubArea.ts new file mode 100644 index 0000000..4689056 --- /dev/null +++ b/server/src/routes/UpdateSubArea/updateSubArea.ts @@ -0,0 +1,95 @@ +import { AES_256_GCM } from "../../Crypto/AES-256-GCM"; +import { AES_256_GCMEncrypted } from "../../Crypto/interface"; +import { ReturnData } from "../../Return To Client"; +import { ReturnClientData } from "../../Return To Client/interface"; +import { LoginData as LD } from "../../Return To Client/interface"; +import { UserData as UD } from "../../Return To Client/interface"; +import { DBSelect } from "../../SQL/dbSelect"; +import { DBUpdate } from "../../SQL/dbUpdate"; +import { InputControl, ValidationError } from "../../Validators/inputControl"; + +export class UpdateSubArea +{ + private returnData = new ReturnData(); + + async performAction(data: { UserData: LD, id:number, name:string, bigAreaID:number, colorSchemeID:number }): Promise + { + try + { + const decUserData: UD = this.decryptUserData(data.UserData.encUserData, data.UserData.userData.id.toString()); + const userData: UD = data.UserData.userData; + if (JSON.stringify(decUserData) === JSON.stringify(userData) && userData.class === 3) + { + this.validation(data.id, data.name, data.bigAreaID, data.colorSchemeID); + + const dbSelect = new DBSelect(); + + const subArea = await dbSelect.selectSubAreaByID(data.id); + + const bigArea = await dbSelect.selectBigAreaByID(data.bigAreaID); + + const colorScheme = await dbSelect.selectColorSchemeByID(data.colorSchemeID); + + if (subArea.length === 0 || bigArea.length === 0 || colorScheme.length === 0) + { + const returnData = this.returnData.returnClientData(-101, 'Data not found'); + return returnData; + } + + const dbUpdate = new DBUpdate(); + + const result = await dbUpdate.updateSubAreaData(data.id, data.name, data.bigAreaID, data.colorSchemeID); + + if(result.affectedRows === 1) + { + const returnData = this.returnData.returnClientData(0, 'Success'); + return returnData; + } else + { + const returnData = this.returnData.returnClientData(-101, 'Data not found'); + return returnData; + } + } else + { + const returnData = this.returnData.returnClientData(-101, 'User data not match'); + return returnData; + } + + + } catch (error) + { + if (error instanceof ValidationError) + { + const returnData = this.returnData.returnClientData(error.code, error.message, []); + return returnData; + } + // invalid iv + if (error instanceof Error && error.message.includes('Unsupported state or unable to authenticate data')) + { + const returnData = this.returnData.returnClientData(-102, 'Invalid IV or Auth Tag'); + return returnData; + } else + { + const returnData = this.returnData.returnClientData(-400, 'Error'); + return returnData; + } + } + } + + decryptUserData(encUserData: AES_256_GCMEncrypted, key: string) + { + const aes_256_GCM = new AES_256_GCM(); + const decUserData: UD = JSON.parse(aes_256_GCM.decrypt(encUserData.encryptedData, encUserData.iv, encUserData.tag, key)); + return decUserData; + } + + validation(id:number, name:string, bigAreaID:number, colorSchemeID:number): void + { + const inputControl = new InputControl(); + inputControl.validateCommonSQLID(id); + inputControl.validateCommonSQLID(bigAreaID); + inputControl.validateCommonSQLID(colorSchemeID); + inputControl.validateSubAreaName(name); + } + +} \ No newline at end of file diff --git a/src/scss/AdminPage/index.scss b/src/scss/AdminPage/index.scss index 0132fe7..c5656ca 100644 --- a/src/scss/AdminPage/index.scss +++ b/src/scss/AdminPage/index.scss @@ -1,8 +1,9 @@ -.contentDiv{ - .adminPageWrapper{ +.contentDiv { + .adminPageWrapper { height: 100%; display: flex; - .sideNavBarWrapper{ + + .sideNavBarWrapper { width: 20%; height: 100%; left: 0; @@ -11,9 +12,10 @@ flex-direction: column; border-right: 1px solid rgba(0, 0, 0, 0.12); - .adminPageSideBarCommonBtnWrapper{ + .adminPageSideBarCommonBtnWrapper { user-select: none; - .adminPageSideBarCommonBtn{ + + .adminPageSideBarCommonBtn { width: 100%; height: 50px; padding-left: 10px; @@ -26,10 +28,12 @@ position: relative; overflow: hidden; } - .adminPageSideBarCommonBtn:hover{ + + .adminPageSideBarCommonBtn:hover { background-color: #ebebeb; } - .adminPageSideBarCommonBtnActive{ + + .adminPageSideBarCommonBtnActive { background-color: rgb(216, 220, 240); color: #3f51b5; width: 100%; @@ -44,7 +48,8 @@ position: relative; overflow: hidden; } - .adminPageSideBarCommonBtnActive:hover{ + + .adminPageSideBarCommonBtnActive:hover { background-color: rgb(197, 202, 232); } @@ -57,38 +62,48 @@ animation: rippleAnimation .6s linear; pointer-events: none; } - + @keyframes rippleAnimation { to { transform: scale(4); opacity: 0; } } - + } - .subItemBtnWrapper{ + .subItemBtnWrapper { height: 0px; overflow: hidden; transition: height 300ms cubic-bezier(0.4, 0, 0.2, 1) 0ms; - .subItemBtn{ - + + .subItemBtn { + padding-left: 20px; height: 50px; } } } - .MainWrapper{ - background-color: white; + .MainWrapper { + background-color: #fafafa; width: 80%; display: flex; flex-direction: column; overflow: auto; - .Title{ + + .commonFullScreenTrackClickWrapper{ + position: fixed; + width: 100%; + height: 100%; + top: 0; + left: 0; + } + + .Title { width: 100%; padding: 15px 0; - display: flex; + display: flex; align-items: center; justify-content: flex-start; padding-left: 20px; @@ -97,25 +112,29 @@ border-bottom: 1px solid rgba(0, 0, 0, 0.12); } - .mainItemWrapper{ + .mainItemWrapper { padding: 24px 24px; display: flex; flex-direction: column; - .mainItemControlWrapper{ + + .mainItemControlWrapper { width: 100%; float: left; - .switchWrapper{ + + .switchWrapper { display: flex; align-items: center; cursor: pointer; float: left; - .switchRoot{ + + .switchRoot { position: relative; width: 55px; height: 36px; padding: 10px; cursor: pointer; - .switchTrack{ + + .switchTrack { background-color: black; opacity: 0.4; width: 100%; @@ -124,10 +143,12 @@ z-index: -1; transition: all 0.2s ease-in-out; } - .switchTrackActive{ + + .switchTrackActive { background-color: #f50057; } - .switchButtonWrapper{ + + .switchButtonWrapper { position: absolute; top: 0; left: 0; @@ -135,63 +156,72 @@ z-index: 1; border-radius: 50%; transition: 0.2s ease-in-out all; - .switchButton{ + + .switchButton { width: 20px; height: 20px; border-radius: 50%; background-color: white; - box-shadow:0px 2px 1px -1px rgba(0,0,0,0.2), 0px 1px 1px 0px rgba(0,0,0,0.14), 0px 1px 3px 0px rgba(0,0,0,0.12); + box-shadow: 0px 2px 1px -1px rgba(0, 0, 0, 0.2), 0px 1px 1px 0px rgba(0, 0, 0, 0.14), 0px 1px 3px 0px rgba(0, 0, 0, 0.12); transition: 0.2s ease-in-out all; } } - .switchButtonWrapperActive{ + + .switchButtonWrapperActive { transform: translateX(20px); - .switchButton{ + + .switchButton { background-color: #f50057; } } - .switchButtonWrapper:hover{ - background-color:rgba(0, 0, 0, 0.04) + + .switchButtonWrapper:hover { + background-color: rgba(0, 0, 0, 0.04) } - .switchButtonWrapperActive:hover{ + + .switchButtonWrapperActive:hover { background-color: rgba(245, 0, 87, 0.04) !important; } } - .switchRoot:hover{ - .switchButtonWrapper{ - background-color:rgba(0, 0, 0, 0.04) + + .switchRoot:hover { + .switchButtonWrapper { + background-color: rgba(0, 0, 0, 0.04) } } - .switchTitle{ + + .switchTitle { font-size: 16px; margin-left: 10px; } } - - .inputWrapper{ - .input{ + + .inputWrapper { + .input { padding: 6px 0; width: 80%; border: none; - box-sizing: border-box!important; + box-sizing: border-box !important; box-shadow: 0 -1px 0 0 rgba(0, 0, 0, 0.42) inset; - transition: all .3s ease; + transition: all .3s ease; } - .input:hover{ + .input:hover { box-shadow: 0 -2px 0 0 rgba(0, 0, 0) inset; } - - .input:focus{ + + .input:focus { outline: none; box-shadow: 0 -2px 0 0 #f50057 inset; } } - + } - .mainItemDescription, .mainItemTitle{ + + .mainItemDescription, + .mainItemTitle { margin-top: 3px; font-size: 14px; color: rgba(0, 0, 0, 0.54); @@ -199,45 +229,343 @@ } } - .commonButtonSubmitWrapper{ + .commonButtonSubmitWrapper { + display: flex; justify-content: flex-end; margin-top: 20px; margin-bottom: 20px; user-select: none; - .commomButtonSubmit{ + .commomButtonSubmit { margin-right: 30px; font-size: 14px; padding: 8px 16px; border-radius: 10px; - background-image:linear-gradient(195deg, #73abff 0%, #16e6f8 100%); + background-image: linear-gradient(195deg, #73abff 0%, #16e6f8 100%); color: white; cursor: pointer; transition: all .3s ease; } - .sendTestEmailButtonSubmit{ + .commonButtonSecondSubmit { margin-right: 10px; font-size: 14px; padding: 8px 16px; border-radius: 10px; - box-shadow: 0 0 0 1px #16e6f8; + box-shadow: 0 0 0 1px #16e6f8 inset; color: #5095fc; cursor: pointer; transition: all .3s ease; } - .sendTestEmailButtonSubmit:hover{ - box-shadow: 0 0 0 1px currentColor; + .commonButtonSecondSubmit:hover { + box-shadow: 0 0 0 1px currentColor inset; background-color: #f5f5ff; } - - .commomButtonSubmit:hover{ - box-shadow:0 14px 26px -12px rgba(22, 192, 248, 0.4), 0 4px 23px rgba(22, 192, 248, 0.15), 0 8px 10px -5px rgba(22, 192, 248, 0.2); - + + .commomButtonSubmit:hover { + box-shadow: 0 14px 26px -12px rgba(22, 192, 248, 0.4), 0 4px 23px rgba(22, 192, 248, 0.15), 0 8px 10px -5px rgba(22, 192, 248, 0.2); + + } + } + + .commonFullScreenWrapper { + visibility: hidden; + opacity: 0; + z-index: 9999; + position: fixed; + width: 100%; + height: 100%; + background-color: rgba(0, 0, 0, 0.5); + top: 0; + left: 0; + display: flex; + align-items: center; + justify-content: center; + transition: all .3s ease; + + .commonFullScreenMainWrapper { + margin: 32px; + border-radius: 12px; + background-color: white; + + .inputAreaWrapper { + display: flex; + flex-wrap: wrap; + + .commonItemWrapper { + padding: 20px; + display: flex; + flex-direction: column; + width: 35%; + min-width: 220px; + position: relative; + + .commonTitle { + font-size: 20px; + margin-bottom: 16px; + user-select: none; + } + + .commonInput { + font: inherit; + border: none; + box-shadow: 0 -1px 0 0 rgba(0, 0, 0, 0.42) inset; + transition: all .3s ease; + padding: 6px 0 6px; + } + + + .commonInput:hover { + box-shadow: 0 -2px 0 0 rgba(0, 0, 0) inset; + } + + .commonInput:focus { + outline: none; + box-shadow: 0 -2px 0 0 #f50057 inset; + } + + .selectInput{ + cursor: pointer; + user-select: none; + } + + .colorGroupWrapper { + display: flex; + justify-content: center; + .fontColor, + .backgroundColor { + width: 20px; + height: 20px; + border-radius: 50%; + box-shadow: 0px 0px 5px 1px rgba(0, 0, 0, 0.1); + } + + .backgroundColor { + margin-left: 15px; + } + } + + .commonDropMenuWrapper{ + visibility: hidden; + opacity: 0; + z-index: 1; + position: absolute; + top: 100px; + min-width: 200px; + box-shadow: 0px 5px 5px -3px rgba(0,0,0,0.2), 0px 8px 10px 1px rgba(0,0,0,0.14), 0px 3px 14px 2px rgba(0,0,0,0.12); + border-radius: 10px; + background-color: #fff; + transition: all 0.2s ease-in-out; + padding: 10px; + + .commonDropMenu{ + padding: 5px 16px; + border-radius: 6px; + transition: all 0.3s ease-in-out; + cursor: pointer; + } + + .commonDropMenu:hover{ + background-color: #f0f2f5; + } + + } + + .commonDropMenuWrapperActive{ + visibility: visible; + opacity: 1; + } + + .colorInput:hover { + box-shadow: 0 -2px 0 0 rgba(0, 0, 0) inset; + } + + .colorInput:focus { + outline: none; + box-shadow: 0 -2px 0 0 #f50057 inset; + } + + .colorPlatteInput { + margin-top: 8px; + width: 100%; + height: 50px; + border: none; + border-radius: 5px; + background: #fafafa; + transition: all .3s ease; + } + + } + + .sampleWrapper { + padding: 20px; + display: flex; + min-width: 220px; + width: 30%; + + .colorScheme-tagExample { + user-select: none; + margin: auto; + padding: 4px 10px; + font-size: 0.875em; + border-radius: 5px; + cursor: pointer; + color: white; + background-color: #000000; + } + } + } + + } + } + + .commonFullScreenMainWrapperActive { + visibility: visible; + opacity: 1; + } + + .commonTableWrapper { + user-select: none; + padding: 24px 20%; + border-spacing: 0; + border-collapse: separate; + + .commonTitleWrapper { + + .commonTitle { + border-bottom: 1px solid rgba(224, 224, 224, 1); + padding: 12px; + } } + + .commonItemWrapper { + td { + border-bottom: 1px solid rgba(224, 224, 224, 1); + text-align: center; + position: relative; + } + + .colorWrapper { + display: flex; + align-items: center; + justify-content: center; + padding: 20px 0; + } + + + .colorGroupWrapper { + display: flex; + justify-content: center; + .fontColor, + .backgroundColor { + width: 20px; + height: 20px; + border-radius: 50%; + box-shadow: 0px 0px 5px 1px rgba(0, 0, 0, 0.1); + } + + .backgroundColor { + margin-left: 15px; + } + } + + .tagExampleWrapper { + display: flex; + + .tagExample { + margin: auto; + padding: 4px 10px; + font-size: 0.875em; + border-radius: 5px; + cursor: pointer; + } + } + + .actionWrapper { + display: flex; + align-items: center; + justify-content: space-evenly; + + .commonBtnWrapper { + padding: 8px; + border-radius: 50%; + cursor: pointer; + transition: all .3s ease; + + .commonBtn { + float: left; + cursor: pointer; + } + } + + .commonBtnWrapper:hover { + background-color: rgba(18, 18, 18, 0.06) !important; + } + } + + .commonDropMenuWrapper{ + visibility: hidden; + opacity: 0; + z-index: 1; + position: absolute; + top: 48px; + min-width: 200px; + box-shadow: 0px 5px 5px -3px rgba(0,0,0,0.2), 0px 8px 10px 1px rgba(0,0,0,0.14), 0px 3px 14px 2px rgba(0,0,0,0.12); + border-radius: 10px; + background-color: #fff; + transition: all 0.2s ease-in-out; + padding: 10px; + + .commonDropMenu{ + padding: 5px 16px; + border-radius: 6px; + transition: all 0.3s ease-in-out; + cursor: pointer; + } + + .commonDropMenu:hover{ + background-color: #f0f2f5; + } + + } + + .commonDropMenuWrapperActive{ + visibility: visible; + opacity: 1; + } + } + + td{ + padding: 0 12px; + } + + .commonInput { + border: none; + box-shadow: 0 -1px 0 0 rgba(0, 0, 0, 0.42) inset; + transition: all 0.3s ease; + padding: 6px 0 6px; + width: 100%; + margin: 12px 0; + font-size: 17px; + } + + .commonInput:hover { + box-shadow: 0 -2px 0 0 rgba(0, 0, 0) inset; + } + + .commonInput:focus { + outline: none; + box-shadow: 0 -2px 0 0 #f50057 inset; + } + + .selectInput { + cursor: pointer; + user-select: none; + } + } } } diff --git a/src/ts/404 Page/index.ts b/src/ts/404 Page/index.ts index ba31a4e..5fa0f6f 100644 --- a/src/ts/404 Page/index.ts +++ b/src/ts/404 Page/index.ts @@ -14,6 +14,7 @@ export class Page404{ private append404Page(site:HTMLElement){ const contentDiv = document.createElement('div'); contentDiv.id = 'contentDiv'; + contentDiv.className = 'contentDiv'; const wrapper404 = document.createElement('div'); diff --git a/src/ts/Admin Page/index.ts b/src/ts/Admin Page/index.ts index b5f28b7..ea79acb 100644 --- a/src/ts/Admin Page/index.ts +++ b/src/ts/Admin Page/index.ts @@ -4,7 +4,9 @@ import { SendPost } from "../Send Fetch"; import { UserVerification } from "../User Verification"; import '../../scss/AdminPage/index.scss'; import { NavigationProgress } from "../Create Navigation Progress"; -import { setting_loginandregister } from "../Send Fetch/interface"; +import { ColorScheme, setting_loginandregister } from "../Send Fetch/interface"; +import { ArticleSubArea } from "./interface"; +import { NavRelated } from "../Navigation Bar"; export class AdminPage { @@ -21,7 +23,7 @@ export class AdminPage if (parseUserData.userData.class !== 3) { const changePage = new ChangePage(); - changePage.toIndex(); + await changePage.toIndex(); this.navigationProgress.end(); return; } @@ -30,14 +32,14 @@ export class AdminPage if (!result) { const changePage = new ChangePage(); - changePage.toIndex(); + await changePage.toIndex(); this.navigationProgress.end(); return; } if (parseUserData.userData.class !== 3) { const changePage = new ChangePage(); - changePage.toIndex(); + await changePage.toIndex(); this.navigationProgress.end(); return; } @@ -131,7 +133,7 @@ export class AdminPage const sideBarSettingButton = document.createElement('div'); - const SideBarSettingSubItem = this.SideBarParameterSubItem(); + const SideBarSettingSubItem = this.SideBarSettingSubItem(); sideBarSettingWrapper.classList.add('adminPageSideBarCommonBtnWrapper'); @@ -155,8 +157,9 @@ export class AdminPage return sideBarSettingWrapper; } - private SideBarParameterSubItem() + private SideBarSettingSubItem() { + const navigationProgress = new NavigationProgress(); const loginAndRegister = () => { const sideBarParameterChildButton = document.createElement('div'); @@ -167,6 +170,7 @@ export class AdminPage sideBarParameterChildButton.onclick = async (event) => { + navigationProgress.start(); const activeElements = document.querySelectorAll('.adminPageSideBarCommonBtnActive'); activeElements.forEach((element) => @@ -179,7 +183,13 @@ export class AdminPage this.addRippleEffect(sideBarParameterChildButton, event); const adminPageWrapper = document.getElementById('adminPageWrapper'); const mainLoginAndRegister = await this.MainLoginAndRegisterSetting(); + const wrapper = document.getElementById('MainWrapper'); + if (wrapper) + { + wrapper.remove(); + } adminPageWrapper?.appendChild(mainLoginAndRegister); + navigationProgress.end(); }; return sideBarParameterChildButton; }; @@ -194,6 +204,7 @@ export class AdminPage sideBarParameterChildButton.onclick = async (event) => { + navigationProgress.start(); const activeElements = document.querySelectorAll('.adminPageSideBarCommonBtnActive'); activeElements.forEach((element) => @@ -206,8 +217,114 @@ export class AdminPage this.addRippleEffect(sideBarParameterChildButton, event); const adminPageWrapper = document.getElementById('adminPageWrapper'); const emailWrapper = await this.MainEmail(); + const wrapper = document.getElementById('MainWrapper'); + if (wrapper) + { + wrapper.remove(); + } adminPageWrapper?.appendChild(emailWrapper); + navigationProgress.end(); + }; + return sideBarParameterChildButton; + }; + + const colorScheme = () => + { + const sideBarParameterChildButton = document.createElement('div'); + sideBarParameterChildButton.classList.add('adminPageSideBarCommonBtn'); + sideBarParameterChildButton.classList.add('subItemBtn'); + + sideBarParameterChildButton.innerText = 'Color Scheme'; + + sideBarParameterChildButton.onclick = async (event) => + { + navigationProgress.start(); + const activeElements = document.querySelectorAll('.adminPageSideBarCommonBtnActive'); + + activeElements.forEach((element) => + { + element.classList.remove('adminPageSideBarCommonBtnActive'); + element.classList.add('adminPageSideBarCommonBtn'); + }); + + sideBarParameterChildButton.classList.add('adminPageSideBarCommonBtnActive'); + this.addRippleEffect(sideBarParameterChildButton, event); + const adminPageWrapper = document.getElementById('adminPageWrapper'); + const colorScheme = await this.MainColorScheme(); + const wrapper = document.getElementById('MainWrapper'); + if (wrapper) + { + wrapper.remove(); + } + adminPageWrapper?.appendChild(colorScheme); + navigationProgress.end(); + }; + return sideBarParameterChildButton; + }; + + const Area = () => + { + const sideBarParameterChildButton = document.createElement('div'); + sideBarParameterChildButton.classList.add('adminPageSideBarCommonBtn'); + sideBarParameterChildButton.classList.add('subItemBtn'); + + sideBarParameterChildButton.innerText = 'Article Area'; + + sideBarParameterChildButton.onclick = async (event) => + { + const activeElements = document.querySelectorAll('.adminPageSideBarCommonBtnActive'); + + activeElements.forEach((element) => + { + element.classList.remove('adminPageSideBarCommonBtnActive'); + element.classList.add('adminPageSideBarCommonBtn'); + }); + + sideBarParameterChildButton.classList.add('adminPageSideBarCommonBtnActive'); + this.addRippleEffect(sideBarParameterChildButton, event); + const adminPageWrapper = document.getElementById('adminPageWrapper'); + const area = await this.MainArea(); + const wrapper = document.getElementById('MainWrapper'); + if (wrapper) + { + wrapper.remove(); + } + adminPageWrapper?.appendChild(area); + + }; + return sideBarParameterChildButton; + }; + + const subArea = () => + { + const sideBarParameterChildButton = document.createElement('div'); + sideBarParameterChildButton.classList.add('adminPageSideBarCommonBtn'); + sideBarParameterChildButton.classList.add('subItemBtn'); + + sideBarParameterChildButton.innerText = 'Article Sub Area'; + + sideBarParameterChildButton.onclick = async (event) => + { + navigationProgress.start(); + const activeElements = document.querySelectorAll('.adminPageSideBarCommonBtnActive'); + activeElements.forEach((element) => + { + element.classList.remove('adminPageSideBarCommonBtnActive'); + element.classList.add('adminPageSideBarCommonBtn'); + }); + + sideBarParameterChildButton.classList.add('adminPageSideBarCommonBtnActive'); + this.addRippleEffect(sideBarParameterChildButton, event); + const adminPageWrapper = document.getElementById('adminPageWrapper'); + const subArea = await this.MainSubArea(); + const wrapper = document.getElementById('MainWrapper'); + if (wrapper) + { + wrapper.remove(); + } + adminPageWrapper?.appendChild(subArea); + navigationProgress.end(); }; return sideBarParameterChildButton; }; @@ -219,36 +336,35 @@ export class AdminPage const LoginAndRegisterBtn = loginAndRegister(); const EmailBtn = Email(); + const ColorSchemeBtn = colorScheme(); + const AreaBtn = Area(); + const SubAreaBtn = subArea(); sideBarParameterChildWrapper.appendChild(LoginAndRegisterBtn); sideBarParameterChildWrapper.appendChild(EmailBtn); - + sideBarParameterChildWrapper.appendChild(ColorSchemeBtn); + sideBarParameterChildWrapper.appendChild(AreaBtn); + sideBarParameterChildWrapper.appendChild(SubAreaBtn); return sideBarParameterChildWrapper; } private addRippleEffect(target: HTMLElement, e: MouseEvent) { - // 创建水波纹元素 const ripple = document.createElement('div'); ripple.classList.add('ripple'); - // 计算水波纹的位置和大小 const rect = target.getBoundingClientRect(); const size = Math.max(rect.width, rect.height); - // 计算相对于按钮的坐标 const x = e.clientX - rect.left - window.scrollX - size / 2; const y = e.clientY - rect.top - window.scrollY - size / 2; - // 设置水波纹元素的样式和位置 ripple.style.width = ripple.style.height = `${size}px`; ripple.style.left = `${x}px`; ripple.style.top = `${y}px`; - // 将水波纹元素添加到按钮中 target.appendChild(ripple); - // 使用动画结束事件来移除水波纹元素 ripple.addEventListener('animationend', () => { ripple.remove(); @@ -279,8 +395,6 @@ export class AdminPage private async MainLoginAndRegisterSetting() { - - function createButtonSubmit() { const buttonSubmitWrapper = document.createElement('div'); @@ -308,10 +422,6 @@ export class AdminPage const LoginAndRegisterSetting = await sendPost.getLoginAndRegisterSettings(); const wrapper = document.getElementById('MainWrapper'); - if (wrapper) - { - wrapper.remove(); - } const MainWrapper = document.createElement('div'); MainWrapper.className = 'MainWrapper'; MainWrapper.id = 'MainWrapper'; @@ -372,7 +482,7 @@ export class AdminPage buttonSubmit.innerHTML = 'Save'; const buttonSendTestEmail = document.createElement('div'); - buttonSendTestEmail.className = 'sendTestEmailButtonSubmit'; + buttonSendTestEmail.className = 'commonButtonSecondSubmit'; buttonSendTestEmail.innerHTML = 'Send Test Email'; buttonSendTestEmail.onclick = async () => @@ -383,23 +493,15 @@ export class AdminPage buttonSubmit.onclick = async () => { - if(!EmailSetting) return; + if (!EmailSetting) return; const sendPost = new SendPost(); const result = await sendPost.updateEmailSettings(EmailSetting); }; buttonSubmitWrapper.appendChild(buttonSendTestEmail); buttonSubmitWrapper.appendChild(buttonSubmit); - - return buttonSubmitWrapper; - } - - const wrapper = document.getElementById('MainWrapper'); - - if (wrapper) - { - wrapper.remove(); + return buttonSubmitWrapper; } const MainWrapper = document.createElement('div'); @@ -531,96 +633,1516 @@ export class AdminPage } - CreateSwitchOption(Title: string, description: string, option: number, onChange: (newValue: number) => void) + private async MainColorScheme() { - const itemWrapper = document.createElement('div'); - itemWrapper.className = 'mainItemWrapper'; - const contorlWrapper = document.createElement('div'); - contorlWrapper.className = 'mainItemControlWrapper'; + function displayColorSchemeTitle() + { + const colorSchemeTitleWrapper = document.createElement('tr'); - const switchWrapper = document.createElement('div'); - switchWrapper.className = 'switchWrapper'; + colorSchemeTitleWrapper.className = 'commonTitleWrapper'; - const switchRoot = document.createElement('div'); + const colorSchemeTitle = document.createElement('th'); + colorSchemeTitle.className = 'commonTitle'; + colorSchemeTitle.innerText = 'Color Scheme'; - switchRoot.className = 'switchRoot'; + const tagSamle = document.createElement('th'); - const switchTitle = document.createElement('div'); + tagSamle.className = 'commonTitle'; + tagSamle.innerText = 'Tag Example'; - switchTitle.className = 'switchTitle'; + const actionTitle = document.createElement('th'); - switchTitle.innerText = Title; + actionTitle.className = 'commonTitle'; + actionTitle.innerText = 'Action'; - const switchTrack = document.createElement('div'); - switchTrack.className = 'switchTrack'; + colorSchemeTitleWrapper.appendChild(colorSchemeTitle); + colorSchemeTitleWrapper.appendChild(tagSamle); + colorSchemeTitleWrapper.appendChild(actionTitle); - const switchButtonWrapper = document.createElement('div'); - switchButtonWrapper.className = 'switchButtonWrapper'; + return colorSchemeTitleWrapper; + } - const switchButton = document.createElement('div'); - switchButton.className = 'switchButton'; + function createColorSchemeItem(colorScheme: ColorScheme) + { + function createColorWrapper() + { + const colorWrapper = document.createElement('td'); + colorWrapper.className = 'colorWrapper'; - const descriptionDiv = document.createElement('div'); - descriptionDiv.className = 'mainItemDescription'; - descriptionDiv.innerText = description; + const colorGroupWrapper = document.createElement('div'); + colorGroupWrapper.className = 'colorGroupWrapper'; - switchWrapper.onclick = () => + const fontColor = document.createElement('div'); + const backgroundColor = document.createElement('div'); + fontColor.className = 'fontColor'; + backgroundColor.className = 'backgroundColor'; + + fontColor.style.backgroundColor = colorScheme.textColor; + backgroundColor.style.backgroundColor = colorScheme.backgroundColor; + + colorGroupWrapper.appendChild(fontColor); + colorGroupWrapper.appendChild(backgroundColor); + colorWrapper.appendChild(colorGroupWrapper); + + return colorWrapper; + } + function createTagExampleWrapper() + { + const tagtd = document.createElement('td'); + const tagExampleWrapper = document.createElement('div'); + tagExampleWrapper.className = 'tagExampleWrapper'; + + const tagExample = document.createElement('div'); + tagExample.className = 'tagExample'; + tagExample.innerText = 'Tag Example'; + + tagExample.style.color = colorScheme.textColor; + tagExample.style.backgroundColor = colorScheme.backgroundColor; + + tagExampleWrapper.appendChild(tagExample); + tagtd.appendChild(tagExampleWrapper); + return tagtd; + } + + function createDeletebtn() + { + const actiontd = document.createElement('td'); + const actionWrapper = document.createElement('div'); + + actionWrapper.className = 'actionWrapper'; + + const deleteBtnWrapper = document.createElement('div'); + + deleteBtnWrapper.className = 'commonBtnWrapper'; + + const deleteBtn = document.createElement('div'); + + deleteBtn.className = 'commonBtn'; + deleteBtn.innerText = '🗑️'; + + deleteBtnWrapper.onclick = async () => + { + const sendPost = new SendPost(); + const result = await sendPost.deleteColorScheme(colorScheme.id); + if (result) + { + colorSchemeItemWrapper.remove(); + } + }; + + deleteBtnWrapper.appendChild(deleteBtn); + actionWrapper.appendChild(deleteBtnWrapper); + actiontd.appendChild(actionWrapper); + return actiontd; + } + const colorSchemeItemWrapper = document.createElement('tr'); + colorSchemeItemWrapper.className = 'commonItemWrapper'; + + const colorWrapper = createColorWrapper(); + const tagExampleWrapper = createTagExampleWrapper(); + const actionWrapper = createDeletebtn(); + colorSchemeItemWrapper.appendChild(colorWrapper); + colorSchemeItemWrapper.appendChild(tagExampleWrapper); + colorSchemeItemWrapper.appendChild(actionWrapper); + + return colorSchemeItemWrapper; + } + + + function createAddColorSchemeBtn() { - option = option === 0 ? 1 : 0; - this.ChangeToSwitchRootActive(option, switchButtonWrapper, switchTrack); - onChange(option); - }; + const buttonSubmitWrapper = document.createElement('div'); + buttonSubmitWrapper.className = 'commonButtonSubmitWrapper'; - this.ChangeToSwitchRootActive(option, switchButtonWrapper, switchTrack); + buttonSubmitWrapper.style.justifyContent = 'flex-start'; - switchButtonWrapper.appendChild(switchButton); + buttonSubmitWrapper.style.paddingLeft = '20%'; - switchRoot.appendChild(switchButtonWrapper); - switchRoot.appendChild(switchTrack); + const buttonAddColorScheme = document.createElement('div'); + buttonAddColorScheme.className = 'commonButtonSecondSubmit'; + buttonAddColorScheme.innerHTML = 'Add Color Scheme'; - switchWrapper.appendChild(switchRoot); - switchWrapper.appendChild(switchTitle); + buttonAddColorScheme.onclick = async () => + { + const addColorScheme = document.querySelector('.commonFullScreenWrapper') as HTMLDivElement; + if (addColorScheme) + { + addColorScheme.classList.add('commonFullScreenMainWrapperActive'); + } - contorlWrapper.appendChild(switchWrapper); + }; - itemWrapper.appendChild(contorlWrapper); - itemWrapper.appendChild(descriptionDiv); - return itemWrapper; - } - ChangeToSwitchRootActive(option: number, switchButtonWrapper: HTMLDivElement, switchTrack: HTMLDivElement) - { - switchButtonWrapper.classList.toggle('switchButtonWrapperActive', option === 1); - switchTrack.classList.toggle('switchTrackActive', option === 1); + buttonSubmitWrapper.appendChild(buttonAddColorScheme); + + return buttonSubmitWrapper; + } + + function createAddColorScheme() + { + + function createtColorPlatte(title: string, color: string, onChange: (newValue: string) => void) + { + const colorPlatteWrapper = document.createElement('div'); + + colorPlatteWrapper.className = 'commonItemWrapper'; + + const colorPlatteTitle = document.createElement('div'); + + colorPlatteTitle.className = 'commonTitle'; + colorPlatteTitle.innerText = title; + + const colorInput = document.createElement('input'); + colorInput.className = 'commonInput'; + colorInput.type = 'text'; + colorInput.value = color; + + const colorPlatteInput = document.createElement('input'); + colorPlatteInput.className = 'colorPlatteInput'; + colorPlatteInput.type = 'color'; + colorPlatteInput.value = color; + + colorPlatteInput.oninput = () => + { + colorInput.value = colorPlatteInput.value; + onChange(colorPlatteInput.value); + }; + + colorInput.oninput = () => + { + colorPlatteInput.value = colorInput.value; + onChange(colorPlatteInput.value); + }; + + colorPlatteWrapper.appendChild(colorPlatteTitle); + colorPlatteWrapper.appendChild(colorInput); + colorPlatteWrapper.appendChild(colorPlatteInput); + return colorPlatteWrapper; + } + + function CreateSample() + { + const sampleWrapper = document.createElement('div'); + + sampleWrapper.className = 'sampleWrapper'; + + const tagExample = document.createElement('div'); + tagExample.className = 'colorScheme-tagExample'; + + tagExample.innerText = 'Tag Example'; + + sampleWrapper.appendChild(tagExample); + + + return sampleWrapper; + } + + function createButtonSubmit() + { + const buttonSubmitWrapper = document.createElement('div'); + buttonSubmitWrapper.className = 'commonButtonSubmitWrapper'; + + const buttonSubmit = document.createElement('div'); + buttonSubmit.className = 'commomButtonSubmit'; + buttonSubmit.innerHTML = 'Save'; + + const buttonCancel = document.createElement('div'); + buttonCancel.className = 'commonButtonSecondSubmit'; + buttonCancel.innerHTML = 'Cancel'; + + buttonCancel.onclick = async () => + { + addColorSchemeWrapper.classList.remove('commonFullScreenMainWrapperActive'); + }; + + buttonSubmit.onclick = async () => + { + const sendPost = new SendPost(); + const result = await sendPost.addColorScheme(textColor, backgroundColor); + if (result) + { + const colorSchemeItemWrapper = createColorSchemeItem({ + id: result.id, + textColor: textColor, + backgroundColor: backgroundColor + }); + colorSchemeWrapper.appendChild(colorSchemeItemWrapper); + addColorSchemeWrapper.classList.remove('commonFullScreenMainWrapperActive'); + } + }; + + buttonSubmitWrapper.appendChild(buttonCancel); + buttonSubmitWrapper.appendChild(buttonSubmit); + + return buttonSubmitWrapper; + } + + let textColor: string = `#ffffff`; + let backgroundColor: string = `#000000`; + const addColorSchemeWrapper = document.createElement('div'); + addColorSchemeWrapper.className = 'commonFullScreenWrapper'; + + const colorSchemeMainWrapper = document.createElement('div'); + colorSchemeMainWrapper.className = 'commonFullScreenMainWrapper'; + colorSchemeMainWrapper.style.width = '80%'; + + const inputAreaWrapper = document.createElement('div'); + + inputAreaWrapper.className = 'inputAreaWrapper'; + + const textColorPlatte = createtColorPlatte( + `Text Color`, + textColor, + (newValue: string) => + { + textColor = newValue; + const tagExample = document.querySelector('.colorScheme-tagExample') as HTMLDivElement; + if (tagExample) + { + tagExample.style.color = textColor; + } + } + ); + + const backgroundColorPlatte = createtColorPlatte( + `Background Color`, + backgroundColor, + (newValue: string) => + { + backgroundColor = newValue; + const tagExample = document.querySelector('.colorScheme-tagExample') as HTMLDivElement; + if (tagExample) + { + tagExample.style.backgroundColor = backgroundColor; + } + } + ); + + const sampleWrapper = CreateSample(); + const buttonSubmitWrapper = createButtonSubmit(); + + inputAreaWrapper.appendChild(textColorPlatte); + inputAreaWrapper.appendChild(backgroundColorPlatte); + inputAreaWrapper.appendChild(sampleWrapper); + colorSchemeMainWrapper.appendChild(inputAreaWrapper); + colorSchemeMainWrapper.appendChild(buttonSubmitWrapper); + + addColorSchemeWrapper.appendChild(colorSchemeMainWrapper); + + + return addColorSchemeWrapper; + } + + + const MainWrapper = document.createElement('div'); + MainWrapper.className = 'MainWrapper'; + MainWrapper.id = 'MainWrapper'; + + const sendPost = new SendPost(); + const colorScheme = await sendPost.getColorScheme(); + if (!colorScheme) return MainWrapper; + + const Title = document.createElement('div'); + Title.className = 'Title'; + Title.innerText = 'Color Scheme'; + + + const colorSchemeWrapper = document.createElement('table'); + colorSchemeWrapper.className = 'commonTableWrapper'; + const colorSchemeTitleWrapper = displayColorSchemeTitle(); + + + + colorSchemeWrapper.appendChild(colorSchemeTitleWrapper); + + colorScheme.forEach((colorScheme) => + { + const colorSchemeItemWrapper = createColorSchemeItem(colorScheme); + colorSchemeWrapper.appendChild(colorSchemeItemWrapper); + }); + + const addColorSchemeBtn = createAddColorSchemeBtn(); + const addColorScheme = createAddColorScheme(); + MainWrapper.appendChild(Title); + MainWrapper.appendChild(colorSchemeWrapper); + MainWrapper.appendChild(addColorSchemeBtn); + MainWrapper.appendChild(addColorScheme); + + return MainWrapper; } - CreateInputTextOption(title: string, description: string, inputContent: string | number | null, onChange: (newValue: string) => void) + private async MainArea() { - const itemWrapper = document.createElement('div'); - itemWrapper.className = 'mainItemWrapper'; + function displayAreaTitle() + { + const TitleWrapper = document.createElement('tr'); - const inputTitle = document.createElement('div'); + TitleWrapper.className = 'commonTitleWrapper'; - inputTitle.className = 'mainItemTitle'; + const colorSchemeTitle = document.createElement('th'); + colorSchemeTitle.className = 'commonTitle'; + colorSchemeTitle.innerText = 'ID'; - inputTitle.innerText = title; + const name = document.createElement('th'); - const contorlWrapper = document.createElement('div'); - contorlWrapper.className = 'mainItemControlWrapper'; + name.className = 'commonTitle'; + name.innerText = 'Area Name'; - const inputWrapper = document.createElement('div'); - inputWrapper.className = 'inputWrapper'; + const actionTitle = document.createElement('th'); - const input = document.createElement('input'); - input.className = 'input'; + actionTitle.className = 'commonTitle'; + actionTitle.innerText = 'Action'; + + TitleWrapper.appendChild(colorSchemeTitle); + TitleWrapper.appendChild(name); + TitleWrapper.appendChild(actionTitle); + + return TitleWrapper; + } + + function createAreaItem(area: { id: number; name: string; }) + { + function createIdWrapper() + { + const idWrapper = document.createElement('td'); + idWrapper.innerText = area.id.toString(); + return idWrapper; + } + + function createEditWrapper(onchange: (newValue: string) => void) + { + const editWrapper = document.createElement('td'); + const inputText = document.createElement('input'); + inputText.className = 'commonInput'; + inputText.value = area.name; + editWrapper.appendChild(inputText); + + inputText.oninput = () => + { + onchange(inputText.value); + }; + + return editWrapper; + }; + + function createBtn() + { + const actiontd = document.createElement('td'); + const actionWrapper = document.createElement('div'); + + actionWrapper.className = 'actionWrapper'; + + const saveBtnWrapper = document.createElement('div'); + saveBtnWrapper.className = 'commonBtnWrapper'; + saveBtnWrapper.onclick = async () => + { + const sendPost = new SendPost(); + const result = await sendPost.updateBigArea(area.id, areaName); + + if (result) + { + const navbar = document.getElementById('navigationBar') as HTMLDivElement; + + if (navbar) + { + navbar.remove(); + const navRelated = new NavRelated(); + await navRelated.init(); + } + } + }; + + const saveBtn = document.createElement('div'); + saveBtn.className = 'commonBtn'; + saveBtn.innerText = '💾'; + + const deleteBtnWrapper = document.createElement('div'); + deleteBtnWrapper.className = 'commonBtnWrapper'; + deleteBtnWrapper.onclick = async () => + { + const sendPost = new SendPost(); + const result = await sendPost.deleteBigArea(area.id); + if (result) + { + AreaItemWrapper.remove(); + const navbar = document.getElementById('navigationBar') as HTMLDivElement; + + if (navbar) + { + navbar.remove(); + const navRelated = new NavRelated(); + await navRelated.init(); + } + } + }; + + const deleteBtn = document.createElement('div'); + + deleteBtn.className = 'commonBtn'; + deleteBtn.innerText = '🗑️'; + + saveBtnWrapper.appendChild(saveBtn); + deleteBtnWrapper.appendChild(deleteBtn); + actionWrapper.appendChild(saveBtnWrapper); + actionWrapper.appendChild(deleteBtnWrapper); + actiontd.appendChild(actionWrapper); + return actiontd; + } + let areaName = area.name; + const AreaItemWrapper = document.createElement('tr'); + AreaItemWrapper.className = 'commonItemWrapper'; + + const IdWrapper = createIdWrapper(); + const editWrapper = createEditWrapper((newValue: string) => { areaName = newValue; }); + const actionWrapper = createBtn(); + AreaItemWrapper.appendChild(IdWrapper); + AreaItemWrapper.appendChild(editWrapper); + AreaItemWrapper.appendChild(actionWrapper); + + return AreaItemWrapper; + }; + + function createAddAreaBtn() + { + const buttonSubmitWrapper = document.createElement('div'); + buttonSubmitWrapper.className = 'commonButtonSubmitWrapper'; + + buttonSubmitWrapper.style.justifyContent = 'flex-start'; + + buttonSubmitWrapper.style.paddingLeft = '20%'; + + const buttonAddColorScheme = document.createElement('div'); + buttonAddColorScheme.className = 'commonButtonSecondSubmit'; + buttonAddColorScheme.innerHTML = 'Add Area'; + + buttonAddColorScheme.onclick = async () => + { + const addColorScheme = document.querySelector('.commonFullScreenWrapper') as HTMLDivElement; + if (addColorScheme) + { + addColorScheme.classList.add('commonFullScreenMainWrapperActive'); + } + + }; + + + + buttonSubmitWrapper.appendChild(buttonAddColorScheme); + + return buttonSubmitWrapper; + } + + function createAddArea() + { + + function createAreaInput(title: string, onChange: (newValue: string) => void) + { + const commonItemWrapper = document.createElement('div'); + + commonItemWrapper.className = 'commonItemWrapper'; + + const commonTitle = document.createElement('div'); + + commonTitle.className = 'commonTitle'; + commonTitle.innerText = title; + + const areaInput = document.createElement('input'); + areaInput.className = 'commonInput'; + areaInput.type = 'text'; + + + areaInput.oninput = () => + { + onChange(areaInput.value); + }; + + commonItemWrapper.appendChild(commonTitle); + commonItemWrapper.appendChild(areaInput); + return commonItemWrapper; + } + + function createButtonSubmit() + { + const buttonSubmitWrapper = document.createElement('div'); + buttonSubmitWrapper.className = 'commonButtonSubmitWrapper'; + + const buttonSubmit = document.createElement('div'); + buttonSubmit.className = 'commomButtonSubmit'; + buttonSubmit.innerHTML = 'Add'; + + const buttonCancel = document.createElement('div'); + buttonCancel.className = 'commonButtonSecondSubmit'; + buttonCancel.innerHTML = 'Cancel'; + + buttonCancel.onclick = async () => + { + addColorSchemeWrapper.classList.remove('commonFullScreenMainWrapperActive'); + }; + + buttonSubmit.onclick = async () => + { + const sendPost = new SendPost(); + const result = await sendPost.addBigArea(areaName); + if (result) + { + const colorSchemeItemWrapper = createAreaItem({ + id: result.id, + name: areaName + }); + AreaWrapper.appendChild(colorSchemeItemWrapper); + addColorSchemeWrapper.classList.remove('commonFullScreenMainWrapperActive'); + + + const navbar = document.getElementById('navigationBar') as HTMLDivElement; + + if (navbar) + { + navbar.remove(); + const navRelated = new NavRelated(); + await navRelated.init(); + } + } + }; + + buttonSubmitWrapper.appendChild(buttonCancel); + buttonSubmitWrapper.appendChild(buttonSubmit); + + return buttonSubmitWrapper; + } + + let areaName = ''; + const addColorSchemeWrapper = document.createElement('div'); + addColorSchemeWrapper.className = 'commonFullScreenWrapper'; + + const colorSchemeMainWrapper = document.createElement('div'); + colorSchemeMainWrapper.className = 'commonFullScreenMainWrapper'; + + const inputAreaWrapper = document.createElement('div'); + + inputAreaWrapper.className = 'inputAreaWrapper'; + + const textColorPlatte = createAreaInput( + `New Area`, + (newValue: string) => + { + areaName = newValue; + } + ); + + const buttonSubmitWrapper = createButtonSubmit(); + + inputAreaWrapper.appendChild(textColorPlatte); + colorSchemeMainWrapper.appendChild(inputAreaWrapper); + colorSchemeMainWrapper.appendChild(buttonSubmitWrapper); + + addColorSchemeWrapper.appendChild(colorSchemeMainWrapper); + + + return addColorSchemeWrapper; + } + + const MainWrapper = document.createElement('div'); + MainWrapper.className = 'MainWrapper'; + MainWrapper.id = 'MainWrapper'; + + const Title = document.createElement('div'); + Title.className = 'Title'; + Title.innerText = 'Article Area'; + + const sendPost = new SendPost(); + const area = await sendPost.getAllBigArea(); + if (!area) return MainWrapper; + + const AreaWrapper = document.createElement('table'); + AreaWrapper.className = 'commonTableWrapper'; + + const areaSchemeTitleWrapper = displayAreaTitle(); + + AreaWrapper.appendChild(areaSchemeTitleWrapper); + + area.forEach(element => + { + const areaItem = createAreaItem(element); + AreaWrapper.appendChild(areaItem); + }); + + const addAreaBtn = createAddAreaBtn(); + const addArea = createAddArea(); + + MainWrapper.appendChild(Title); + MainWrapper.appendChild(AreaWrapper); + MainWrapper.appendChild(addAreaBtn); + MainWrapper.appendChild(addArea); + return MainWrapper; + } + + private async MainSubArea() + { + function displayAreaTitle() + { + const TitleWrapper = document.createElement('tr'); + + TitleWrapper.className = 'commonTitleWrapper'; + + const colorSchemeTitle = document.createElement('th'); + colorSchemeTitle.className = 'commonTitle'; + colorSchemeTitle.innerText = 'ID'; + + const name = document.createElement('th'); + name.className = 'commonTitle'; + name.innerText = 'Sub Area Name'; + + const UnserArea = document.createElement('th'); + UnserArea.className = 'commonTitle'; + UnserArea.innerText = 'Under Area'; + + const colorScheme = document.createElement('th'); + colorScheme.className = 'commonTitle'; + colorScheme.innerText = 'Color Scheme'; + + const actionTitle = document.createElement('th'); + actionTitle.className = 'commonTitle'; + actionTitle.innerText = 'Action'; + + TitleWrapper.appendChild(colorSchemeTitle); + TitleWrapper.appendChild(name); + TitleWrapper.appendChild(UnserArea); + TitleWrapper.appendChild(colorScheme); + TitleWrapper.appendChild(actionTitle); + + return TitleWrapper; + } + + function creaSubAreaItem(element: ArticleSubArea) + { + + function createIdWrapper() + { + const idWrapper = document.createElement('td'); + idWrapper.innerText = element.subAreaID.toString(); + return idWrapper; + } + + function createEditWrapper(onchange: (newValue: string) => void) + { + const editWrapper = document.createElement('td'); + const inputText = document.createElement('input'); + inputText.className = 'commonInput'; + inputText.value = element.subareaName; + editWrapper.appendChild(inputText); + + inputText.oninput = () => + { + onchange(inputText.value); + }; + + return editWrapper; + } + + function createSelectWrapper(onchange: (newValue: number) => void) + { + const selectWrapper = document.createElement('td'); + const inputText = document.createElement('input'); + + const dropMenuWrapper = document.createElement('div'); + + + dropMenuWrapper.className = 'commonDropMenuWrapper'; + + inputText.onclick = () => + { + console.log('click'); + dropMenuWrapper.classList.add('commonDropMenuWrapperActive'); + inputText.style.boxShadow = `0 -2px 0 0 #f50057 inset;`; + const fullScreenWrapper = document.createElement('div'); + + fullScreenWrapper.className = 'commonFullScreenTrackClickWrapper'; + + fullScreenWrapper.onclick = () => + { + fullScreenWrapper.remove(); + dropMenuWrapper.classList.remove('commonDropMenuWrapperActive'); + }; + + MainWrapper.appendChild(fullScreenWrapper); + }; + + area?.forEach(element => + { + const dropMenu = document.createElement('div'); + + dropMenu.className = 'commonDropMenu'; + + dropMenu.innerHTML = element.bigAreaName; + + dropMenu.onclick = () => + { + inputText.value = element.bigAreaName; + onchange(element.bigareaID); + dropMenuWrapper.classList.remove('commonDropMenuWrapperActive'); + const fullScreenWrapper = document.querySelector('.commonFullScreenTrackClickWrapper') as HTMLDivElement; + if (fullScreenWrapper) + { + fullScreenWrapper.remove(); + } + }; + + dropMenuWrapper.appendChild(dropMenu); + + }); + + + + + + + inputText.classList.add('commonInput'); + inputText.classList.add('selectInput'); + inputText.value = element.bigArea.bigAreaName; + inputText.readOnly = true; + + selectWrapper.appendChild(inputText); + selectWrapper.appendChild(dropMenuWrapper); + + return selectWrapper; + } + + function createColorSelect(onchange: (newValue: number) => void) + { + const selectWrapper = document.createElement('td'); + const input = document.createElement('div'); + input.classList.add('commonInput'); + input.classList.add('selectInput'); + + const colorGroupWrapper = document.createElement('div'); + + colorGroupWrapper.className = 'colorGroupWrapper'; + + const fontColor = document.createElement('div'); + + const backgroundColor = document.createElement('div'); + + fontColor.className = 'fontColor'; + + + backgroundColor.className = 'backgroundColor'; + + fontColor.style.backgroundColor = element.colorScheme.textColor; + + backgroundColor.style.backgroundColor = element.colorScheme.backgroundColor; + + + const dropMenuWrapper = document.createElement('div'); + dropMenuWrapper.className = 'commonDropMenuWrapper'; + + colorScheme?.forEach(element => + { + const dropMenu = document.createElement('div'); + + dropMenu.className = 'commonDropMenu'; + + const dmcolorGroupWrapper = document.createElement('div'); + + dmcolorGroupWrapper.className = 'colorGroupWrapper'; + + const dmfontColor = document.createElement('div'); + + const dmbackgroundColor = document.createElement('div'); + + dmfontColor.className = 'fontColor'; + + dmbackgroundColor.className = 'backgroundColor'; + + + dmfontColor.style.backgroundColor = element.textColor; + + dmbackgroundColor.style.backgroundColor = element.backgroundColor; + + dmcolorGroupWrapper.appendChild(dmfontColor); + + dmcolorGroupWrapper.appendChild(dmbackgroundColor); + + dropMenu.appendChild(dmcolorGroupWrapper); + + dropMenu.onclick = () => + { + fontColor.style.backgroundColor = element.textColor; + backgroundColor.style.backgroundColor = element.backgroundColor; + dropMenuWrapper.classList.remove('commonDropMenuWrapperActive'); + const fullScreenWrapper = document.querySelector('.commonFullScreenTrackClickWrapper') as HTMLDivElement; + if (fullScreenWrapper) + { + fullScreenWrapper.remove(); + } + onchange(element.id); + }; + + dropMenuWrapper.appendChild(dropMenu); + + }); + + input.onclick = () => + { + dropMenuWrapper.classList.add('commonDropMenuWrapperActive'); + input.style.boxShadow = `0 -2px 0 0 #f50057 inset;`; + const fullScreenWrapper = document.createElement('div'); + + fullScreenWrapper.className = 'commonFullScreenTrackClickWrapper'; + + fullScreenWrapper.onclick = () => + { + fullScreenWrapper.remove(); + dropMenuWrapper.classList.remove('commonDropMenuWrapperActive'); + }; + + MainWrapper.appendChild(fullScreenWrapper); + }; + + + colorGroupWrapper.appendChild(fontColor); + + colorGroupWrapper.appendChild(backgroundColor); + + input.appendChild(colorGroupWrapper); + + selectWrapper.appendChild(input); + selectWrapper.appendChild(dropMenuWrapper); + + + + + + + + return selectWrapper; + } + + function createBtn() + { + const actiontd = document.createElement('td'); + const actionWrapper = document.createElement('div'); + + actionWrapper.className = 'actionWrapper'; + + const saveBtnWrapper = document.createElement('div'); + saveBtnWrapper.className = 'commonBtnWrapper'; + saveBtnWrapper.onclick = async () => + { + console.log(element.subAreaID, subAreaName, bigAreaID, colorSchemeID); + const sendPost = new SendPost(); + const result = await sendPost.updateSubArea(element.subAreaID, subAreaName, bigAreaID, colorSchemeID); + if (result) + { + const navbar = document.getElementById('navigationBar') as HTMLDivElement; + + if (navbar) + { + navbar.remove(); + const navRelated = new NavRelated(); + await navRelated.init(); + } + } + }; + + const saveBtn = document.createElement('div'); + saveBtn.className = 'commonBtn'; + saveBtn.innerText = '💾'; + + const deleteBtnWrapper = document.createElement('div'); + deleteBtnWrapper.className = 'commonBtnWrapper'; + deleteBtnWrapper.onclick = async () => + { + const sendPost = new SendPost(); + const result = await sendPost.deleteSubArea(element.subAreaID); + if (result) + { + AreaItemWrapper.remove(); + const navbar = document.getElementById('navigationBar') as HTMLDivElement; + + if (navbar) + { + navbar.remove(); + const navRelated = new NavRelated(); + await navRelated.init(); + } + } + }; + + const deleteBtn = document.createElement('div'); + + deleteBtn.className = 'commonBtn'; + deleteBtn.innerText = '🗑️'; + + saveBtnWrapper.appendChild(saveBtn); + deleteBtnWrapper.appendChild(deleteBtn); + actionWrapper.appendChild(saveBtnWrapper); + actionWrapper.appendChild(deleteBtnWrapper); + actiontd.appendChild(actionWrapper); + return actiontd; + } + + const AreaItemWrapper = document.createElement('tr'); + AreaItemWrapper.className = 'commonItemWrapper'; + + let subAreaName = element.subareaName; + let bigAreaID = element.bigArea.bigareaID; + let colorSchemeID = element.colorScheme.id; + + const IdWrapper = createIdWrapper(); + + const editWrapper = createEditWrapper((newValue: string) => + { + subAreaName = newValue; + }); + + const selectWrapper = createSelectWrapper((newValue: number) => + { + bigAreaID = newValue; + }); + + const colorSelect = createColorSelect( + (newValue: number) => + { + colorSchemeID = newValue; + } + ); + + const actionWrapper = createBtn(); + + AreaItemWrapper.appendChild(IdWrapper); + + AreaItemWrapper.appendChild(editWrapper); + + AreaItemWrapper.appendChild(selectWrapper); + + AreaItemWrapper.appendChild(colorSelect); + + AreaItemWrapper.appendChild(actionWrapper); + + return AreaItemWrapper; + } + + function createAddSubAreaBtn() + { + const buttonSubmitWrapper = document.createElement('div'); + buttonSubmitWrapper.className = 'commonButtonSubmitWrapper'; + + buttonSubmitWrapper.style.justifyContent = 'flex-start'; + + buttonSubmitWrapper.style.paddingLeft = '20%'; + + const buttonAddColorScheme = document.createElement('div'); + buttonAddColorScheme.className = 'commonButtonSecondSubmit'; + buttonAddColorScheme.innerHTML = 'Add Sub Area'; + + buttonAddColorScheme.onclick = async () => + { + const addColorScheme = document.querySelector('.commonFullScreenWrapper') as HTMLDivElement; + if (addColorScheme) + { + addColorScheme.classList.add('commonFullScreenMainWrapperActive'); + } + + }; + + + + buttonSubmitWrapper.appendChild(buttonAddColorScheme); + + return buttonSubmitWrapper; + } + + function createAddSubArea() + { + + function createSubAreaInput(title: string, onChange: (newValue: string) => void) + { + const commonItemWrapper = document.createElement('div'); + + commonItemWrapper.className = 'commonItemWrapper'; + + commonItemWrapper.style.width = `33%`; + + const commonTitle = document.createElement('div'); + + commonTitle.className = 'commonTitle'; + commonTitle.innerText = title; + + const areaInput = document.createElement('input'); + areaInput.className = 'commonInput'; + areaInput.type = 'text'; + + + areaInput.oninput = () => + { + onChange(areaInput.value); + }; + + commonItemWrapper.appendChild(commonTitle); + commonItemWrapper.appendChild(areaInput); + return commonItemWrapper; + } + + function createBigAreaInput(title: string, bigarea: string, onChange: (newValue: number) => void) + { + const selectWrapper = document.createElement('div'); + selectWrapper.className = 'commonItemWrapper'; + selectWrapper.style.width = `33%`; + + const commonTitle = document.createElement('div'); + + commonTitle.className = 'commonTitle'; + commonTitle.innerText = title; + + const inputText = document.createElement('input'); + + inputText.classList.add('commonInput'); + inputText.classList.add('selectInput'); + + inputText.value = bigarea; + inputText.placeholder = 'Select Area'; + + inputText.readOnly = true; + + const dropMenuWrapper = document.createElement('div'); + + dropMenuWrapper.className = 'commonDropMenuWrapper'; + + inputText.onclick = () => + { + dropMenuWrapper.classList.add('commonDropMenuWrapperActive'); + inputText.style.boxShadow = `0 -2px 0 0 #f50057 inset;`; + const fullScreenWrapper = document.createElement('div'); + + fullScreenWrapper.className = 'commonFullScreenTrackClickWrapper'; + + fullScreenWrapper.onclick = () => + { + fullScreenWrapper.remove(); + dropMenuWrapper.classList.remove('commonDropMenuWrapperActive'); + }; + + addSubAreaWrapper.appendChild(fullScreenWrapper); + }; + + area?.forEach(element => + { + const dropMenu = document.createElement('div'); + + dropMenu.className = 'commonDropMenu'; + + dropMenu.innerHTML = element.bigAreaName; + + dropMenu.onclick = () => + { + inputText.value = element.bigAreaName; + onChange(element.bigareaID); + dropMenuWrapper.classList.remove('commonDropMenuWrapperActive'); + const fullScreenWrapper = document.querySelector('.commonFullScreenTrackClickWrapper') as HTMLDivElement; + if (fullScreenWrapper) + { + fullScreenWrapper.remove(); + } + }; + + dropMenuWrapper.appendChild(dropMenu); + }); + + selectWrapper.appendChild(commonTitle); + selectWrapper.appendChild(inputText); + selectWrapper.appendChild(dropMenuWrapper); + return selectWrapper; + + } + + function createColorSchemeInput(title: string, onChange: (newValue: number) => void) + { + const selectWrapper = document.createElement('div'); + selectWrapper.className = 'commonItemWrapper'; + selectWrapper.style.width = `33%`; + + const commonTitle = document.createElement('div'); + + commonTitle.className = 'commonTitle'; + commonTitle.innerText = title; + + const input = document.createElement('div'); + input.classList.add('commonInput'); + input.classList.add('selectInput'); + + const colorGroupWrapper = document.createElement('div'); + + colorGroupWrapper.className = 'colorGroupWrapper'; + + const fontColor = document.createElement('div'); + + const backgroundColor = document.createElement('div'); + + fontColor.className = 'fontColor'; + + backgroundColor.className = 'backgroundColor'; + + if (colorScheme) + { + fontColor.style.backgroundColor = colorScheme[0].textColor; + backgroundColor.style.backgroundColor = colorScheme[0].backgroundColor; + } + + + const dropMenuWrapper = document.createElement('div'); + dropMenuWrapper.className = 'commonDropMenuWrapper'; + + colorScheme?.forEach(element => + { + const dropMenu = document.createElement('div'); + + dropMenu.className = 'commonDropMenu'; + + const dmcolorGroupWrapper = document.createElement('div'); + + dmcolorGroupWrapper.className = 'colorGroupWrapper'; + + const dmfontColor = document.createElement('div'); + + const dmbackgroundColor = document.createElement('div'); + + dmfontColor.className = 'fontColor'; + + dmbackgroundColor.className = 'backgroundColor'; + + + dmfontColor.style.backgroundColor = element.textColor; + + dmbackgroundColor.style.backgroundColor = element.backgroundColor; + + dmcolorGroupWrapper.appendChild(dmfontColor); + + dmcolorGroupWrapper.appendChild(dmbackgroundColor); + + dropMenu.appendChild(dmcolorGroupWrapper); + + dropMenu.onclick = () => + { + fontColor.style.backgroundColor = element.textColor; + backgroundColor.style.backgroundColor = element.backgroundColor; + dropMenuWrapper.classList.remove('commonDropMenuWrapperActive'); + const fullScreenWrapper = document.querySelector('.commonFullScreenTrackClickWrapper') as HTMLDivElement; + if (fullScreenWrapper) + { + fullScreenWrapper.remove(); + } + onChange(element.id); + }; + + dropMenuWrapper.appendChild(dropMenu); + + }); + + input.onclick = () => + { + dropMenuWrapper.classList.add('commonDropMenuWrapperActive'); + input.style.boxShadow = `0 -2px 0 0 #f50057 inset;`; + const fullScreenWrapper = document.createElement('div'); + + fullScreenWrapper.className = 'commonFullScreenTrackClickWrapper'; + + fullScreenWrapper.onclick = () => + { + fullScreenWrapper.remove(); + dropMenuWrapper.classList.remove('commonDropMenuWrapperActive'); + }; + + addSubAreaWrapper.appendChild(fullScreenWrapper); + }; + + colorGroupWrapper.appendChild(fontColor); + colorGroupWrapper.appendChild(backgroundColor); + input.appendChild(colorGroupWrapper); + selectWrapper.appendChild(commonTitle); + selectWrapper.appendChild(input); + selectWrapper.appendChild(dropMenuWrapper); + return selectWrapper; + + } + + function createButtonSubmit() + { + const buttonSubmitWrapper = document.createElement('div'); + buttonSubmitWrapper.className = 'commonButtonSubmitWrapper'; + + const buttonSubmit = document.createElement('div'); + buttonSubmit.className = 'commomButtonSubmit'; + buttonSubmit.innerHTML = 'Add'; + + const buttonCancel = document.createElement('div'); + buttonCancel.className = 'commonButtonSecondSubmit'; + buttonCancel.innerHTML = 'Cancel'; + + buttonCancel.onclick = async () => + { + addSubAreaWrapper.classList.remove('commonFullScreenMainWrapperActive'); + }; + + buttonSubmit.onclick = async () => + { + const sendPost = new SendPost(); + const result = await sendPost.addSubArea(subAreaName, bigAreaID, colorSchemeID); + if (result) + { + const foundColorScheme = colorScheme?.find(cs => cs.id === colorSchemeID); + const foundArea = area?.find(a => a.bigareaID === bigAreaID); + + const newItem = creaSubAreaItem({ + subAreaID: result.id, + subareaName: subAreaName, + bigArea: { + bigareaID: bigAreaID, + bigAreaName: foundArea ? foundArea.bigAreaName : '' + }, + colorScheme: { + id: colorSchemeID, + textColor: foundColorScheme ? foundColorScheme.textColor : '', + backgroundColor: foundColorScheme ? foundColorScheme.backgroundColor : '' + } + }); + AreaWrapper.appendChild(newItem); + addSubAreaWrapper.classList.remove('commonFullScreenMainWrapperActive'); + const navbar = document.getElementById('navigationBar') as HTMLDivElement; + + if (navbar) + { + navbar.remove(); + const navRelated = new NavRelated(); + await navRelated.init(); + } + } + + + }; + + buttonSubmitWrapper.appendChild(buttonCancel); + buttonSubmitWrapper.appendChild(buttonSubmit); + + return buttonSubmitWrapper; + } + + let subAreaName = ''; + let bigAreaID = area ? area[0].bigareaID : 1; + let colorSchemeID = colorScheme ? colorScheme[0].id : 1; + const addSubAreaWrapper = document.createElement('div'); + addSubAreaWrapper.className = 'commonFullScreenWrapper'; + + const colorSchemeMainWrapper = document.createElement('div'); + colorSchemeMainWrapper.className = 'commonFullScreenMainWrapper'; + + const inputAreaWrapper = document.createElement('div'); + + inputAreaWrapper.className = 'inputAreaWrapper'; + + const NewSubArea = createSubAreaInput( + `New Sub Area`, + (newValue: string) => + { + subAreaName = newValue; + } + ); + + const BigAreaInput = createBigAreaInput( + `Under Area`, + area ? area[0].bigAreaName : '', + (newValue: number) => + { + bigAreaID = newValue; + } + ); + + const colorSchemeInput = createColorSchemeInput( + `Color Scheme`, + (newValue: number) => + { + colorSchemeID = newValue; + } + ); + + const buttonSubmitWrapper = createButtonSubmit(); + + inputAreaWrapper.appendChild(NewSubArea); + inputAreaWrapper.appendChild(BigAreaInput); + inputAreaWrapper.appendChild(colorSchemeInput); + colorSchemeMainWrapper.appendChild(inputAreaWrapper); + colorSchemeMainWrapper.appendChild(buttonSubmitWrapper); + + addSubAreaWrapper.appendChild(colorSchemeMainWrapper); + + + return addSubAreaWrapper; + } + + const MainWrapper = document.createElement('div'); + MainWrapper.className = 'MainWrapper'; + MainWrapper.id = 'MainWrapper'; + + + const sendPost = new SendPost(); + const area = await sendPost.getAllArticleArea(); + const colorScheme = await sendPost.getColorScheme(); + + if (!colorScheme) return MainWrapper; + + if (!area) return MainWrapper; + + const newArea: ArticleSubArea[] = []; + + area.forEach(articleArea => + { + articleArea.subarea.forEach(subarea => + { + if (subarea.subAreaID && subarea.subareaName && subarea.colorscheme) + { + newArea.push({ + subAreaID: subarea.subAreaID, + subareaName: subarea.subareaName, + bigArea: { + bigareaID: articleArea.bigareaID, + bigAreaName: articleArea.bigAreaName + }, + colorScheme: { + id: subarea.colorscheme.id, + textColor: subarea.colorscheme.textColor, + backgroundColor: subarea.colorscheme.backgroundColor + } + }); + } + }); + }); + + newArea.sort((a, b) => a.subAreaID - b.subAreaID); + + const Title = document.createElement('div'); + Title.className = 'Title'; + Title.innerText = 'Article Sub Area'; + + + const AreaWrapper = document.createElement('table'); + AreaWrapper.className = 'commonTableWrapper'; + AreaWrapper.style.padding = '24px 10%'; + + const areaTitleWrapper = displayAreaTitle(); + + AreaWrapper.appendChild(areaTitleWrapper); + + + newArea.forEach(element => + { + const areaItem = creaSubAreaItem(element); + AreaWrapper.appendChild(areaItem); + + }); + + const addAreaBtn = createAddSubAreaBtn(); + + const addArea = createAddSubArea(); + + MainWrapper.appendChild(Title); + MainWrapper.appendChild(AreaWrapper); + MainWrapper.appendChild(addAreaBtn); + MainWrapper.appendChild(addArea); + return MainWrapper; + } + + CreateSwitchOption(Title: string, description: string, option: number, onChange: (newValue: number) => void) + { + const itemWrapper = document.createElement('div'); + itemWrapper.className = 'mainItemWrapper'; + + const contorlWrapper = document.createElement('div'); + contorlWrapper.className = 'mainItemControlWrapper'; + + const switchWrapper = document.createElement('div'); + switchWrapper.className = 'switchWrapper'; + + const switchRoot = document.createElement('div'); + + switchRoot.className = 'switchRoot'; + + const switchTitle = document.createElement('div'); + + switchTitle.className = 'switchTitle'; + + switchTitle.innerText = Title; + + const switchTrack = document.createElement('div'); + switchTrack.className = 'switchTrack'; + + const switchButtonWrapper = document.createElement('div'); + switchButtonWrapper.className = 'switchButtonWrapper'; + + const switchButton = document.createElement('div'); + switchButton.className = 'switchButton'; + + const descriptionDiv = document.createElement('div'); + descriptionDiv.className = 'mainItemDescription'; + descriptionDiv.innerText = description; + + switchWrapper.onclick = () => + { + option = option === 0 ? 1 : 0; + this.ChangeToSwitchRootActive(option, switchButtonWrapper, switchTrack); + onChange(option); + }; + + this.ChangeToSwitchRootActive(option, switchButtonWrapper, switchTrack); + + switchButtonWrapper.appendChild(switchButton); + + switchRoot.appendChild(switchButtonWrapper); + switchRoot.appendChild(switchTrack); + + switchWrapper.appendChild(switchRoot); + switchWrapper.appendChild(switchTitle); + + contorlWrapper.appendChild(switchWrapper); + + itemWrapper.appendChild(contorlWrapper); + itemWrapper.appendChild(descriptionDiv); + + return itemWrapper; + } + + ChangeToSwitchRootActive(option: number, switchButtonWrapper: HTMLDivElement, switchTrack: HTMLDivElement) + { + switchButtonWrapper.classList.toggle('switchButtonWrapperActive', option === 1); + switchTrack.classList.toggle('switchTrackActive', option === 1); + } + + CreateInputTextOption(title: string, description: string, inputContent: string | number | null, onChange: (newValue: string) => void) + { + const itemWrapper = document.createElement('div'); + itemWrapper.className = 'mainItemWrapper'; + + const inputTitle = document.createElement('div'); + + inputTitle.className = 'mainItemTitle'; + + inputTitle.innerText = title; + + const contorlWrapper = document.createElement('div'); + contorlWrapper.className = 'mainItemControlWrapper'; + + const inputWrapper = document.createElement('div'); + inputWrapper.className = 'inputWrapper'; + + const input = document.createElement('input'); + input.className = 'input'; if (typeof inputContent === 'number') { input.type = 'number'; input.min = '0'; - input.max = '65535' - if(inputContent !== 0){ + input.max = '65535'; + if (inputContent !== 0) + { input.value = inputContent.toString(); } } else if (typeof inputContent === 'string') @@ -661,7 +2183,7 @@ export class AdminPage return itemWrapper; } - CreateInputPasswordOption(title: string, description: string, inputContent: string|null , onChange: (newValue: string) => void) + CreateInputPasswordOption(title: string, description: string, inputContent: string | null, onChange: (newValue: string) => void) { const itemWrapper = document.createElement('div'); itemWrapper.className = 'mainItemWrapper'; diff --git a/src/ts/Admin Page/interface.ts b/src/ts/Admin Page/interface.ts new file mode 100644 index 0000000..2072848 --- /dev/null +++ b/src/ts/Admin Page/interface.ts @@ -0,0 +1,13 @@ +import { ColorScheme } from "../Send Fetch/interface"; + +interface BigArea { + bigareaID: number; + bigAreaName: string; +} + +export interface ArticleSubArea { + subAreaID: number; + subareaName: string; + bigArea: BigArea; + colorScheme: ColorScheme; +} \ No newline at end of file diff --git a/src/ts/Article Page/index.ts b/src/ts/Article Page/index.ts index b8c6530..1d2ec6d 100644 --- a/src/ts/Article Page/index.ts +++ b/src/ts/Article Page/index.ts @@ -7,7 +7,7 @@ export class MakeArticlePage { handlePopMsg = new HandlePopMsg(); - init() + async init() { const hash = window.location.hash; if (hash.startsWith('#/p')) @@ -15,7 +15,7 @@ export class MakeArticlePage const id = hash.slice(hash.indexOf('?id=') + 4); if (id && Number.isInteger(parseInt(id))) { - this.makeArticlePage(parseInt(id)); + await this.makeArticlePage(parseInt(id)); } else { @@ -74,9 +74,9 @@ export class MakeArticlePage const articleAuthor = document.createElement('div'); articleAuthor.className = 'articleAuthor'; articleAuthor.innerText = `Author: ${data.author.articleAuthor}`; - articleAuthor.onclick = ()=>{ + articleAuthor.onclick = async ()=>{ const changePage = new ChangePage(true); - changePage.toUserProfile(data.author.articleAuthorID.toString()); + await changePage.toUserProfile(data.author.articleAuthorID.toString()); } return articleAuthor; } @@ -87,9 +87,9 @@ export class MakeArticlePage articleArea.innerText = `Area: ${data.article.articleArea}`; articleArea.style.color = data.colorScheme.areaTextColor; articleArea.style.backgroundColor = data.colorScheme.areaBackgroundColor; - articleArea.onclick = ()=>{ + articleArea.onclick = async ()=>{ const changePage = new ChangePage(true); - changePage.toArea(data.article.articleArea); + await changePage.toArea(data.article.articleArea); } return articleArea; } diff --git a/src/ts/Main Page/articleByArea.ts b/src/ts/Main Page/articleByArea.ts index ce8d774..99dbee5 100644 --- a/src/ts/Main Page/articleByArea.ts +++ b/src/ts/Main Page/articleByArea.ts @@ -4,9 +4,9 @@ import { MakeArticleCard } from "./makeArticleCard"; export class AreaPage { - init = (area: string) => + init = async (area: string) => { - this.createContentDiv(area); + await this.createContentDiv(area); }; private createContentDiv = async (area: string) => diff --git a/src/ts/Main Page/articleBySearch.ts b/src/ts/Main Page/articleBySearch.ts index 86b713e..658663e 100644 --- a/src/ts/Main Page/articleBySearch.ts +++ b/src/ts/Main Page/articleBySearch.ts @@ -4,9 +4,9 @@ import { MakeArticleCard } from "./makeArticleCard"; export class SearchResultPage{ - init = (keyword: string) => + init = async (keyword: string) => { - this.createContentDiv(keyword); + await this.createContentDiv(keyword); }; private createContentDiv = async (keyword: string) => diff --git a/src/ts/Main Page/index.ts b/src/ts/Main Page/index.ts index 908b0d3..46a34f5 100644 --- a/src/ts/Main Page/index.ts +++ b/src/ts/Main Page/index.ts @@ -4,9 +4,9 @@ import '../../scss/MainPage/index.scss'; import { MakeArticleCard } from './makeArticleCard'; export class MainPage { - init = () => + init = async () => { - this.createContentDiv(); + await this.createContentDiv(); }; private createContentDiv = async () => diff --git a/src/ts/Main Page/makeArticleCard.ts b/src/ts/Main Page/makeArticleCard.ts index d9915fa..63a12f3 100644 --- a/src/ts/Main Page/makeArticleCard.ts +++ b/src/ts/Main Page/makeArticleCard.ts @@ -16,10 +16,10 @@ export class MakeArticleCard const avatar = document.createElement('img'); avatar.className = 'avatar'; avatar.src = `${urlconfig.avatarUrl}${ArticleCardData.author.articleAuthorAvatar}`; - avatar.onclick = () => + avatar.onclick = async () => { const changePage = new ChangePage(true); - changePage.toUserProfile(ArticleCardData.author.articleAuthorID.toString()); + await changePage.toUserProfile(ArticleCardData.author.articleAuthorID.toString()); }; avatarWrapper.appendChild(avatar); return avatarWrapper; @@ -32,10 +32,10 @@ export class MakeArticleCard const authorName = document.createElement('p'); authorName.className = 'authorName'; authorName.innerHTML = ArticleCardData.author.articleAuthor; - authorName.onclick = () => + authorName.onclick = async () => { const changePage = new ChangePage(true); - changePage.toUserProfile(ArticleCardData.author.articleAuthorID.toString()); + await changePage.toUserProfile(ArticleCardData.author.articleAuthorID.toString()); }; authorNameWrapper.appendChild(authorName); return authorNameWrapper; @@ -78,10 +78,10 @@ export class MakeArticleCard const ArticleTitle = document.createElement('h4'); ArticleTitle.className = 'ArticleTitle'; - ArticleTitle.onclick = () => + ArticleTitle.onclick = async () => { const changePage = new ChangePage(true); - changePage.toArticle(ArticleCardData.article.articleID.toString()); + await changePage.toArticle(ArticleCardData.article.articleID.toString()); }; ArticleTitle.innerHTML = ArticleCardData.article.articleTitle; @@ -119,10 +119,10 @@ export class MakeArticleCard ArticleArea.innerHTML = ArticleCardData.article.articleArea; ArticleArea.style.color = ArticleCardData.colorScheme.areaTextColor; ArticleArea.style.backgroundColor = ArticleCardData.colorScheme.areaBackgroundColor; - ArticleArea.onclick = () => + ArticleArea.onclick = async () => { const changePage = new ChangePage(true); - changePage.toArea(ArticleCardData.article.articleArea); + await changePage.toArea(ArticleCardData.article.articleArea); }; const ArticleLastEditTime = document.createElement('p'); diff --git a/src/ts/Manage Article Page/index.ts b/src/ts/Manage Article Page/index.ts index 74dac4b..559cd7b 100644 --- a/src/ts/Manage Article Page/index.ts +++ b/src/ts/Manage Article Page/index.ts @@ -43,7 +43,7 @@ export class ManageArticle { localStorage.clear(); const changePage = new ChangePage(true); - changePage.toIndex(); + await changePage.toIndex(); location.reload(); } }; @@ -70,7 +70,7 @@ export class ManageArticle { this.navigationProgress.end(); const changePage = new ChangePage(true); - changePage.toIndex(); + await changePage.toIndex(); return; } } diff --git a/src/ts/Navigation Bar/changePage.ts b/src/ts/Navigation Bar/changePage.ts index cc71c36..736413a 100644 --- a/src/ts/Navigation Bar/changePage.ts +++ b/src/ts/Navigation Bar/changePage.ts @@ -18,40 +18,42 @@ export class ChangePage { navRelated.init(); } if(DeletePrevious){ - const contentDiv = document.getElementById('contentDiv'); - if (contentDiv) - { - contentDiv.remove(); - } + const contentDivs = document.querySelectorAll('.contentDiv'); + contentDivs.forEach((contentDiv) => { + if(contentDiv.parentNode) + contentDiv.parentNode.removeChild(contentDiv); + }); } + + } - toUserProfile (id:string) { + async toUserProfile (id:string) { document.title = 'User'; const makeUserProfile = new MakeUserProfile(); window.location.href = urlconfig.url + '/#/u?id=' + id; - makeUserProfile.Init(); + await makeUserProfile.Init(); } - toPostArticle () { + async toPostArticle () { document.title = 'Post Article'; window.location.href = urlconfig.url + '/#/newpost'; const createNewPost = new UploadArticle(); - createNewPost.init(); + await createNewPost.init(); } - toIndex () { + async toIndex () { document.title = 'Blog'; window.location.href = urlconfig.url + '/#'; const mainPage = new MainPage(); - mainPage.init(); + await mainPage.init(); } - toArticle (id:string) { + async toArticle (id:string) { document.title = 'Article'; window.location.href = urlconfig.url + '/#/p?id=' + id; const makeArticlePage = new MakeArticlePage(); - makeArticlePage.init(); + await makeArticlePage.init(); } to404Page(){ @@ -63,11 +65,11 @@ export class ChangePage { page404.init(); } - toManageArticle(){ + async toManageArticle(){ document.title = 'Manage Article'; window.location.href = urlconfig.url + '/#/manage article'; const manageArticle = new ManageArticle(); - manageArticle.init(); + await manageArticle.init(); } async toEditArticle(id:number){ @@ -77,31 +79,31 @@ export class ChangePage { await updateArticle.init(id); } - toArea(area:string){ + async toArea(area:string){ document.title = area; window.location.href = urlconfig.url + '/#/area?area=' + area; const areaPage = new AreaPage(); - areaPage.init(area); + await areaPage.init(area); } - toSearch(value:string){ + async toSearch(value:string){ document.title = 'Search'; window.location.href = urlconfig.url + '/#/search?keyword=' + value; const searchResultPage = new SearchResultPage(); - searchResultPage.init(value); + await searchResultPage.init(value); } - toAdminPage(){ + async toAdminPage(){ document.title = 'Admin Page'; window.location.href = urlconfig.url + '/#/admin'; const adminPage = new AdminPage() - adminPage.init(); + await adminPage.init(); } - toActivateAccountPage(){ + async toActivateAccountPage(){ document.title = 'Activate Account'; const activateAccountPage = new ActivateAccountPage(); - activateAccountPage.init(); + await activateAccountPage.init(); } } \ No newline at end of file diff --git a/src/ts/Navigation Bar/index.ts b/src/ts/Navigation Bar/index.ts index 3359840..d0b47f2 100644 --- a/src/ts/Navigation Bar/index.ts +++ b/src/ts/Navigation Bar/index.ts @@ -62,7 +62,7 @@ export class NavRelated { localStorage.clear(); const changePage = new ChangePage(true); - changePage.toIndex(); + await changePage.toIndex(); return await this.createRightBanner(); } } else @@ -113,21 +113,21 @@ export class NavRelated searchBar.placeholder = 'Search'; searchBar.type = 'text'; searchBar.autocomplete = 'off'; - searchBar.onkeydown = function(e){ + searchBar.onkeydown = async function(e){ if(e.key === 'Enter'){ const changePage = new ChangePage(true); const value = searchBar.value; - changePage.toSearch(value); + await changePage.toSearch(value); } } const btnSearch = document.createElement('div') btnSearch.className = 'btnSearch'; btnSearch.innerText = 'Search'; - btnSearch.onclick = function(){ + btnSearch.onclick = async function(){ const changePage = new ChangePage(true); const value = searchBar.value; - changePage.toSearch(value); + await changePage.toSearch(value); } searchBarWrapper.appendChild(searchBar); searchBarComponentWrapper.appendChild(searchBarWrapper); @@ -149,10 +149,10 @@ export class NavRelated userMenuAvatar.className = 'userMenuAvatar'; userMenuAvatar.src = `${urlconfig.avatarUrl}${userData.userData.avatar}`; - userMenuAvatar.onclick = () => + userMenuAvatar.onclick = async () => { const changePage = new ChangePage(true); - changePage.toUserProfile(userData.userData.id.toString()); + await changePage.toUserProfile(userData.userData.id.toString()); }; userMenuAvatarWrapper.appendChild(userMenuAvatar); @@ -186,18 +186,18 @@ export class NavRelated const menuItem = [ { name: 'Post Article', - func: function () + func: async function () { const changePage = new ChangePage(true); - changePage.toPostArticle(); + await changePage.toPostArticle(); } }, { name: 'Manage Article', - func: function () + func: async function () { const changePage = new ChangePage(true); - changePage.toManageArticle(); + await changePage.toManageArticle(); } }, { @@ -218,10 +218,10 @@ export class NavRelated const adminPage = [ { name: 'Admin Page', - func: function () + func: async function () { const changePage = new ChangePage(true); - changePage.toAdminPage(); + await changePage.toAdminPage(); } } ]; @@ -234,12 +234,12 @@ export class NavRelated const userMenuWrapper = document.createElement('div'); userMenuWrapper.className = 'userMenuWrapper'; - menuItem.forEach((item) => + menuItem.forEach(async (item) => { const userMenu = document.createElement('div'); userMenu.className = `${item.name}Wrapper`; userMenu.innerText = item.name; - userMenu.onclick = item.func; // 绑定函数到onclick事件 + userMenu.onclick = await item.func; // 绑定函数到onclick事件 userMenuWrapper.appendChild(userMenu); }); @@ -352,10 +352,10 @@ export class NavRelated logo.className = 'logo'; const logoLink = document.createElement('a'); logoLink.rel = 'home'; - logoLink.onclick = () => + logoLink.onclick = async () => { const changePage = new ChangePage(true); - changePage.toIndex(); + await changePage.toIndex(); }; const siteTitle = document.createElement('p'); @@ -378,16 +378,16 @@ export class NavRelated const AllArticleArea = await sendPost.getAllArticleArea(); const areaItemWrapper = document.createElement('div'); areaItemWrapper.className = 'areaItemWrapper'; + if(!AllArticleArea) return areaItemWrapper; AllArticleArea.forEach((item) =>{ const areaItem = document.createElement('div'); areaItem.className = 'areaItem'; const areaLink = document.createElement('div'); - areaLink.textContent = item.AreaName; + areaLink.textContent = item.bigAreaName; areaLink.className = 'menu-item'; areaItem.appendChild(areaLink); - - if(item.subArea[0]){ + if(item.subarea[0].subAreaID){ const DropMenuWrapper = document.createElement('div'); const DropMenu = document.createElement('div'); DropMenuWrapper.className = 'dropMenuWrapper'; @@ -414,17 +414,19 @@ export class NavRelated } }; - item.subArea.forEach((subItem) =>{ - if(subItem === null) return; + item.subarea.forEach((subItem) =>{ + if(subItem.subareaName === null) return; + const subAreaLink = document.createElement('div'); - subAreaLink.textContent = subItem; + subAreaLink.textContent = subItem.subareaName; subAreaLink.className = 'subAreaLink'; - subAreaLink.onclick = () =>{ + subAreaLink.onclick = async () =>{ + if(subItem.subareaName === null) return; const changePage = new ChangePage(true); - changePage.toArea(subItem); + await changePage.toArea(subItem.subareaName); } DropMenu.appendChild(subAreaLink); diff --git a/src/ts/Send Fetch/index.ts b/src/ts/Send Fetch/index.ts index 0fabd91..d0cefec 100644 --- a/src/ts/Send Fetch/index.ts +++ b/src/ts/Send Fetch/index.ts @@ -4,7 +4,7 @@ import { ChangePage } from "../Navigation Bar/changePage"; import { UserData as UD, ArticleData as AD } from "../Navigation Bar/interface"; import { HandlePopMsg } from "../Navigation Bar/popMsg"; import { ProfileData } from "../User Profile/interface"; -import { ArticleArea, ArticleCardData, ArticleContent, RetArticleData, setting_email, setting_loginandregister } from "./interface"; +import { ArticleArea, ArticleCardData, ArticleContent, ColorScheme, RetArticleData, setting_email, setting_loginandregister } from "./interface"; import { urlconfig } from "../Url Config/config"; /** @@ -40,7 +40,7 @@ export class SendPost .catch(error => { console.error('Error:', error); - throw new Error('Backend Not Running'); + this.handlePopMsg.popMsg(error); }); } @@ -170,7 +170,7 @@ export class SendPost ArticleID: articleID }; await this.postWithUrlParams('articlePermission', params) - .then((response) => + .then(async (response) => { if (response.code === 0) { @@ -179,15 +179,15 @@ export class SendPost this.handlePopMsg.popMsg(response.message); localStorage.clear(); const changePage = new ChangePage(true); - changePage.toIndex(); + await changePage.toIndex(); location.reload(); } }) - .catch((error) => + .catch(async (error) => { localStorage.clear(); const changePage = new ChangePage(true); - changePage.toIndex(); + await changePage.toIndex(); location.reload(); }); } else @@ -195,13 +195,13 @@ export class SendPost this.handlePopMsg.popMsg('You has no permission to edit this article'); localStorage.clear(); const changePage = new ChangePage(true); - changePage.toIndex(); + await changePage.toIndex(); } } - UploadArticle(title: string, article: string, area: string, tag: string) + async UploadArticle(title: string, article: string, area: string, tag: string) { this.navigationProgress.start(); const UserData = localStorage.getItem('UserData'); @@ -220,13 +220,13 @@ export class SendPost ArticleData: ArticleData }; this.postWithUrlParams('uploadArticle', params) - .then((response) => + .then(async (response) => { if (response.code === 0) { this.handlePopMsg.popMsg(response.message); const changePage = new ChangePage(true); - changePage.toIndex(); + await changePage.toIndex(); } else { this.handlePopMsg.popMsg(response.message); @@ -244,12 +244,12 @@ export class SendPost { localStorage.clear(); const changePage = new ChangePage(true); - changePage.toIndex(); + await changePage.toIndex(); location.reload(); } } - UpdateArticle(id: number, title: string, article: string, area: string, tag: string) + async UpdateArticle(id: number, title: string, article: string, area: string, tag: string) { this.navigationProgress.start(); const UserData = localStorage.getItem('UserData'); @@ -290,7 +290,7 @@ export class SendPost { localStorage.clear(); const changePage = new ChangePage(true); - changePage.toIndex(); + await changePage.toIndex(); location.reload(); } } @@ -324,7 +324,7 @@ export class SendPost const params = { area: area }; - return await this.postWithUrlParams('getArticleCardData', params).then((response) => + return await this.postWithUrlParams('getArticleCardData', params).then(async (response) => { if (response.code === 0) { @@ -333,7 +333,7 @@ export class SendPost { this.handlePopMsg.popMsg(response.message); const changePage = new ChangePage(true); - changePage.to404Page(); + await changePage.to404Page(); return []; } @@ -412,7 +412,7 @@ export class SendPost const params = { articleId: id }; - return await this.postWithUrlParams('getArticleContent', params).then((response) => + return await this.postWithUrlParams('getArticleContent', params).then(async (response) => { if (response.code === 0) { @@ -421,13 +421,13 @@ export class SendPost { this.handlePopMsg.popMsg(response.message); const changePage = new ChangePage(true); - changePage.to404Page(); + await changePage.to404Page(); return null; } - }).catch((error: any) => + }).catch(async (error: any) => { const changePage = new ChangePage(true); - changePage.to404Page(); + await changePage.to404Page(); return null; }) .finally(() => @@ -480,11 +480,11 @@ export class SendPost return null; } }) - .catch((error) => + .catch(async (error) => { this.handlePopMsg.popMsg((error as Error).message); const changePage = new ChangePage(true); - changePage.toIndex(); + await changePage.toIndex(); }) .finally(() => { @@ -504,25 +504,25 @@ export class SendPost }; this.postWithUrlParams('deleteArticle', params) - .then((response) => + .then(async (response) => { if (response.code === 0) { this.handlePopMsg.popMsg(response.message); const changePage = new ChangePage(true); - changePage.toManageArticle(); + await changePage.toManageArticle(); } else { this.handlePopMsg.popMsg(response.message); const changePage = new ChangePage(true); - changePage.toIndex(); + await changePage.toIndex(); } }) - .catch((error) => + .catch(async (error) => { this.handlePopMsg.popMsg((error as Error).message); const changePage = new ChangePage(true); - changePage.toIndex(); + await changePage.toIndex(); }) .finally(() => { @@ -533,7 +533,7 @@ export class SendPost this.handlePopMsg.popMsg('You has no permission to delete this article'); localStorage.clear(); const changePage = new ChangePage(true); - changePage.toIndex(); + await changePage.toIndex(); } } @@ -621,13 +621,14 @@ export class SendPost { this.navigationProgress.start(); const UserData = localStorage.getItem('UserData'); - if(UserData){ + if (UserData) + { const parseUserData: UD = JSON.parse(UserData); const params = { UserData: parseUserData, }; return await this.postWithUrlParams('getLoginAndRegisterSettings', params) - .then((response) => + .then(async (response) => { if (response.code === 0) { @@ -636,15 +637,15 @@ export class SendPost { this.handlePopMsg.popMsg(response.message); const changePage = new ChangePage(true); - changePage.toIndex(); + await changePage.toIndex(); return null; } }) - .catch((error) => + .catch(async (error) => { this.handlePopMsg.popMsg((error as Error).message); const changePage = new ChangePage(true); - changePage.toIndex(); + await changePage.toIndex(); return null; } ).finally(() => @@ -657,9 +658,11 @@ export class SendPost } - async updateLoginAndRegisterSettings(setting: setting_loginandregister){ + async updateLoginAndRegisterSettings(setting: setting_loginandregister) + { const UserData = localStorage.getItem('UserData'); - if(UserData){ + if (UserData) + { const parseUserData: UD = JSON.parse(UserData); const params = { UserData: parseUserData, @@ -691,16 +694,18 @@ export class SendPost } - async getEmailSettings(){ + async getEmailSettings() + { this.navigationProgress.start(); const UserData = localStorage.getItem('UserData'); - if(UserData){ + if (UserData) + { const parseUserData: UD = JSON.parse(UserData); const params = { UserData: parseUserData, }; return await this.postWithUrlParams('getEmailSettings', params) - .then((response) => + .then(async (response) => { if (response.code === 0) { @@ -709,32 +714,35 @@ export class SendPost { this.handlePopMsg.popMsg(response.message); const changePage = new ChangePage(true); - changePage.toIndex(); + await changePage.toIndex(); return null; } }) - .catch((error) => + .catch(async (error) => { this.handlePopMsg.popMsg((error as Error).message); const changePage = new ChangePage(true); - changePage.toIndex(); + await changePage.toIndex(); return null; } ).finally(() => { this.navigationProgress.end(); }); - } else { + } else + { const changePage = new ChangePage(true); - changePage.toIndex(); + await changePage.toIndex(); return null; } } - async updateEmailSettings(setting: setting_email){ + async updateEmailSettings(setting: setting_email) + { this.navigationProgress.start(); const UserData = localStorage.getItem('UserData'); - if(UserData){ + if (UserData) + { const parseUserData: UD = JSON.parse(UserData); const params = { UserData: parseUserData, @@ -765,10 +773,12 @@ export class SendPost } } - async sendTestEmail(testEmailAddress:string){ + async sendTestEmail(testEmailAddress: string) + { this.navigationProgress.start(); const UserData = localStorage.getItem('UserData'); - if(UserData){ + if (UserData) + { const parseUserData: UD = JSON.parse(UserData); const params = { UserData: parseUserData, @@ -799,7 +809,8 @@ export class SendPost } } - async sendActivateAccountRequest(token:string){ + async sendActivateAccountRequest(token: string) + { this.navigationProgress.start(); const params = { token: token @@ -828,7 +839,8 @@ export class SendPost }); } - async getAllArticleArea(){ + async getAllArticleArea() + { const params = {}; return await this.postWithUrlParams('getAllArticleArea', params) .then((response) => @@ -839,17 +851,363 @@ export class SendPost } else { this.handlePopMsg.popMsg(response.message); - return []; + return null; } }) .catch((error) => { this.handlePopMsg.popMsg((error as Error).message); - return []; + return null; + } + ).finally(() => + { + }); + } + + async getColorScheme() + { + const params = {}; + return await this.postWithUrlParams('getAllColorScheme', params) + .then((response) => + { + if (response.code === 0) + { + return response.data as ColorScheme[]; + } else + { + this.handlePopMsg.popMsg(response.message); + return null; + } + }) + .catch((error) => + { + this.handlePopMsg.popMsg((error as Error).message); + return null; } ).finally(() => { }); + + } + + async deleteColorScheme(id: number) + { + this.navigationProgress.start(); + const UserData = localStorage.getItem('UserData'); + if (UserData) + { + const parseUserData: UD = JSON.parse(UserData); + const params = { + UserData: parseUserData, + id: id + }; + return await this.postWithUrlParams('deleteColorScheme', params) + .then((response) => + { + if (response.code === 0) + { + this.handlePopMsg.popMsg(response.message); + return true; + } else + { + this.handlePopMsg.popMsg(response.message); + return false; + } + }) + .catch((error) => + { + this.handlePopMsg.popMsg((error as Error).message); + return false; + } + ).finally(() => + { + this.navigationProgress.end(); + }); + } + } + + async addColorScheme(textColor: string, backgroundColor: string) + { + this.navigationProgress.start(); + const UserData = localStorage.getItem('UserData'); + if (UserData) + { + const parseUserData: UD = JSON.parse(UserData); + const params = { + UserData: parseUserData, + textColor: textColor, + backgroundColor: backgroundColor + }; + return await this.postWithUrlParams('addColorScheme', params) + .then((response) => + { + if (response.code === 0) + { + this.handlePopMsg.popMsg(response.message); + return response.data as { id: number; }; + } else + { + this.handlePopMsg.popMsg(response.message); + return null; + } + }) + .catch((error) => + { + this.handlePopMsg.popMsg((error as Error).message); + return null; + } + ).finally(() => + { + this.navigationProgress.end(); + }); + } + } + + async getAllBigArea() + { + this.navigationProgress.start(); + const params = {}; + return await this.postWithUrlParams('getAllArticleBigArea', params) + .then((response) => + { + if (response.code === 0) + { + return response.data as { + id: number, + name: string; + }[]; + } else + { + this.handlePopMsg.popMsg(response.message); + return null; + } + }) + .catch((error) => + { + this.handlePopMsg.popMsg((error as Error).message); + return null; + } + ).finally(() => + { + this.navigationProgress.end(); + }); + } + + async deleteBigArea(id: number){ + this.navigationProgress.start(); + const UserData = localStorage.getItem('UserData'); + if (UserData) + { + const parseUserData: UD = JSON.parse(UserData); + const params = { + UserData: parseUserData, + id: id + }; + return await this.postWithUrlParams('deleteBigArea', params) + .then((response) => + { + if (response.code === 0) + { + this.handlePopMsg.popMsg(response.message); + return true; + } else + { + this.handlePopMsg.popMsg(response.message); + return false; + } + }) + .catch((error) => + { + this.handlePopMsg.popMsg((error as Error).message); + return false; + } + ).finally(() => + { + this.navigationProgress.end(); + }); + } + } + + async updateBigArea(id: number, name: string){ + this.navigationProgress.start(); + const UserData = localStorage.getItem('UserData'); + if (UserData) + { + const parseUserData: UD = JSON.parse(UserData); + const params = { + UserData: parseUserData, + id: id, + name: name + }; + return await this.postWithUrlParams('updateBigArea', params) + .then((response) => + { + if (response.code === 0) + { + this.handlePopMsg.popMsg(response.message); + return true; + } else + { + this.handlePopMsg.popMsg(response.message); + return false; + } + }) + .catch((error) => + { + this.handlePopMsg.popMsg((error as Error).message); + return false; + } + ).finally(() => + { + this.navigationProgress.end(); + }); + } + } + + + async addBigArea(name: string){ + this.navigationProgress.start(); + const UserData = localStorage.getItem('UserData'); + if (UserData) + { + const parseUserData: UD = JSON.parse(UserData); + const params = { + UserData: parseUserData, + name: name + }; + return await this.postWithUrlParams('addBigArea', params) + .then((response) => + { + if (response.code === 0) + { + this.handlePopMsg.popMsg(response.message); + return response.data as { id: number; }; + } else + { + this.handlePopMsg.popMsg(response.message); + return null; + } + }) + .catch((error) => + { + this.handlePopMsg.popMsg((error as Error).message); + return null; + } + ).finally(() => + { + this.navigationProgress.end(); + }); + } } + async updateSubArea(id: number, name: string, bigAreaID: number, colorSchemeID:number){ + this.navigationProgress.start(); + const UserData = localStorage.getItem('UserData'); + if (UserData) + { + const parseUserData: UD = JSON.parse(UserData); + const params = { + UserData: parseUserData, + id: id, + name: name, + bigAreaID: bigAreaID, + colorSchemeID: colorSchemeID + }; + return await this.postWithUrlParams('updateSubArea', params) + .then((response) => + { + if (response.code === 0) + { + this.handlePopMsg.popMsg(response.message); + return true; + } else + { + this.handlePopMsg.popMsg(response.message); + return false; + } + }) + .catch((error) => + { + this.handlePopMsg.popMsg((error as Error).message); + return false; + } + ).finally(() => + { + this.navigationProgress.end(); + }); + } + + } + + async deleteSubArea(id: number){ + this.navigationProgress.start(); + const UserData = localStorage.getItem('UserData'); + if (UserData) + { + const params = { + UserData: JSON.parse(UserData), + id: id + }; + return await this.postWithUrlParams('deleteSubArea', params) + .then((response) => + { + if (response.code === 0) + { + this.handlePopMsg.popMsg(response.message); + + return true; + } else + { + this.handlePopMsg.popMsg(response.message); + return false; + } + }) + .catch((error) => + { + console.log(error); + return false; + } + ).finally(() => + { + this.navigationProgress.end(); + }); + } + } + + async addSubArea(name: string, bigAreaID: number, colorSchemeID:number){ + this.navigationProgress.start(); + const UserData = localStorage.getItem('UserData'); + if (UserData) + { + const params = { + UserData: JSON.parse(UserData), + name: name, + bigAreaID: bigAreaID, + colorSchemeID: colorSchemeID + }; + return await this.postWithUrlParams('addSubArea', params) + .then((response) => + { + if (response.code === 0) + { + this.handlePopMsg.popMsg(response.message); + + return response.data as { id: number; }; + } else + { + this.handlePopMsg.popMsg(response.message); + return null; + } + }) + .catch((error) => + { + console.log(error); + return null; + } + ).finally(() => + { + this.navigationProgress.end(); + }); + } + } } diff --git a/src/ts/Send Fetch/interface.ts b/src/ts/Send Fetch/interface.ts index c8aae62..ce9bd93 100644 --- a/src/ts/Send Fetch/interface.ts +++ b/src/ts/Send Fetch/interface.ts @@ -66,7 +66,21 @@ export interface setting_email{ forceSSL: number; } +interface Subarea { + subAreaID: number | null; + subareaName: string | null; + colorscheme: ColorScheme | null; +} + export interface ArticleArea { - AreaName: string; - subArea: (string | null)[]; + bigareaID: number; + bigAreaName:string; + subarea: Subarea[]; } + + +export interface ColorScheme{ + id: number + textColor: string + backgroundColor: string +} \ No newline at end of file diff --git a/src/ts/UploadArticle/index.ts b/src/ts/UploadArticle/index.ts index 5efac7e..08ed5d6 100644 --- a/src/ts/UploadArticle/index.ts +++ b/src/ts/UploadArticle/index.ts @@ -6,6 +6,7 @@ import { IDomEditor } from "@wangeditor/editor"; import { UserVerification } from "../User Verification"; import { NavigationProgress } from "../Create Navigation Progress"; import { RetArticleData } from "../Send Fetch/interface"; +import { ChangePage } from "../Navigation Bar/changePage"; export class UploadArticle { private navigationProgress = new NavigationProgress(); @@ -52,38 +53,8 @@ export class UploadArticle async function createTitleInput(article: RetArticleData | null) { - async function createSelect() + async function createTitleInput() { - const sendPost = new SendPost(); - const allArea = await sendPost.getAllArticleArea(); - const areaOptions = allArea.map(area => area.subArea); - const areaWrapper = document.createElement('div'); - const areaSelect = document.createElement('select'); - areaWrapper.className = 'areaWrapper'; - areaSelect.id = 'areaSelect'; - areaSelect.className = 'areaSelect'; - areaOptions.forEach(optionValue => - { - optionValue.forEach(optionValue => - { - if (!optionValue) return; - const option = document.createElement('option'); - option.value = optionValue; - option.innerHTML = optionValue; - areaSelect.appendChild(option); - }); - }); - - if (article?.article.articleArea) - { - areaSelect.value = article.article.articleArea; - } - areaWrapper.appendChild(areaSelect); - return areaWrapper; - - } - - async function createTitleInput(){ const titleWrapper = document.createElement('div'); const titleInput = document.createElement('input'); titleWrapper.className = 'titleWrapper'; @@ -92,16 +63,18 @@ export class UploadArticle titleInput.id = 'titleInput'; titleInput.className = 'titleInput'; - titleInput.onfocus = () =>{ - titleWrapper.style.backgroundColor = 'white' - titleWrapper.style.boxShadow = '0 0 0 1px rgb(22,192,248) inset' - - } + titleInput.onfocus = () => + { + titleWrapper.style.backgroundColor = 'white'; + titleWrapper.style.boxShadow = '0 0 0 1px rgb(22,192,248) inset'; - titleInput.onblur = () =>{ - titleWrapper.style.backgroundColor = 'rgba(242, 243, 245, 1)' - titleWrapper.style.boxShadow = 'none' - } + }; + + titleInput.onblur = () => + { + titleWrapper.style.backgroundColor = 'rgba(242, 243, 245, 1)'; + titleWrapper.style.boxShadow = 'none'; + }; if (article?.article.articleTitle) { @@ -111,48 +84,57 @@ export class UploadArticle return titleWrapper; } - async function createAreaInput(){ - function createDropDownMenu(){ + async function createAreaInput() + { + function createDropDownMenu() + { const dropDownWrapper = document.createElement('div'); dropDownWrapper.className = 'dropDownWrapper'; - + const bigAreaWrapper = document.createElement('div'); bigAreaWrapper.className = 'bigAreaWrapper'; const subAreaWrapper = document.createElement('div'); subAreaWrapper.className = 'subAreaWrapper'; - - allArea.forEach(area => { + if (!allArea) return dropDownWrapper; + allArea.forEach(area => + { const bigAreaBtn = document.createElement('div'); bigAreaBtn.className = 'bigAreaBtn'; - bigAreaBtn.innerHTML = area.AreaName; - bigAreaBtn.onclick = () =>{ + bigAreaBtn.innerHTML = area.bigAreaName; + bigAreaBtn.onclick = () => + { const subAreaBtn = document.querySelectorAll('.subAreaBtn') as NodeListOf; - subAreaBtn.forEach(btn => { + subAreaBtn.forEach(btn => + { btn.remove(); - }) - area.subArea.forEach(subArea => { - if(subArea){ + }); + area.subarea.forEach(subArea => + { + if (subArea.subareaName) + { const subAreaBtn = document.createElement('div'); subAreaBtn.className = 'subAreaBtn'; - subAreaBtn.innerHTML = subArea; + subAreaBtn.innerHTML = subArea.subareaName; subAreaWrapper.appendChild(subAreaBtn); - subAreaBtn.onclick = () =>{ - areaInput.value = area.AreaName + ' - ' + subArea; - areaHiddenInput.value = subArea; - areaInputWrapper.style.backgroundColor = 'rgba(242, 243, 245, 1)' - areaInputWrapper.style.boxShadow = 'none' - areaWrapper.style.width = '30%' + subAreaBtn.onclick = () => + { + if (!subArea.subareaName) return; + areaInput.value = area.bigAreaName + ' - ' + subArea.subareaName; + areaHiddenInput.value = subArea.subareaName; + areaInputWrapper.style.backgroundColor = 'rgba(242, 243, 245, 1)'; + areaInputWrapper.style.boxShadow = 'none'; + areaWrapper.style.width = '30%'; dropDownWrapper.classList.remove('showDropDownWrapper'); - } + }; } - }) - subAreaWrapper.style.width =bigAreaWrapper.offsetWidth + 'px'; - } + }); + subAreaWrapper.style.width = bigAreaWrapper.offsetWidth + 'px'; + }; bigAreaWrapper.appendChild(bigAreaBtn); - }) + }); dropDownWrapper.appendChild(bigAreaWrapper); dropDownWrapper.appendChild(subAreaWrapper); @@ -160,7 +142,6 @@ export class UploadArticle return dropDownWrapper; } const allArea = await sendPost.getAllArticleArea(); - console.log(allArea); const areaWrapper = document.createElement('div'); areaWrapper.className = 'areaWrapper'; @@ -177,14 +158,22 @@ export class UploadArticle areaHiddenInput.type = 'hidden'; areaHiddenInput.className = 'areaHiddenInput'; areaHiddenInput.id = 'areaHiddenInput'; - + if (article?.article.articleArea) { - const foundArea = allArea.find(area => area.subArea.includes(article.article.articleArea)); - if(foundArea){ - areaInput.value = foundArea.AreaName + ' - ' + article.article.articleArea; - areaHiddenInput.value = article.article.articleArea; + if (allArea) + { + const foundArea = allArea.find(area => + area.subarea.some(subarea => subarea.subareaName === article.article.articleArea) + ); + if (foundArea) + { + const foundSubarea = foundArea.subarea.find(subarea => subarea.subareaName === article.article.articleArea); + areaInput.value = foundArea.bigAreaName + ' - ' + foundSubarea?.subareaName; + areaHiddenInput.value = foundSubarea?.subareaName || ''; + } } + } const dropDownWrapper = createDropDownMenu(); @@ -193,33 +182,38 @@ export class UploadArticle areaInputWrapper.appendChild(dropDownWrapper); areaWrapper.appendChild(areaInputWrapper); - areaInput.onfocus = () =>{ - areaInputWrapper.style.backgroundColor = 'white' - areaInputWrapper.style.boxShadow = '0 0 0 1px rgb(22,192,248) inset' - areaWrapper.style.width = '50%' + areaInput.onfocus = () => + { + areaInputWrapper.style.backgroundColor = 'white'; + areaInputWrapper.style.boxShadow = '0 0 0 1px rgb(22,192,248) inset'; + areaWrapper.style.width = '50%'; const dropDownWrapper = document.querySelector('.dropDownWrapper') as HTMLDivElement; - if(dropDownWrapper){ + if (dropDownWrapper) + { dropDownWrapper.classList.add('showDropDownWrapper'); } - } + }; - areaInput.onblur = () =>{ + areaInput.onblur = () => + { const dropDownWrapper = document.querySelector('.dropDownWrapper') as HTMLDivElement; - if(dropDownWrapper.matches(':hover')){ - dropDownWrapper.onmouseleave = () =>{ - areaInputWrapper.style.backgroundColor = 'rgba(242, 243, 245, 1)' - areaInputWrapper.style.boxShadow = 'none' - areaWrapper.style.width = '30%' + if (dropDownWrapper.matches(':hover')) + { + dropDownWrapper.onmouseleave = () => + { + areaInputWrapper.style.backgroundColor = 'rgba(242, 243, 245, 1)'; + areaInputWrapper.style.boxShadow = 'none'; + areaWrapper.style.width = '30%'; dropDownWrapper.classList.remove('showDropDownWrapper'); - } + }; return; } - areaInputWrapper.style.backgroundColor = 'rgba(242, 243, 245, 1)' - areaInputWrapper.style.boxShadow = 'none' - areaWrapper.style.width = '30%' + areaInputWrapper.style.backgroundColor = 'rgba(242, 243, 245, 1)'; + areaInputWrapper.style.boxShadow = 'none'; + areaWrapper.style.width = '30%'; dropDownWrapper.classList.remove('showDropDownWrapper'); - } + }; return areaWrapper; } @@ -228,11 +222,11 @@ export class UploadArticle inputWrapper.className = 'inputWrapper'; const sendPost = new SendPost(); - + const titleWrapper = await createTitleInput(); const areaInput = await createAreaInput(); - - + + inputWrapper.appendChild(titleWrapper); inputWrapper.appendChild(areaInput); @@ -241,27 +235,41 @@ export class UploadArticle const createEditor = (html: string | null = null) => { - const editorWarpper = document.createElement('div'); - const toolbar = document.createElement('div'); - const editorContainer = document.createElement('div'); - - editorWarpper.id = 'editor—wrapper'; - editorWarpper.className = 'editor'; - - toolbar.id = 'toolbar-container'; - - editorContainer.id = 'editor-container'; - editorContainer.className = 'editor-container'; - - editorWarpper.appendChild(toolbar); - editorWarpper.appendChild(editorContainer); - postWrapper.appendChild(editorWarpper); + try { + const editorWarpper = document.createElement('div'); + const toolbar = document.createElement('div'); + const editorContainer = document.createElement('div'); + + editorWarpper.id = 'editor—wrapper'; + editorWarpper.className = 'editor'; + + toolbar.id = 'toolbar-container'; + + editorContainer.id = 'editor-container'; + editorContainer.className = 'editor-container'; + + editorWarpper.appendChild(toolbar); + editorWarpper.appendChild(editorContainer); + postWrapper.appendChild(editorWarpper); + + // 在窗口大小改变时重新设置 editorContainer 的高度 + window.addEventListener('resize', this.setEditorContainerHeight); + + const editor = new Editor(); + return editor.createEditor(html); + } catch (error) { + console.log(error); + const contentDivs = document.querySelectorAll('.contentDiv'); + contentDivs.forEach((contentDiv) => { + if(contentDiv.parentNode) + contentDiv.parentNode.removeChild(contentDiv); + }); + const changePage = new ChangePage(true); + changePage.to404Page(); - // 在窗口大小改变时重新设置 editorContainer 的高度 - window.addEventListener('resize', this.setEditorContainerHeight); + return null; + } - const editor = new Editor(); - return editor.createEditor(html); }; @@ -313,11 +321,13 @@ export class UploadArticle if (article) { const editor = createEditor(article.article.articleContent); + if(!editor) return; const submitWrapper = createSubmit(editor); postWrapper.appendChild(submitWrapper); } else { const editor = createEditor(); + if(!editor) return; const submitWrapper = createSubmit(editor); postWrapper.appendChild(submitWrapper); } diff --git a/src/ts/User Profile/index.ts b/src/ts/User Profile/index.ts index 95f6a28..7ede23b 100644 --- a/src/ts/User Profile/index.ts +++ b/src/ts/User Profile/index.ts @@ -31,25 +31,24 @@ export class MakeUserProfile if (id && Number.isInteger(parseInt(id))) { const profileData = await sendPost.getUserProfile(parseInt(id)); - console.log(profileData); if (profileData) { this.CreateContent(profileData); } else { const changePage = new ChangePage(true); - changePage.to404Page(); + await changePage.to404Page(); return; } } else { const changePage = new ChangePage(true); - changePage.to404Page(); + await changePage.to404Page(); } } else if (hash === '') { const changePage = new ChangePage(true); - changePage.to404Page(); + await changePage.to404Page(); } }; @@ -131,7 +130,7 @@ export class MakeUserProfile } clicked = true; const changePage = new ChangePage(true); - changePage.toUserProfile(data.userData.id.toString()); + await changePage.toUserProfile(data.userData.id.toString()); } } }; @@ -287,7 +286,7 @@ export class MakeUserProfile }, { name: 'Settings', - func: function () + func: async function () { navigationProgress.start(); const rightPanel = document.querySelector('.userProfile-Usertab-rightPanel') as HTMLDivElement; @@ -297,7 +296,7 @@ export class MakeUserProfile const rightPanerWrapper = document.querySelector('.userProfile-Usertab-rightPanelWrapper') as HTMLDivElement; if (rightPanerWrapper) { - const rightPanel = createSettingRightPanel(); + const rightPanel = await createSettingRightPanel(); rightPanerWrapper.appendChild(rightPanel); } @@ -485,13 +484,13 @@ export class MakeUserProfile return rightPanel; } - function createSettingRightPanel() + async function createSettingRightPanel() { function Navigation() { const navigationItems = [{ name: 'Information', - func: function (navigationItem: HTMLDivElement) + func: async function (navigationItem: HTMLDivElement) { const navigationItemOnSelect = document.querySelectorAll('.navigationItemOnSelect'); navigationItemOnSelect.forEach(item => @@ -511,7 +510,7 @@ export class MakeUserProfile const rightPanel = document.querySelector('.userProfile-Usertab-rightPanel') as HTMLDivElement; if (rightPanel) { - const basicSettings = BasicSettings(); + const basicSettings = await BasicSettings(); if (basicSettings) { rightPanel.appendChild(basicSettings); @@ -572,7 +571,7 @@ export class MakeUserProfile return navigationWrapper; } - function BasicSettings() + async function BasicSettings() { function createChangeName(parsedUserData: UserData) @@ -728,7 +727,7 @@ export class MakeUserProfile { localStorage.setItem('UserData', JSON.stringify(newUserData)); const changePage = new ChangePage(true); - changePage.toUserProfile(newUserData.userData.id.toString()); + await changePage.toUserProfile(newUserData.userData.id.toString()); } } @@ -766,7 +765,7 @@ export class MakeUserProfile } else { const changePage = new ChangePage(true); - changePage.to404Page(); + await changePage.to404Page(); return; } @@ -862,7 +861,7 @@ export class MakeUserProfile if(result){ const changePage = new ChangePage(true); - changePage.toUserProfile(parsedUserData.userData.id.toString()); + await changePage.toUserProfile(parsedUserData.userData.id.toString()); } @@ -890,7 +889,7 @@ export class MakeUserProfile rightPanel.className = 'userProfile-Usertab-rightPanel'; const navigation = Navigation(); - const basicSettings = BasicSettings(); + const basicSettings = await BasicSettings(); rightPanel.appendChild(navigation); if (basicSettings) diff --git a/src/ts/User Verification/index.ts b/src/ts/User Verification/index.ts index 61c7980..695cb78 100644 --- a/src/ts/User Verification/index.ts +++ b/src/ts/User Verification/index.ts @@ -17,7 +17,7 @@ export class UserVerification{ { localStorage.clear(); const changePage = new ChangePage(true); - changePage.toIndex(); + await changePage.toIndex(); location.reload(); return false } diff --git a/src/ts/index.ts b/src/ts/index.ts index 51bd38b..169908a 100644 --- a/src/ts/index.ts +++ b/src/ts/index.ts @@ -23,33 +23,33 @@ class init const id = hash.slice(hash.indexOf('?id=') + 4); if (id && Number.isInteger(parseInt(id))) { - changePage.toUserProfile(id); + await changePage.toUserProfile(id); } else { - changePage.toIndex(); + await changePage.toIndex(); } } else if (hash === '') { - changePage.toIndex(); + await changePage.toIndex(); } else if (hash.startsWith('#/newpost')) { - changePage.toPostArticle(); + await changePage.toPostArticle(); } else if (hash.startsWith('#/p')) { const id = hash.slice(hash.indexOf('?id=') + 4); if (id && Number.isInteger(parseInt(id))) { - changePage.toArticle(id); + await changePage.toArticle(id); } else { - changePage.toIndex(); + await changePage.toIndex(); } } else if (hash.startsWith('#/404')) { changePage.to404Page(); } else if (hash.startsWith('#/manage%20article')) { - changePage.toManageArticle(); + await changePage.toManageArticle(); } else if (hash.startsWith('#/editArticle')) { const id = hash.slice(hash.indexOf('?id=') + 4); @@ -58,48 +58,49 @@ class init await changePage.toEditArticle(parseInt(id)); } else { - changePage.toIndex(); + await changePage.toIndex(); } } else if (hash.startsWith('#/area')) { const area = hash.slice(hash.indexOf('?area=') + 6); if (area) { - changePage.toArea(area); + await changePage.toArea(area); } else { - changePage.toIndex(); + await changePage.toIndex(); } } else if (hash.startsWith('#/search')){ const keyword = hash.slice(hash.indexOf('?keyword=') + 9); console.log(keyword) if (keyword) { - changePage.toSearch(keyword); + await changePage.toSearch(keyword); } else { - changePage.toIndex(); + await changePage.toIndex(); } } else if (hash.startsWith('#/admin')){ - changePage.toAdminPage(); + await changePage.toAdminPage(); } else if(hash.startsWith('#/activateAccount')){ const token = hash.slice(hash.indexOf('?token=') + 7); if (token) { - changePage.toActivateAccountPage(); + await changePage.toActivateAccountPage(); } else { - changePage.toIndex(); + await changePage.toIndex(); } - } - else + }else { - changePage.toIndex(); + await changePage.toIndex(); } const navigationProgress = new NavigationProgress(); navigationProgress.init(); + + }