From 82a11e2660d2fda5eb127c1bbec8d2177b462439 Mon Sep 17 00:00:00 2001 From: James Gates Date: Fri, 29 Mar 2024 13:53:56 -0700 Subject: [PATCH 01/16] Update README.md --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 428a284..0d37445 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,10 @@ This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next [![Next.js Bundle Analysis](https://github.com/james-gates-0212/portfolio/actions/workflows/nextjs-bundle-analysis.yml/badge.svg)](https://github.com/james-gates-0212/portfolio/actions/workflows/nextjs-bundle-analysis.yml) [![Proof HTML](https://github.com/james-gates-0212/portfolio/actions/workflows/proof-html.yml/badge.svg)](https://github.com/james-gates-0212/portfolio/actions/workflows/proof-html.yml) +![GitHub Issues](https://img.shields.io/github/issues/james-gates-0212/portfolio) +![GitHub Release](https://img.shields.io/github/v/release/james-gates-0212/portfolio) +![GitHub Tag](https://img.shields.io/github/v/tag/james-gates-0212/portfolio) + ## ๐Ÿ“Environment - node 18.x or later From 5d4280879b886579615892cdb696d66ab424b300 Mon Sep 17 00:00:00 2001 From: James Gates Date: Sat, 30 Mar 2024 01:02:45 -0700 Subject: [PATCH 02/16] Update README.md --- README.md | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 0d37445..70227e7 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,9 @@ -# โค๏ธPortfolio based on Next.js & tailwindcss - -This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). - -## ๐Ÿ”–Repository Status +![GitHub forks](https://img.shields.io/github/forks/james-gates-0212/portfolio?style=flat) +![GitHub Discussions](https://img.shields.io/github/discussions/james-gates-0212/portfolio) +![GitHub Issues](https://img.shields.io/github/issues/james-gates-0212/portfolio) +![GitHub License](https://img.shields.io/github/license/james-gates-0212/portfolio) +![GitHub Release](https://img.shields.io/github/v/release/james-gates-0212/portfolio) +![GitHub Tag](https://img.shields.io/github/v/tag/james-gates-0212/portfolio) [![Auto Assign](https://github.com/james-gates-0212/portfolio/actions/workflows/auto-assign.yml/badge.svg)](https://github.com/james-gates-0212/portfolio/actions/workflows/auto-assign.yml) [![CodeQL](https://github.com/james-gates-0212/portfolio/actions/workflows/github-code-scanning/codeql/badge.svg)](https://github.com/james-gates-0212/portfolio/actions/workflows/github-code-scanning/codeql) @@ -10,9 +11,9 @@ This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next [![Next.js Bundle Analysis](https://github.com/james-gates-0212/portfolio/actions/workflows/nextjs-bundle-analysis.yml/badge.svg)](https://github.com/james-gates-0212/portfolio/actions/workflows/nextjs-bundle-analysis.yml) [![Proof HTML](https://github.com/james-gates-0212/portfolio/actions/workflows/proof-html.yml/badge.svg)](https://github.com/james-gates-0212/portfolio/actions/workflows/proof-html.yml) -![GitHub Issues](https://img.shields.io/github/issues/james-gates-0212/portfolio) -![GitHub Release](https://img.shields.io/github/v/release/james-gates-0212/portfolio) -![GitHub Tag](https://img.shields.io/github/v/tag/james-gates-0212/portfolio) +# โค๏ธPortfolio based on Next.js & tailwindcss + +This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). ## ๐Ÿ“Environment From 1998f2182b444c66a48d17f11b7a51c83cebcc96 Mon Sep 17 00:00:00 2001 From: James Gates Date: Sat, 30 Mar 2024 02:30:35 -0700 Subject: [PATCH 03/16] Update README.md --- README.md | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 70227e7..4c52559 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,11 @@ ![GitHub Discussions](https://img.shields.io/github/discussions/james-gates-0212/portfolio) ![GitHub Issues](https://img.shields.io/github/issues/james-gates-0212/portfolio) ![GitHub License](https://img.shields.io/github/license/james-gates-0212/portfolio) +![GitHub Repo stars](https://img.shields.io/github/stars/james-gates-0212/portfolio?style=flat) +![GitHub top language](https://img.shields.io/github/languages/top/james-gates-0212/portfolio) +![GitHub repo file or directory count](https://img.shields.io/github/directory-file-count/james-gates-0212/portfolio) +![GitHub code size in bytes](https://img.shields.io/github/languages/code-size/james-gates-0212/portfolio) +![GitHub repo size](https://img.shields.io/github/repo-size/james-gates-0212/portfolio) ![GitHub Release](https://img.shields.io/github/v/release/james-gates-0212/portfolio) ![GitHub Tag](https://img.shields.io/github/v/tag/james-gates-0212/portfolio) @@ -11,24 +16,24 @@ [![Next.js Bundle Analysis](https://github.com/james-gates-0212/portfolio/actions/workflows/nextjs-bundle-analysis.yml/badge.svg)](https://github.com/james-gates-0212/portfolio/actions/workflows/nextjs-bundle-analysis.yml) [![Proof HTML](https://github.com/james-gates-0212/portfolio/actions/workflows/proof-html.yml/badge.svg)](https://github.com/james-gates-0212/portfolio/actions/workflows/proof-html.yml) -# โค๏ธPortfolio based on Next.js & tailwindcss +# Portfolio based on Next.js & tailwindcss This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app). -## ๐Ÿ“Environment +## Environment - node 18.x or later - yarn 1.x or later - npm 8.x or later -## ๐ŸšงInstall npm packages +## Install npm packages ```bash npm i -g yarn yarn ``` -## ๐ŸŒˆGetting Started +## Getting Started First, run the development server: @@ -42,13 +47,13 @@ You can start editing the page by modifying `app/page.tsx`. The page auto-update This project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font. -## โœ…Lint +## Lint ```bash yarn lint ``` -## โค๏ธPrettier +## Prettier ```bash prettier --write ./ @@ -59,7 +64,7 @@ prettier --write ./ npm i -g prettier ``` -## ๐Ÿš€Build or Start project as production +## Build or Start project as production ```bash yarn build @@ -71,7 +76,7 @@ or yarn start ``` -## ๐Ÿ†Learn More +## Learn More To learn more about Next.js, take a look at the following resources: @@ -80,13 +85,13 @@ To learn more about Next.js, take a look at the following resources: You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome! -## ๐Ÿš€Deploy on Vercel +## Deploy on Vercel The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. Check out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details. -## ๐ŸžGit commit +## Git commit Please use [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) for your commits. @@ -98,7 +103,7 @@ e.g. [footer] ``` -### โ™ป๏ธTypes +### Types `type` should be one of belows. @@ -118,7 +123,7 @@ remove style ``` -### ๐Ÿ““Subject +### Subject `subject` should include one of emojis as belows. From f7f6db0f989236fe79672665a074c060427d542a Mon Sep 17 00:00:00 2001 From: James Gates Date: Sun, 31 Mar 2024 18:01:29 -0700 Subject: [PATCH 04/16] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 4c52559..6ca72f4 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,4 @@ +![Hits](https://hits.seeyoufarm.com/api/count/incr/badge.svg?url=https%3A%2F%2Fgithub.com%2Fjames-gates-0212%2Fportfolio) ![GitHub forks](https://img.shields.io/github/forks/james-gates-0212/portfolio?style=flat) ![GitHub Discussions](https://img.shields.io/github/discussions/james-gates-0212/portfolio) ![GitHub Issues](https://img.shields.io/github/issues/james-gates-0212/portfolio) From 7afa6ad23f6e980f2700437a1edf2b5bcc8733f8 Mon Sep 17 00:00:00 2001 From: james-gates-0212 Date: Mon, 1 Apr 2024 22:40:05 -0700 Subject: [PATCH 05/16] =?UTF-8?q?config:=20add=20`--turbo`=20option?= =?UTF-8?q?=F0=9F=9A=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c36cbd4..2291fee 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,7 @@ "yarn": ">=1" }, "scripts": { - "dev": "next dev", + "dev": "next dev --turbo", "build": "next build", "start": "npm run build && next start", "lint": "next lint", From 3be7c73f7d7764f4cd561409bc10ff5b70e46e22 Mon Sep 17 00:00:00 2001 From: james-gates-0212 Date: Mon, 8 Apr 2024 01:46:51 -0700 Subject: [PATCH 06/16] :bricks: upgrade: commit lint & database migration --- .gitignore | 1 + .husky/pre-commit | 3 - README.md | 130 ++++++++++++----- commitlint.config.js | 54 +------ next.config.js | 3 + package.json | 12 +- src/database/migrations/create.ts | 48 ++++-- src/database/models/experience.ts | 59 ++++++++ src/database/models/index.ts | 17 ++- src/database/models/user.ts | 86 ++--------- tsconfig.json | 15 +- yarn.lock | 233 +++++++++++++++++++++++++----- 12 files changed, 432 insertions(+), 229 deletions(-) create mode 100644 src/database/models/experience.ts diff --git a/.gitignore b/.gitignore index a84107a..785504f 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,7 @@ # testing /coverage +/coverage* # next.js /.next/ diff --git a/.husky/pre-commit b/.husky/pre-commit index d29b88c..e69de29 100644 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -1,3 +0,0 @@ -# yarn test - -# git commit --no-verify diff --git a/README.md b/README.md index 6ca72f4..b2889c7 100644 --- a/README.md +++ b/README.md @@ -60,11 +60,6 @@ yarn lint prettier --write ./ ``` -```bash -# if you couldn't run above command line, it would work after run this... -npm i -g prettier -``` - ## Build or Start project as production ```bash @@ -94,27 +89,30 @@ Check out our [Next.js deployment documentation](https://nextjs.org/docs/deploym ## Git commit -Please use [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) for your commits. +> [!NOTE]\ +> Please use [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) for your commits. e.g. -```commit -[scope]: +``` +{emoji} [scope]: [body] [footer] ``` ### Types -`type` should be one of belows. +> [!IMPORTANT]\ +> `type` should be one of belows. -```bash +``` add adopt apply build chore config +delete docs feat fix @@ -122,33 +120,89 @@ init refactor remove style +test +update +upgrade ``` -### Subject - -`subject` should include one of emojis as belows. - -โ™ป๏ธ -โšก๏ธ -โœ… -โœ๏ธ -โœจ -โค๏ธ -โฌ†๏ธ -โฌ‡๏ธ -โญ๏ธ -๐ŸŒˆ -๐ŸŽ -๐ŸŽ‰ -๐Ÿ† -๐Ÿž -๐Ÿ‘Œ -๐Ÿ““ -๐Ÿ“ -๐Ÿ“ฆ -๐Ÿ”€ -๐Ÿ”– -๐Ÿš€ -๐Ÿšง -๐Ÿšจ -๐Ÿ› ๏ธ +### Emoji + +> [!IMPORTANT]\ +> Your commit should start with gitmoji code.\ +> Please check the emoji code on https://gitmoji.dev/ + +| Description | Emoji | Code | +| :------------------------------------------------------------ | :---: | :---------------------------- | +| Add a dependency. | โž• | `:heavy_plus_sign:` | +| Add a failing test. | ๐Ÿงช | `:test_tube:` | +| Add or update a .gitignore file. | ๐Ÿ™ˆ | `:see_no_evil:` | +| Add or update an easter egg. | ๐Ÿฅš | `:egg:` | +| Add or update analytics or track code. | ๐Ÿ“ˆ | `:chart_with_upwards_trend:` | +| Add or update animations and transitions. | ๐Ÿ’ซ | `:dizzy:` | +| Add or update assets. | ๐Ÿฑ | `:bento:` | +| Add or update business logic. | ๐Ÿ‘” | `:necktie:` | +| Add or update CI build system. | ๐Ÿ‘ท | `:construction_worker:` | +| Add or update code related to multithreading or concurrency. | ๐Ÿงต | `:thread:` | +| Add or update code related to validation. | ๐Ÿฆบ | `:safety_vest:` | +| Add or update comments in source code. | ๐Ÿ’ก | `:bulb:` | +| Add or update compiled files or packages. | ๐Ÿ“ฆ๏ธ | `:package:` | +| Add or update configuration files. | ๐Ÿ”ง | `:wrench:` | +| Add or update contributor(s). | ๐Ÿ‘ฅ | `:busts_in_silhouette:` | +| Add or update development scripts. | ๐Ÿ”จ | `:hammer:` | +| Add or update documentation. | ๐Ÿ“ | `:memo:` | +| Add or update healthcheck. | ๐Ÿฉบ | `:stethoscope:` | +| Add or update license. | ๐Ÿ“„ | `:page_facing_up:` | +| Add or update logs. | ๐Ÿ”Š | `:loud_sound:` | +| Add or update secrets. | ๐Ÿ” | `:closed_lock_with_key:` | +| Add or update seed files. | ๐ŸŒฑ | `:seedling:` | +| Add or update snapshots. | ๐Ÿ“ธ | `:camera_flash:` | +| Add or update text and literals. | ๐Ÿ’ฌ | `:speech_balloon:` | +| Add or update the UI and style files. | ๐Ÿ’„ | `:lipstick:` | +| Add or update types. | ๐Ÿท๏ธ | `:label:` | +| Add sponsorships or money related infrastructure. | ๐Ÿ’ธ | `:money_with_wings:` | +| Add, update, or pass tests. | โœ… | `:white_check_mark:` | +| Add, update, or remove feature flags. | ๐Ÿšฉ | `:triangular_flag_on_post:` | +| Begin a project. | ๐ŸŽ‰ | `:tada:` | +| Catch errors. | ๐Ÿฅ… | `:goal_net:` | +| Critical hotfix. | ๐Ÿš‘๏ธ | `:ambulance:` | +| Data exploration/inspection. | ๐Ÿง | `:monocle_face:` | +| Deploy stuff. | ๐Ÿš€ | `:rocket:` | +| Deprecate code that needs to be cleaned up. | ๐Ÿ—‘๏ธ | `:wastebasket:` | +| Downgrade dependencies. | โฌ‡๏ธ | `:arrow_down:` | +| Fix a bug. | ๐Ÿ› | `:bug:` | +| Fix CI Build. | ๐Ÿ’š | `:green_heart:` | +| Fix compiler / linter warnings. | ๐Ÿšจ | `:rotating_light:` | +| Fix security or privacy issues. | ๐Ÿ”’๏ธ | `:lock:` | +| Fix typos. | โœ๏ธ | `:pencil2:` | +| Improve accessibility. | โ™ฟ๏ธ | `:wheelchair:` | +| Improve developer experience. | ๐Ÿง‘โ€๐Ÿ’ป | `:technologist:` | +| Improve performance. | โšก๏ธ | `:zap:` | +| Improve SEO. | ๐Ÿ”๏ธ | `:mag:` | +| Improve structure / format of the code. | ๐ŸŽจ | `:art:` | +| Improve user experience / usability. | ๐Ÿšธ | `:children_crossing:` | +| Infrastructure related changes. | ๐Ÿงฑ | `:bricks:` | +| Internationalization and localization. | ๐ŸŒ | `:globe_with_meridians:` | +| Introduce breaking changes. | ๐Ÿ’ฅ | `:boom:` | +| Introduce new features. | โœจ | `:sparkles:` | +| Make architectural changes. | ๐Ÿ—๏ธ | `:building_construction:` | +| Merge branches. | ๐Ÿ”€ | `:twisted_rightwards_arrows:` | +| Mock things. | ๐Ÿคก | `:clown_face:` | +| Move or rename resources (e.g.: files, paths, routes). | ๐Ÿšš | `:truck:` | +| Perform database related changes. | ๐Ÿ—ƒ๏ธ | `:card_file_box:` | +| Perform experiments. | โš—๏ธ | `:alembic:` | +| Pin dependencies to specific versions. | ๐Ÿ“Œ | `:pushpin:` | +| Refactor code. | โ™ป๏ธ | `:recycle:` | +| Release / Version tags. | ๐Ÿ”– | `:bookmark:` | +| Remove a dependency. | โž– | `:heavy_minus_sign:` | +| Remove code or files. | ๐Ÿ”ฅ | `:fire:` | +| Remove dead code. | โšฐ๏ธ | `:coffin:` | +| Remove logs. | ๐Ÿ”‡ | `:mute:` | +| Revert changes. | โช๏ธ | `:rewind:` | +| Simple fix for a non-critical issue. | ๐Ÿฉน | `:adhesive_bandage:` | +| Update code due to external API changes. | ๐Ÿ‘ฝ๏ธ | `:alien:` | +| Upgrade dependencies. | โฌ†๏ธ | `:arrow_up:` | +| Work in progress. | ๐Ÿšง | `:construction:` | +| Work on code related to authorization, roles and permissions. | ๐Ÿ›‚ | `:passport_control:` | +| Work on responsive design. | ๐Ÿ“ฑ | `:iphone:` | +| Write bad code that needs to be improved. | ๐Ÿ’ฉ | `:poop:` | +| Write code drunkenly. | ๐Ÿป | `:beers:` | diff --git a/commitlint.config.js b/commitlint.config.js index cd9ebe4..36e7804 100644 --- a/commitlint.config.js +++ b/commitlint.config.js @@ -1,54 +1,6 @@ -const emojis = [ - 'โ™ป๏ธ', - 'โšก๏ธ', - 'โœ…', - 'โœ๏ธ', - 'โœจ', - 'โค๏ธ', - 'โฌ†๏ธ', - 'โฌ‡๏ธ', - 'โญ๏ธ', - '๐ŸŒˆ', - '๐ŸŽ', - '๐ŸŽ‰', - '๐Ÿ†', - '๐Ÿž', - '๐Ÿ‘Œ', - '๐Ÿ““', - '๐Ÿ“', - '๐Ÿ“ฆ', - '๐Ÿ”€', - '๐Ÿ”–', - '๐Ÿš€', - '๐Ÿšง', - '๐Ÿšจ', - '๐Ÿ› ๏ธ', -]; - -const includeEmojis = new RegExp(`[${emojis.join('')}]`, 'g'); - const config = { - extends: ['@commitlint/config-conventional'], - plugins: [ - { - rules: { - 'header-match-team-pattern': (parsed) => { - const { subject } = parsed; - if (includeEmojis.test(subject) === false) { - return [false, 'subject should include one of [' + emojis.join(', ') + ']']; - } - return [true, '']; - }, - }, - }, - ], - /* - * Any rules defined here will override rules from @commitlint/config-conventional - */ + extends: ['gitmoji'], rules: { - 'scope-case': [0, 'always', 'lower-case'], - 'subject-case': [0, 'always', 'lower-case'], - 'header-match-team-pattern': [2, 'always'], 'type-enum': [ 2, 'always', @@ -59,6 +11,7 @@ const config = { 'build', 'chore', 'config', + 'delete', 'docs', 'feat', 'fix', @@ -66,6 +19,9 @@ const config = { 'refactor', 'remove', 'style', + 'test', + 'update', + 'upgrade', ], ], }, diff --git a/next.config.js b/next.config.js index 8f50446..ab56d60 100644 --- a/next.config.js +++ b/next.config.js @@ -15,6 +15,9 @@ const nextConfig = withBundleAnalyzer({ const commitHash = ChildProcess.execSync('git log --pretty=format:"%h" -n1').toString().trim(); return process.env.GIT_HASH || commitHash; }, + experimental: { + serverComponentsExternalPackages: ['sequelize'], + }, }); module.exports = nextConfig; diff --git a/package.json b/package.json index 2291fee..b1e0b6a 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,9 @@ "lint": "next lint", "prepare": "husky", "commitlint": "commitlint --edit", - "analyze": "cross-env ANALYZE=true next build" + "analyze": "cross-env ANALYZE=true next build", + "seed": "ts-node src/database/migrations/create.ts seed", + "flush": "ts-node src/database/migrations/create.ts flush" }, "dependencies": { "@headlessui/react": "^1.7.18", @@ -46,8 +48,8 @@ "sqlite": "^5.1.1" }, "devDependencies": { - "@commitlint/cli": "^19.0.3", - "@commitlint/config-conventional": "^19.0.3", + "@commitlint/cli": "^19.2.1", + "@commitlint/config-conventional": "^19.1.0", "@icongo/cg": "^1.2.0", "@icongo/fc": "^1.2.0", "@icongo/ic": "^1.2.0", @@ -63,6 +65,9 @@ "@types/node": "^20.11.24", "@types/react": "^18.2.60", "autoprefixer": "^10.0.1", + "colors": "^1.4.0", + "commitlint": "^19.2.1", + "commitlint-config-gitmoji": "^2.3.1", "cross-env": "^7.0.3", "eslint": "^8.57.0", "eslint-config-next": "14.1.0", @@ -72,6 +77,7 @@ "prettier-plugin-tailwindcss": "^0.5.11", "tailwindcss": "^3.3.0", "ts-loader": "^9.5.1", + "ts-node": "^10.9.2", "typescript": "^5.4.3", "webpack": "^5.91.0", "webpack-cli": "^5.1.4" diff --git a/src/database/migrations/create.ts b/src/database/migrations/create.ts index 476e21e..be8b4ed 100644 --- a/src/database/migrations/create.ts +++ b/src/database/migrations/create.ts @@ -4,21 +4,49 @@ */ require('dotenv').config(); -import models from '@/database/models'; +import models from '../models'; +import 'colors'; const database = models(); if (!database) { + console.log('Failed to connect your database'.yellow.underline.bold); process.exit(1); } -database.sequelize - .sync() - .then(() => { - console.log('OK'); - process.exit(); - }) - .catch((error) => { +export const seed = async () => { + try { + await database.sequelize.sync({ force: false }); + console.log('Database seeded'.bgGreen.underline.bold); + process.exit(0); + } catch (error) { + if (error instanceof Error) { + console.log(`Seed database failed: ${error.message}`.red.underline.bold); + } console.error(error); - process.exit(1); - }); + } +}; + +export const flush = async () => { + console.log('Delete all database tables'.red.underline.bold); + try { + await database.user.drop({ cascade: true }); + await database.experience.drop({ cascade: true }); + process.exit(0); + } catch (error) { + let msg; + + if (error instanceof Error) msg = error.message; + else msg = error; + + console.log(`flushing database failed: ${msg}`.red.underline.bold); + console.error(error); + } +}; + +let arg = process.argv[2]; +if (arg === 'seed') { + seed(); +} else if (arg === 'flush') { + flush(); +} diff --git a/src/database/models/experience.ts b/src/database/models/experience.ts new file mode 100644 index 0000000..fe553bc --- /dev/null +++ b/src/database/models/experience.ts @@ -0,0 +1,59 @@ +import { DataTypes } from 'sequelize'; + +export default function model(sequelize) { + const user = sequelize.define( + 'experience', + { + id: { + type: DataTypes.INTEGER, + autoIncrement: true, + primaryKey: true, + }, + since: { + type: DataTypes.DATEONLY, + allowNull: true, + }, + until: { + type: DataTypes.DATEONLY, + allowNull: true, + }, + position: { + type: DataTypes.STRING(128), + allowNull: false, + validate: { + notEmpty: true, + len: [0, 128], + }, + }, + company: { + type: DataTypes.STRING(128), + allowNull: false, + validate: { + notEmpty: true, + len: [0, 128], + }, + }, + description: { + type: DataTypes.TEXT, + allowNull: false, + validate: { + notEmpty: true, + len: [0, 2048], + }, + }, + }, + { + indexes: [ + { + fields: ['since'], + }, + { + fields: ['until'], + }, + ], + timestamps: true, + }, + ); + + return user; +} diff --git a/src/database/models/index.ts b/src/database/models/index.ts index a773a29..be1aa9f 100644 --- a/src/database/models/index.ts +++ b/src/database/models/index.ts @@ -6,13 +6,14 @@ import * as pg from 'pg'; import * as mysql from 'mysql2'; import * as mariadb from 'mariadb'; import * as sqlite from 'sqlite'; -import { Sequelize } from 'sequelize'; -import { getConfig } from '@/config'; +import { Dialect, Sequelize } from 'sequelize'; +import { getConfig } from '../../config'; // import models for tables of your database -import userModel from '@/database/models/user'; +import experienceModel from './experience'; +import userModel from './user'; -const dataModels: Array = [userModel]; +const dataModels: Array = [experienceModel, userModel]; const databaseModules = { postgres: pg, @@ -21,7 +22,7 @@ const databaseModules = { sqlite: sqlite, }; -const highlight = require('cli-highlight').highlight; +const highlight: Function = require('cli-highlight').highlight as Function; function models() { const config = getConfig(); @@ -32,10 +33,10 @@ function models() { return null; } - let sequelize = new (Sequelize)(config.database.db, config.database.user, config.database.password, { + let sequelize = new Sequelize(config.database.db, config.database.user, config.database.password, { host: config.database.host, port: config.database.port, - dialect: config.database.dialect, + dialect: config.database.dialect as Dialect, dialectModule: databaseModules[config.database.dialect], dialectOptions: { ssl: config.database.ssl && { @@ -45,7 +46,7 @@ function models() { }, }, logging: config.database.logging - ? (log) => + ? (log: string) => console.log( highlight(log, { language: 'sql', diff --git a/src/database/models/user.ts b/src/database/models/user.ts index 0dac68f..d082289 100644 --- a/src/database/models/user.ts +++ b/src/database/models/user.ts @@ -5,48 +5,23 @@ export default function model(sequelize) { 'user', { id: { - type: DataTypes.BIGINT.UNSIGNED, + type: DataTypes.INTEGER, autoIncrement: true, primaryKey: true, }, - fullName: { - type: DataTypes.STRING(255), - allowNull: true, - validate: { - len: [0, 255], - }, - }, - firstName: { - type: DataTypes.STRING(80), - allowNull: true, - validate: { - len: [0, 80], - }, - }, - lastName: { - type: DataTypes.STRING(175), - allowNull: true, - validate: { - len: [0, 175], - }, - }, - password: { - type: DataTypes.STRING(255), - allowNull: true, + key: { + type: DataTypes.STRING(128), + allowNull: false, validate: { - len: [0, 255], - }, - get() { - return undefined; + notEmpty: true, + len: [0, 128], }, }, - email: { - type: DataTypes.STRING(255), + value: { + type: DataTypes.TEXT, allowNull: false, validate: { - isEmail: true, - notEmpty: true, - len: [0, 255], + len: [0, 1024], }, }, }, @@ -54,53 +29,12 @@ export default function model(sequelize) { indexes: [ { unique: true, - fields: ['email'], - where: { - deletedAt: null, - }, + fields: ['key'], }, ], timestamps: true, }, ); - user.associate = (models) => { - models.user.belongsTo(models.user, { - as: 'createdBy', - }); - - models.user.belongsTo(models.user, { - as: 'updatedBy', - }); - }; - - user.beforeCreate((user, options) => { - user = trimStringFields(user); - user.fullName = buildFullName(user.firstName, user.lastName); - }); - - user.beforeUpdate((user, options) => { - user = trimStringFields(user); - user.fullName = buildFullName(user.firstName, user.lastName); - }); - - return user; -} - -function buildFullName(firstName, lastName) { - if (!firstName && !lastName) { - return null; - } - - return [firstName, lastName].filter(Boolean).join(' ').trim(); -} - -function trimStringFields(user) { - user.email = user.email.trim(); - - user.firstName = user.firstName ? user.firstName.trim() : null; - - user.lastName = user.lastName ? user.lastName.trim() : null; - return user; } diff --git a/tsconfig.json b/tsconfig.json index fe77eef..f80eea1 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -16,13 +16,18 @@ "strict": true, "plugins": [ { - "name": "next", - }, + "name": "next" + } ], "paths": { - "@/*": ["./src/*"], - }, + "@/*": ["./src/*"] + } + }, + "ts-node": { + "compilerOptions": { + "module": "CommonJS" + } }, "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], - "exclude": ["node_modules"], + "exclude": ["node_modules"] } diff --git a/yarn.lock b/yarn.lock index a9f97af..613399e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -48,23 +48,23 @@ dependencies: regenerator-runtime "^0.14.0" -"@commitlint/cli@^19.0.3": - version "19.0.3" - resolved "https://registry.yarnpkg.com/@commitlint/cli/-/cli-19.0.3.tgz#a415069099864b75dc65bb22c703c11a3837f258" - integrity sha512-mGhh/aYPib4Vy4h+AGRloMY+CqkmtdeKPV9poMcZeImF5e3knQ5VYaSeAM0mEzps1dbKsHvABwaDpafLUuM96g== +"@commitlint/cli@^19.2.1": + version "19.2.1" + resolved "https://registry.yarnpkg.com/@commitlint/cli/-/cli-19.2.1.tgz#8f00d27a8b7c7780e75b06fd4658fdc1e9209f1b" + integrity sha512-cbkYUJsLqRomccNxvoJTyv5yn0bSy05BBizVyIcLACkRbVUqYorC351Diw/XFSWC/GtpwiwT2eOvQgFZa374bg== dependencies: "@commitlint/format" "^19.0.3" - "@commitlint/lint" "^19.0.3" - "@commitlint/load" "^19.0.3" - "@commitlint/read" "^19.0.3" + "@commitlint/lint" "^19.1.0" + "@commitlint/load" "^19.2.0" + "@commitlint/read" "^19.2.1" "@commitlint/types" "^19.0.3" execa "^8.0.1" yargs "^17.0.0" -"@commitlint/config-conventional@^19.0.3": - version "19.0.3" - resolved "https://registry.yarnpkg.com/@commitlint/config-conventional/-/config-conventional-19.0.3.tgz#28b8b9e7561366bf663c2955e4fe8e3fd954675d" - integrity sha512-vh0L8XeLaEzTe8VCxSd0gAFvfTK0RFolrzw4o431bIuWJfi/yRCHJlsDwus7wW2eJaFFDR0VFXJyjGyDQhi4vA== +"@commitlint/config-conventional@^19.1.0": + version "19.1.0" + resolved "https://registry.yarnpkg.com/@commitlint/config-conventional/-/config-conventional-19.1.0.tgz#6b4b7938aa3bc308214a683247520f602e55961e" + integrity sha512-KIKD2xrp6Uuk+dcZVj3++MlzIr/Su6zLE8crEDQCZNvWHNQSeeGbzOlNtsR32TUy6H3JbP7nWgduAHCaiGQ6EA== dependencies: "@commitlint/types" "^19.0.3" conventional-changelog-conventionalcommits "^7.0.2" @@ -110,27 +110,27 @@ "@commitlint/types" "^19.0.3" semver "^7.6.0" -"@commitlint/lint@^19.0.3": - version "19.0.3" - resolved "https://registry.yarnpkg.com/@commitlint/lint/-/lint-19.0.3.tgz#7db83188310c1a5fbdffebbb32a35aa1b0aacee3" - integrity sha512-uHPyRqIn57iIplYa5xBr6oNu5aPXKGC4WLeuHfqQHclwIqbJ33g3yA5fIA+/NYnp5ZM2EFiujqHFaVUYj6HlKA== +"@commitlint/lint@^19.1.0": + version "19.1.0" + resolved "https://registry.yarnpkg.com/@commitlint/lint/-/lint-19.1.0.tgz#0f4b26b1452d59a92a28b5fa6de9bdbee18399a1" + integrity sha512-ESjaBmL/9cxm+eePyEr6SFlBUIYlYpI80n+Ltm7IA3MAcrmiP05UMhJdAD66sO8jvo8O4xdGn/1Mt2G5VzfZKw== dependencies: "@commitlint/is-ignored" "^19.0.3" "@commitlint/parse" "^19.0.3" "@commitlint/rules" "^19.0.3" "@commitlint/types" "^19.0.3" -"@commitlint/load@^19.0.3": - version "19.0.3" - resolved "https://registry.yarnpkg.com/@commitlint/load/-/load-19.0.3.tgz#f05ce3830482e3908799f8e6eb202c8d9713efd8" - integrity sha512-18Tk/ZcDFRKIoKfEcl7kC+bYkEQ055iyKmGsYDoYWpKf6FUvBrP9bIWapuy/MB+kYiltmP9ITiUx6UXtqC9IRw== +"@commitlint/load@^19.2.0": + version "19.2.0" + resolved "https://registry.yarnpkg.com/@commitlint/load/-/load-19.2.0.tgz#3ca51fdead4f1e1e09c9c7df343306412b1ef295" + integrity sha512-XvxxLJTKqZojCxaBQ7u92qQLFMMZc4+p9qrIq/9kJDy8DOrEa7P1yx7Tjdc2u2JxIalqT4KOGraVgCE7eCYJyQ== dependencies: "@commitlint/config-validator" "^19.0.3" "@commitlint/execute-rule" "^19.0.0" - "@commitlint/resolve-extends" "^19.0.3" + "@commitlint/resolve-extends" "^19.1.0" "@commitlint/types" "^19.0.3" chalk "^5.3.0" - cosmiconfig "^8.3.6" + cosmiconfig "^9.0.0" cosmiconfig-typescript-loader "^5.0.0" lodash.isplainobject "^4.0.6" lodash.merge "^4.6.2" @@ -150,20 +150,21 @@ conventional-changelog-angular "^7.0.0" conventional-commits-parser "^5.0.0" -"@commitlint/read@^19.0.3": - version "19.0.3" - resolved "https://registry.yarnpkg.com/@commitlint/read/-/read-19.0.3.tgz#b04abaa4daae2ba84f5f77a3800a6c3bc27cd94d" - integrity sha512-b5AflTyAXkUx5qKw4TkjjcOccXZHql3JqMi522knTQktq2AubKXFz60Sws+K4FsefwPws6fGz9mqiI/NvsvxFA== +"@commitlint/read@^19.2.1": + version "19.2.1" + resolved "https://registry.yarnpkg.com/@commitlint/read/-/read-19.2.1.tgz#7296b99c9a989e60e5927fff8388a1dd44299c2f" + integrity sha512-qETc4+PL0EUv7Q36lJbPG+NJiBOGg7SSC7B5BsPWOmei+Dyif80ErfWQ0qXoW9oCh7GTpTNRoaVhiI8RbhuaNw== dependencies: "@commitlint/top-level" "^19.0.0" "@commitlint/types" "^19.0.3" + execa "^8.0.1" git-raw-commits "^4.0.0" minimist "^1.2.8" -"@commitlint/resolve-extends@^19.0.3": - version "19.0.3" - resolved "https://registry.yarnpkg.com/@commitlint/resolve-extends/-/resolve-extends-19.0.3.tgz#04d0e95f9119983765741df603d36f4457f56c13" - integrity sha512-18BKmta8OC8+Ub+Q3QGM9l27VjQaXobloVXOrMvu8CpEwJYv62vC/t7Ka5kJnsW0tU9q1eMqJFZ/nN9T/cOaIA== +"@commitlint/resolve-extends@^19.1.0": + version "19.1.0" + resolved "https://registry.yarnpkg.com/@commitlint/resolve-extends/-/resolve-extends-19.1.0.tgz#fa5b8f921e9c8d76f53624c35bf25b9676bd73fa" + integrity sha512-z2riI+8G3CET5CPgXJPlzftH+RiWYLMYv4C9tSLdLXdr6pBNimSKukYP9MS27ejmscqCTVA4almdLh0ODD2KYg== dependencies: "@commitlint/config-validator" "^19.0.3" "@commitlint/types" "^19.0.3" @@ -195,6 +196,13 @@ dependencies: find-up "^7.0.0" +"@commitlint/types@^17": + version "17.8.1" + resolved "https://registry.yarnpkg.com/@commitlint/types/-/types-17.8.1.tgz#883a0ad35c5206d5fef7bc6ce1bbe648118af44e" + integrity sha512-PXDQXkAmiMEG162Bqdh9ChML/GJZo6vU+7F03ALKDK8zYc6SuAr47LjG7hGYRqUOz+WK0dU7bQ0xzuqFMdxzeQ== + dependencies: + chalk "^4.1.0" + "@commitlint/types@^19.0.3": version "19.0.3" resolved "https://registry.yarnpkg.com/@commitlint/types/-/types-19.0.3.tgz#feff4ecac2b5c359f2a57f9ab094b2ac80ef0266" @@ -203,6 +211,13 @@ "@types/conventional-commits-parser" "^5.0.0" chalk "^5.3.0" +"@cspotcode/source-map-support@^0.8.0": + version "0.8.1" + resolved "https://registry.yarnpkg.com/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz#00629c35a688e05a88b1cda684fb9d5e73f000a1" + integrity sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw== + dependencies: + "@jridgewell/trace-mapping" "0.3.9" + "@discoveryjs/json-ext@0.5.7", "@discoveryjs/json-ext@^0.5.0": version "0.5.7" resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz#1d572bfbbe14b7704e0ba0f39b74815b84870d70" @@ -276,6 +291,26 @@ resolved "https://registry.yarnpkg.com/@floating-ui/utils/-/utils-0.2.1.tgz#16308cea045f0fc777b6ff20a9f25474dd8293d2" integrity sha512-9TANp6GPoMtYzQdt54kfAyMmz1+osLlXdg2ENroU7zzrtflTLrrC/lgrIfaSe+Wu0b89GKccT7vxXA0MoAIO+Q== +"@gitmoji/commit-types@1.1.5": + version "1.1.5" + resolved "https://registry.yarnpkg.com/@gitmoji/commit-types/-/commit-types-1.1.5.tgz#8ca89aeea4e253a3818ac63206b4eb4f2352c4ff" + integrity sha512-8D3FZMRY+gtYpTcHG1SOGmm9CFqxNh6rI9xDoCydxHxnWgqInbdF3nk9gibW5gXA58Hf2cVcJaLEcGOKLRAtmw== + +"@gitmoji/gitmoji-regex@1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@gitmoji/gitmoji-regex/-/gitmoji-regex-1.0.0.tgz#937645b0bb90de70538a172efb5a07b81d6d22fd" + integrity sha512-+BFXxcWCxn0UIYuG1v5n9SfaCCS8tw95j1x3QsTJRdGGiihRyVLTHiu1wrHlzH3z4nYXKjCKiZFTdWwMjRI+gQ== + dependencies: + emoji-regex "^10" + gitmojis "^3" + +"@gitmoji/parser-opts@1.4.0": + version "1.4.0" + resolved "https://registry.yarnpkg.com/@gitmoji/parser-opts/-/parser-opts-1.4.0.tgz#16891bceab2dd2c44c7fd930413e959a033c27f9" + integrity sha512-zzmx/vtpdB/ijjUm7u9OzHNCXWKpSbzVEgVzOzhilMgoTBlUDyInZFUtiCTV+Wf4oCP9nxGa/kQGQFfN+XLH1g== + dependencies: + "@gitmoji/gitmoji-regex" "1.0.0" + "@headlessui/react@^1.7.18": version "1.7.18" resolved "https://registry.yarnpkg.com/@headlessui/react/-/react-1.7.18.tgz#30af4634d2215b2ca1aa29d07f33d02bea82d9d7" @@ -393,7 +428,7 @@ "@jridgewell/sourcemap-codec" "^1.4.10" "@jridgewell/trace-mapping" "^0.3.24" -"@jridgewell/resolve-uri@^3.1.0": +"@jridgewell/resolve-uri@^3.0.3", "@jridgewell/resolve-uri@^3.1.0": version "3.1.2" resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6" integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw== @@ -421,6 +456,14 @@ resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== +"@jridgewell/trace-mapping@0.3.9": + version "0.3.9" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz#6534fd5933a53ba7cbf3a17615e273a0d1273ff9" + integrity sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ== + dependencies: + "@jridgewell/resolve-uri" "^3.0.3" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/trace-mapping@^0.3.20", "@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25": version "0.3.25" resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz#15f190e98895f3fc23276ee14bc76b675c2e50f0" @@ -588,6 +631,26 @@ resolved "https://registry.yarnpkg.com/@tanstack/virtual-core/-/virtual-core-3.1.3.tgz#77ced625f19ec9350f6e460f142b3be9bff03866" integrity sha512-Y5B4EYyv1j9V8LzeAoOVeTg0LI7Fo5InYKgAjkY1Pu9GjtUwX/EKxNcU7ng3sKr99WEf+bPTcktAeybyMOYo+g== +"@tsconfig/node10@^1.0.7": + version "1.0.11" + resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.11.tgz#6ee46400685f130e278128c7b38b7e031ff5b2f2" + integrity sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw== + +"@tsconfig/node12@^1.0.7": + version "1.0.11" + resolved "https://registry.yarnpkg.com/@tsconfig/node12/-/node12-1.0.11.tgz#ee3def1f27d9ed66dac6e46a295cffb0152e058d" + integrity sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag== + +"@tsconfig/node14@^1.0.0": + version "1.0.3" + resolved "https://registry.yarnpkg.com/@tsconfig/node14/-/node14-1.0.3.tgz#e4386316284f00b98435bf40f72f75a09dabf6c1" + integrity sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow== + +"@tsconfig/node16@^1.0.2": + version "1.0.4" + resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.4.tgz#0b92dcc0cc1c81f6f306a381f28e31b1a56536e9" + integrity sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA== + "@tsparticles/basic@^3.3.0": version "3.3.0" resolved "https://registry.yarnpkg.com/@tsparticles/basic/-/basic-3.3.0.tgz#b25c60e61da6b8238b73526a45b0f3be0ef44322" @@ -1201,12 +1264,12 @@ acorn-jsx@^5.3.2: resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== -acorn-walk@^8.0.0: +acorn-walk@^8.0.0, acorn-walk@^8.1.1: version "8.3.2" resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-8.3.2.tgz#7703af9415f1b6db9315d6895503862e231d34aa" integrity sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A== -acorn@^8.0.4, acorn@^8.7.1, acorn@^8.8.2, acorn@^8.9.0: +acorn@^8.0.4, acorn@^8.4.1, acorn@^8.7.1, acorn@^8.8.2, acorn@^8.9.0: version "8.11.3" resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.11.3.tgz#71e0b14e13a4ec160724b38fb7b0f233b1b81d7a" integrity sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg== @@ -1298,6 +1361,11 @@ are-we-there-yet@^2.0.0: delegates "^1.0.0" readable-stream "^3.6.0" +arg@^4.1.0: + version "4.1.3" + resolved "https://registry.yarnpkg.com/arg/-/arg-4.1.3.tgz#269fc7ad5b8e42cb63c896d5666017261c144089" + integrity sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA== + arg@^5.0.2: version "5.0.2" resolved "https://registry.yarnpkg.com/arg/-/arg-5.0.2.tgz#c81433cc427c92c4dcf4865142dbca6f15acd59c" @@ -1698,6 +1766,11 @@ colorette@^2.0.14: resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a" integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w== +colors@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78" + integrity sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA== + commander@^10.0.0, commander@^10.0.1: version "10.0.1" resolved "https://registry.yarnpkg.com/commander/-/commander-10.0.1.tgz#881ee46b4f77d1c1dccc5823433aa39b022cbe06" @@ -1718,6 +1791,33 @@ commander@^7.2.0: resolved "https://registry.yarnpkg.com/commander/-/commander-7.2.0.tgz#a36cb57d0b501ce108e4d20559a150a391d97ab7" integrity sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw== +commitlint-config-gitmoji@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/commitlint-config-gitmoji/-/commitlint-config-gitmoji-2.3.1.tgz#754a2167068c6a80644969b379341e8a9f041aca" + integrity sha512-T15ssbsyNc6szHlnGWo0/xvIA1mObqM70E9TwKNVTpksxhm+OdFht8hvDdKJAVi4nlZX5tcfTeILOi7SHBGH3w== + dependencies: + "@commitlint/types" "^17" + "@gitmoji/commit-types" "1.1.5" + "@gitmoji/parser-opts" "1.4.0" + commitlint-plugin-gitmoji "2.2.6" + +commitlint-plugin-gitmoji@2.2.6: + version "2.2.6" + resolved "https://registry.yarnpkg.com/commitlint-plugin-gitmoji/-/commitlint-plugin-gitmoji-2.2.6.tgz#cae0b406646c3965b96e217b5e8a33d1de5fc7e6" + integrity sha512-oKHPHeNXby0Ix0ZbHVSK5ZyPx1V4fyBjLOy93cYwXhOEPXe36nkDc/HDPFfQpzx1vz39277TaP9LScbqTbscfQ== + dependencies: + "@commitlint/types" "^17" + "@gitmoji/gitmoji-regex" "1.0.0" + gitmojis "^3" + +commitlint@^19.2.1: + version "19.2.1" + resolved "https://registry.yarnpkg.com/commitlint/-/commitlint-19.2.1.tgz#c6d490e9f4987ea1a2fb7592957aff3eabdc3df2" + integrity sha512-avW7E38gbdbUK7NIi/7gfkaKwlw8tjXy2HlmEdBS9A9+HeoHV1o/i96Sc9qjX0rKyPzKEsLi6cP3Wt3xM0kjEA== + dependencies: + "@commitlint/cli" "^19.2.1" + "@commitlint/types" "^19.0.3" + compare-func@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/compare-func/-/compare-func-2.0.0.tgz#fb65e75edbddfd2e568554e8b5b05fff7a51fcb3" @@ -1775,15 +1875,20 @@ cosmiconfig-typescript-loader@^5.0.0: dependencies: jiti "^1.19.1" -cosmiconfig@^8.3.6: - version "8.3.6" - resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-8.3.6.tgz#060a2b871d66dba6c8538ea1118ba1ac16f5fae3" - integrity sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA== +cosmiconfig@^9.0.0: + version "9.0.0" + resolved "https://registry.yarnpkg.com/cosmiconfig/-/cosmiconfig-9.0.0.tgz#34c3fc58287b915f3ae905ab6dc3de258b55ad9d" + integrity sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg== dependencies: + env-paths "^2.2.1" import-fresh "^3.3.0" js-yaml "^4.1.0" parse-json "^5.2.0" - path-type "^4.0.0" + +create-require@^1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333" + integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ== cross-env@^7.0.3: version "7.0.3" @@ -1901,6 +2006,11 @@ didyoumean@^1.2.2: resolved "https://registry.yarnpkg.com/didyoumean/-/didyoumean-1.2.2.tgz#989346ffe9e839b4555ecf5666edea0d3e8ad037" integrity sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw== +diff@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" + integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== + dir-glob@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" @@ -1969,6 +2079,11 @@ electron-to-chromium@^1.4.668: resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.687.tgz#8b80da91848c13a90802f840c7de96c8558fef52" integrity sha512-Ic85cOuXSP6h7KM0AIJ2hpJ98Bo4hyTUjc4yjMbkvD+8yTxEhfK9+8exT2KKYsSjnCn2tGsKVSZwE7ZgTORQCw== +emoji-regex@^10: + version "10.3.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-10.3.0.tgz#76998b9268409eb3dae3de989254d456e70cfe23" + integrity sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw== + emoji-regex@^8.0.0: version "8.0.0" resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" @@ -1995,6 +2110,11 @@ enhanced-resolve@^5.12.0: graceful-fs "^4.2.4" tapable "^2.2.0" +env-paths@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.1.tgz#420399d416ce1fbe9bc0a07c62fa68d67fd0f8f2" + integrity sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A== + envinfo@^7.7.3: version "7.11.1" resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.11.1.tgz#2ffef77591057081b0129a8fd8cf6118da1b94e1" @@ -2689,6 +2809,11 @@ git-raw-commits@^4.0.0: meow "^12.0.1" split2 "^4.0.0" +gitmojis@^3: + version "3.14.0" + resolved "https://registry.yarnpkg.com/gitmojis/-/gitmojis-3.14.0.tgz#b6756f46d5b21204840ce4eadd51a5fbe07032a1" + integrity sha512-wPdXxKWfp2dJE6/ch8TDJjYdXvr+4uV67AEcbImj5QC4DmP6Byje1goJL8WgMPFnLY63eadK6oxtnSjdRFzS6g== + glob-parent@^5.1.2, glob-parent@~5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" @@ -3470,6 +3595,11 @@ make-dir@^3.1.0: dependencies: semver "^6.0.0" +make-error@^1.1.1: + version "1.3.6" + resolved "https://registry.yarnpkg.com/make-error/-/make-error-1.3.6.tgz#2eb2e37ea9b67c4891f684a1394799af484cf7a2" + integrity sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw== + mariadb@^3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/mariadb/-/mariadb-3.3.0.tgz#e9c844965d616a1842fa05221206e8678842c09e" @@ -4933,6 +5063,25 @@ ts-loader@^9.5.1: semver "^7.3.4" source-map "^0.7.4" +ts-node@^10.9.2: + version "10.9.2" + resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-10.9.2.tgz#70f021c9e185bccdca820e26dc413805c101c71f" + integrity sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ== + dependencies: + "@cspotcode/source-map-support" "^0.8.0" + "@tsconfig/node10" "^1.0.7" + "@tsconfig/node12" "^1.0.7" + "@tsconfig/node14" "^1.0.0" + "@tsconfig/node16" "^1.0.2" + acorn "^8.4.1" + acorn-walk "^8.1.1" + arg "^4.1.0" + create-require "^1.1.0" + diff "^4.0.1" + make-error "^1.1.1" + v8-compile-cache-lib "^3.0.1" + yn "3.1.1" + tsconfig-paths@^3.15.0: version "3.15.0" resolved "https://registry.yarnpkg.com/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz#5299ec605e55b1abb23ec939ef15edaf483070d4" @@ -5076,6 +5225,11 @@ uuid@^8.3.2: resolved "https://registry.yarnpkg.com/uuid/-/uuid-8.3.2.tgz#80d5b5ced271bb9af6c445f21a1a04c606cefbe2" integrity sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg== +v8-compile-cache-lib@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz#6336e8d71965cb3d35a1bbb7868445a7c05264bf" + integrity sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg== + validator@^13.9.0: version "13.11.0" resolved "https://registry.yarnpkg.com/validator/-/validator-13.11.0.tgz#23ab3fd59290c61248364eabf4067f04955fbb1b" @@ -5344,6 +5498,11 @@ yargs@^17.0.0: y18n "^5.0.5" yargs-parser "^21.1.1" +yn@3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" + integrity sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q== + yocto-queue@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" From ee13d73833eecd4361744ea83cb7c10912477ab2 Mon Sep 17 00:00:00 2001 From: james-gates-0212 Date: Mon, 8 Apr 2024 01:59:34 -0700 Subject: [PATCH 07/16] :triangular_flag_on_post: add: base structure for user information --- src/admin/common/MyTable.tsx | 20 ++++-- src/app/admin/user/page.tsx | 70 +++++++++++---------- src/app/api/user/route.ts | 9 +++ src/components/admin/NavBar.tsx | 29 ++++++--- src/components/clients/partials/NavBar.tsx | 29 ++++++--- src/database/repositories/baseRepository.ts | 3 + src/database/repositories/userRepository.ts | 27 ++++++++ src/i18n/en.ts | 12 ++-- src/i18n/index.ts | 19 +++--- src/services/baseService.ts | 13 ++++ src/services/userService.ts | 16 +++++ 11 files changed, 182 insertions(+), 65 deletions(-) create mode 100644 src/app/api/user/route.ts create mode 100644 src/database/repositories/baseRepository.ts create mode 100644 src/database/repositories/userRepository.ts create mode 100644 src/services/baseService.ts create mode 100644 src/services/userService.ts diff --git a/src/admin/common/MyTable.tsx b/src/admin/common/MyTable.tsx index 057c011..7c5e6c8 100644 --- a/src/admin/common/MyTable.tsx +++ b/src/admin/common/MyTable.tsx @@ -1,5 +1,6 @@ 'use client'; +import { classNames } from '@/components/Commons'; import { i18n } from '@/i18n'; import { Checkbox, Table } from 'flowbite-react'; @@ -8,6 +9,8 @@ interface IHeader { label: string; onClick?: Function; order?: 'asc' | 'desc' | 'none'; + width?: number; + classes?: string[]; } interface ICell { @@ -54,24 +57,33 @@ export default function MyTable(props: ITables) { {hasCheckBox && ( - + )} - {(headers || []).map(({ key, label, onClick, order }: IHeader) => ( + {(headers || []).map(({ key, label, onClick, order, width, classes }: IHeader) => ( { evt.stopPropagation(); evt.preventDefault(); onClick && onClick.call(null, key, order); }} + style={{ width }} > {i18n(label)} ))} + {(!rows || rows.length === 0) && ( + + + {i18n('table.noRecords')} + + + )} {(rows || []).map((row: IRow, i) => ( {hasCheckBox && ( @@ -79,8 +91,8 @@ export default function MyTable(props: ITables) { )} - {headerKeys.map((key) => ( - + {headerKeys.map((key, index) => ( + ))} diff --git a/src/app/admin/user/page.tsx b/src/app/admin/user/page.tsx index 8939bb2..eb80511 100644 --- a/src/app/admin/user/page.tsx +++ b/src/app/admin/user/page.tsx @@ -3,8 +3,28 @@ import { i18n } from '@/i18n'; import BreadCrumb from '@/admin/common/BreadCrumb'; import MyTable from '@/admin/common/MyTable'; +import { useEffect, useState } from 'react'; +import { Spinner } from 'flowbite-react'; export default function Page() { + const [loading, setLoading] = useState(false); + const [data, setData] = useState([]); + useEffect(() => { + setLoading(true); + + fetch('/api/user', { + method: 'POST', + }) + .then(async (response) => { + const { rows } = await response.json(); + setData(rows); + }) + .catch((e) => { + console.error(e); + }) + .finally(() => setLoading(false)); + }, []); + return (
- ( - {value} - ), - }, - lastName: 'Gates', - email: 'pop.runner88@outlook.com', - }, - ]} - hasCheckBox - hoverable - /> + {loading ? ( +
+ +
+ ) : ( + + )}
); } diff --git a/src/app/api/user/route.ts b/src/app/api/user/route.ts new file mode 100644 index 0000000..6ba38d1 --- /dev/null +++ b/src/app/api/user/route.ts @@ -0,0 +1,9 @@ +import UserService from '@/services/userService'; +import { NextRequest } from 'next/server'; + +export async function POST(request: NextRequest) { + const service: UserService = new UserService(); + await service.init(); + const { count, rows } = await service.getAll(); + return Response.json({ count, rows }); +} diff --git a/src/components/admin/NavBar.tsx b/src/components/admin/NavBar.tsx index 9af52be..905814d 100644 --- a/src/components/admin/NavBar.tsx +++ b/src/components/admin/NavBar.tsx @@ -1,6 +1,6 @@ 'use client'; -import { DarkThemeToggle, Navbar, NavbarBrand, NavbarCollapse, NavbarLink, NavbarToggle } from 'flowbite-react'; +import { Avatar, DarkThemeToggle, Dropdown, Navbar } from 'flowbite-react'; import { i18n } from '@/i18n'; import { usePathname } from 'next/navigation'; import Link from 'next/link'; @@ -10,18 +10,29 @@ export default function NavBar() { return ( - + {i18n('app.title')} - -
+ +
- + } arrowIcon={false} inline> + + James Gates + james.gates.0212@gmail.com + + + User + + + Sign out + +
- - + + {i18n('entities.user.menu')} - - + + ); } diff --git a/src/components/clients/partials/NavBar.tsx b/src/components/clients/partials/NavBar.tsx index 5ea1326..ea2e756 100644 --- a/src/components/clients/partials/NavBar.tsx +++ b/src/components/clients/partials/NavBar.tsx @@ -1,6 +1,6 @@ 'use client'; -import { DarkThemeToggle, Navbar, NavbarBrand, NavbarCollapse, NavbarLink, NavbarToggle } from 'flowbite-react'; +import { Avatar, DarkThemeToggle, Dropdown, Navbar } from 'flowbite-react'; import { i18n } from '@/i18n'; import { usePathname } from 'next/navigation'; import { VLLinkedin } from '@icongo/vl'; @@ -12,21 +12,32 @@ export default function NavBar() { return ( - + {i18n('app.title')} - -
+ +
- + } arrowIcon={false} inline> + + James Gates + james.gates.0212@gmail.com + + + User + + + Sign out + +
- + {NavItems.map((item) => ( - + {item.name} - + ))} - + ); } diff --git a/src/database/repositories/baseRepository.ts b/src/database/repositories/baseRepository.ts new file mode 100644 index 0000000..e423e71 --- /dev/null +++ b/src/database/repositories/baseRepository.ts @@ -0,0 +1,3 @@ +export default class BaseRepository { + constructor() {} +} diff --git a/src/database/repositories/userRepository.ts b/src/database/repositories/userRepository.ts new file mode 100644 index 0000000..9f992fe --- /dev/null +++ b/src/database/repositories/userRepository.ts @@ -0,0 +1,27 @@ +import { IRepositoryOptions } from '@/database/repositories/IRepositoryOptions'; +import BaseRepository from '@/database/repositories/baseRepository'; +import SequelizeRepository from '@/database/repositories/sequelizeRepostiory'; +import { Op } from 'sequelize'; + +export default class UserRepository extends BaseRepository { + constructor() { + super(); + } + + static async findAndCountAll({ filter, limit = 0, offset = 0, orderBy = '' }, options: IRepositoryOptions) { + let whereAnd: Array = []; + let include: any = []; + + const where = { [Op.and]: whereAnd }; + + let { rows, count } = await options.database.user.findAndCountAll({ + where, + include, + limit: limit ? Number(limit) : undefined, + offset: offset ? Number(offset) : undefined, + order: orderBy ? [orderBy.split('_')] : [['id', 'desc']], + }); + + return { rows, count }; + } +} diff --git a/src/i18n/en.ts b/src/i18n/en.ts index 6c8961f..ff5c68b 100644 --- a/src/i18n/en.ts +++ b/src/i18n/en.ts @@ -53,16 +53,20 @@ const en = { }, }, + table: { + noRecords: 'No records', + }, + entities: { user: { title: 'User', menu: 'User', fields: { id: 'ID', - fullName: 'Full Name', - firstName: 'First Name', - lastName: 'Last Name', - email: 'Email', + key: 'Key', + value: 'Value', + createdAt: 'Created At', + updatedAt: 'Updated At', }, }, }, diff --git a/src/i18n/index.ts b/src/i18n/index.ts index 750c99d..0e9e11c 100644 --- a/src/i18n/index.ts +++ b/src/i18n/index.ts @@ -13,20 +13,25 @@ const defaultLanguage = 'en'; /** * Replaces the parameters of a message with the args. */ -function format(message, args) { - if (!message) { - return null; +function format(message?: string, args?: string[]) { + if (!message || !args) { + return message || ''; } - return message.replace(/{(\d+)}/g, function (match, number) { - return typeof args[number] != 'undefined' ? args[number] : match; + return message.replace(/{(\d+)}/g, function (match, number: string) { + const index = parseInt(number); + if (index >= 0 && index < args.length) { + return args[index]; + } else { + return match; + } }); } /** * Checks if the key exists on the language. */ -export const i18nExists = (key) => { +export const i18nExists = (key: string) => { const dictionary = languages[defaultLanguage]; const message = _get(dictionary, key); return Boolean(message); @@ -35,7 +40,7 @@ export const i18nExists = (key) => { /** * Returns the translation based on the key. */ -export const i18n = (key, ...args) => { +export const i18n = (key: string, ...args: string[]) => { const dictionary = languages[defaultLanguage]; const message = _get(dictionary, key); diff --git a/src/services/baseService.ts b/src/services/baseService.ts new file mode 100644 index 0000000..4dc5de6 --- /dev/null +++ b/src/services/baseService.ts @@ -0,0 +1,13 @@ +import databaseInit from '@/database/databaseConnection'; + +export default class BaseService { + database; + + constructor() { + this.database = null; + } + + async init() { + this.database = await databaseInit(); + } +} diff --git a/src/services/userService.ts b/src/services/userService.ts new file mode 100644 index 0000000..ddf4fee --- /dev/null +++ b/src/services/userService.ts @@ -0,0 +1,16 @@ +import SequelizeRepository from '@/database/repositories/sequelizeRepostiory'; +import UserRepository from '@/database/repositories/userRepository'; +import BaseService from '@/services/baseService'; + +export default class UserService extends BaseService { + constructor() { + super(); + } + + async getAll() { + return await UserRepository.findAndCountAll( + { filter: '', limit: undefined, offset: undefined }, + { language: 'en', currentUser: null, database: this.database, transaction: null }, + ); + } +} From 9d4f4df38c65b4b740f56833fed88aba9cbb9c0a Mon Sep 17 00:00:00 2001 From: james-gates-0212 Date: Mon, 8 Apr 2024 05:39:04 -0700 Subject: [PATCH 08/16] :heavy_plus_sign: adopt: moment --- package.json | 1 + yarn.lock | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index b1e0b6a..212a181 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,7 @@ "flowbite-react": "^0.7.5", "lodash": "^4.17.21", "mariadb": "^3.3.0", + "moment": "^2.30.1", "mysql2": "^3.9.2", "next": "^14.1.0", "pg": "^8.11.3", diff --git a/yarn.lock b/yarn.lock index 613399e..af94952 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3733,7 +3733,7 @@ moment-timezone@^0.5.43: dependencies: moment "^2.29.4" -moment@^2.29.4: +moment@^2.29.4, moment@^2.30.1: version "2.30.1" resolved "https://registry.yarnpkg.com/moment/-/moment-2.30.1.tgz#f8c91c07b7a786e30c59926df530b4eac96974ae" integrity sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how== From 4f6ac2ea70e3d555423cfde8a68d0996ef76b448 Mon Sep 17 00:00:00 2001 From: james-gates-0212 Date: Mon, 8 Apr 2024 05:41:18 -0700 Subject: [PATCH 09/16] :art: upgrade: breadcrumb, mytable --- src/admin/common/BreadCrumb.tsx | 2 +- src/admin/common/MyTable.tsx | 13 ++++++++++--- src/components/Commons.tsx | 4 +++- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/src/admin/common/BreadCrumb.tsx b/src/admin/common/BreadCrumb.tsx index 8f17bc2..25beb44 100644 --- a/src/admin/common/BreadCrumb.tsx +++ b/src/admin/common/BreadCrumb.tsx @@ -8,7 +8,7 @@ interface IBreadCrumbItem { export default function BreadCrumb({ links }: { links: Array }) { return ( - + {(links || []).map((link: IBreadCrumbItem, i) => ( {link.name} diff --git a/src/admin/common/MyTable.tsx b/src/admin/common/MyTable.tsx index 7c5e6c8..39e7a82 100644 --- a/src/admin/common/MyTable.tsx +++ b/src/admin/common/MyTable.tsx @@ -3,6 +3,7 @@ import { classNames } from '@/components/Commons'; import { i18n } from '@/i18n'; import { Checkbox, Table } from 'flowbite-react'; +import moment from 'moment'; interface IHeader { key: string; @@ -11,6 +12,9 @@ interface IHeader { order?: 'asc' | 'desc' | 'none'; width?: number; classes?: string[]; + format?: { + moment?: string; + }; } interface ICell { @@ -35,10 +39,13 @@ export default function MyTable(props: ITables) { const headerKeys = (headers || []).map((header: IHeader) => header.key); - const RenderCell = ({ props }: { props: ICell | number | string }) => { + const RenderCell = ({ props, header }: { props: ICell | number | string; header: IHeader }) => { const { value, onClick, render } = typeof props === 'object' ? props : { value: props, onClick: undefined, render: undefined }; - const content = (render && render.call(null, value)) || value; + let content = (render && render.call(null, value)) || value; + if (header.format?.moment) { + content = moment(content).format(header.format.moment); + } return ( { @@ -93,7 +100,7 @@ export default function MyTable(props: ITables) { )} {headerKeys.map((key, index) => ( - + ))} diff --git a/src/components/Commons.tsx b/src/components/Commons.tsx index 36ea49b..89ebdc9 100644 --- a/src/components/Commons.tsx +++ b/src/components/Commons.tsx @@ -1,3 +1,5 @@ export function classNames(...classes) { - return classes.filter(Boolean).join(' '); + return Array.from(new Set(classes.filter(Boolean))).join(' '); } + +export const DEFAULT_MOMENT_FORMAT = 'YYYY-MM-DD HH:mm'; From aa1c18b6a774b6ba0fab79bd55857e1b33000ff3 Mon Sep 17 00:00:00 2001 From: james-gates-0212 Date: Mon, 8 Apr 2024 05:42:03 -0700 Subject: [PATCH 10/16] :triangular_flag_on_post: add: new user information --- next.config.js | 2 - src/app/admin/user/page.tsx | 49 ++++++++++++---- src/app/api/user/[id]/route.ts | 23 ++++++++ src/components/admin/user/Modal.tsx | 64 +++++++++++++++++++++ src/database/repositories/userRepository.ts | 43 +++++++++++++- src/i18n/en.ts | 4 ++ src/services/baseService.ts | 7 +++ src/services/userService.ts | 26 +++++++-- 8 files changed, 199 insertions(+), 19 deletions(-) create mode 100644 src/app/api/user/[id]/route.ts create mode 100644 src/components/admin/user/Modal.tsx diff --git a/next.config.js b/next.config.js index ab56d60..6e74678 100644 --- a/next.config.js +++ b/next.config.js @@ -7,8 +7,6 @@ const withBundleAnalyzer = NextBundleAnalyzer({ }); const nextConfig = withBundleAnalyzer({ - output: 'export', - images: { unoptimized: process.env.NODE_ENV === 'development' }, basePath: process.env.BASE_PATH || '', generateBuildId: async () => { // This could be anything, using the latest git hash diff --git a/src/app/admin/user/page.tsx b/src/app/admin/user/page.tsx index eb80511..5e9c0f8 100644 --- a/src/app/admin/user/page.tsx +++ b/src/app/admin/user/page.tsx @@ -1,14 +1,19 @@ 'use client'; +import { Button, Spinner } from 'flowbite-react'; +import { DEFAULT_MOMENT_FORMAT } from '@/components/Commons'; import { i18n } from '@/i18n'; +import { useEffect, useState } from 'react'; import BreadCrumb from '@/admin/common/BreadCrumb'; import MyTable from '@/admin/common/MyTable'; -import { useEffect, useState } from 'react'; -import { Spinner } from 'flowbite-react'; +import UserInfoModal from '@/components/admin/user/Modal'; export default function Page() { const [loading, setLoading] = useState(false); const [data, setData] = useState([]); + + const [modal, setModal] = useState(null); + useEffect(() => { setLoading(true); @@ -26,13 +31,20 @@ export default function Page() { }, []); return ( -
- +
+
+ +
+ +
+
{loading ? (
@@ -43,14 +55,27 @@ export default function Page() { { key: 'id', label: 'entities.user.fields.id', classes: ['text-right'], width: 150 }, { key: 'key', label: 'entities.user.fields.key', width: 300 }, { key: 'value', label: 'entities.user.fields.value' }, - { key: 'createdAt', label: 'entities.user.fields.createdAt', width: 0 }, - { key: 'updatedAt', label: 'entities.user.fields.updatedAt', width: 0 }, + { + key: 'createdAt', + label: 'entities.user.fields.createdAt', + classes: ['whitespace-nowrap', 'text-center'], + width: 0, + format: { moment: DEFAULT_MOMENT_FORMAT }, + }, + { + key: 'updatedAt', + label: 'entities.user.fields.updatedAt', + classes: ['whitespace-nowrap', 'text-center'], + width: 0, + format: { moment: DEFAULT_MOMENT_FORMAT }, + }, ]} rows={data} hasCheckBox hoverable /> )} -
+ {modal && setModal(null)} />} +
); } diff --git a/src/app/api/user/[id]/route.ts b/src/app/api/user/[id]/route.ts new file mode 100644 index 0000000..afad763 --- /dev/null +++ b/src/app/api/user/[id]/route.ts @@ -0,0 +1,23 @@ +import UserService from '@/services/userService'; +import { NextRequest } from 'next/server'; + +export async function POST(request: NextRequest, { params }: { params: { id: string } }) { + try { + const service: UserService = new UserService(); + await service.init(); + + const data = await request.json(); + + const { id } = params; + + const recId = parseInt(id) || 0; + + if (recId <= 0) { + await service.create(data); + } + + return Response.json({}); + } catch (error) { + return Response.json(error); + } +} diff --git a/src/components/admin/user/Modal.tsx b/src/components/admin/user/Modal.tsx new file mode 100644 index 0000000..d9fb62c --- /dev/null +++ b/src/components/admin/user/Modal.tsx @@ -0,0 +1,64 @@ +'use client'; + +import { Button, Label, Modal, TextInput } from 'flowbite-react'; +import { useRef, useState } from 'react'; + +export default function UserInfoModal(props) { + const [loading, setLoading] = useState(false); + const { onClose } = props; + const keyInputRef = useRef(null); + const valueInputRef = useRef(null); + + const onSave = () => { + setLoading(true); + + fetch('/api/user/-1', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + key: keyInputRef.current?.value, + value: valueInputRef.current?.value, + }), + }) + .then(async (response) => { + setLoading(false); + onClose(); + }) + .catch((e) => { + setLoading(false); + console.error(e); + }); + }; + + return ( + !loading && onClose()} initialFocus={keyInputRef}> + + +
+
+
+
+ +
+
+
+
+ +
+
+
+ + + + +
+ ); +} diff --git a/src/database/repositories/userRepository.ts b/src/database/repositories/userRepository.ts index 9f992fe..17f9ce8 100644 --- a/src/database/repositories/userRepository.ts +++ b/src/database/repositories/userRepository.ts @@ -1,7 +1,8 @@ import { IRepositoryOptions } from '@/database/repositories/IRepositoryOptions'; +import { Op } from 'sequelize'; import BaseRepository from '@/database/repositories/baseRepository'; +import Error404 from '@/errors/Error404'; import SequelizeRepository from '@/database/repositories/sequelizeRepostiory'; -import { Op } from 'sequelize'; export default class UserRepository extends BaseRepository { constructor() { @@ -24,4 +25,44 @@ export default class UserRepository extends BaseRepository { return { rows, count }; } + + static async findById(id, options: IRepositoryOptions) { + const transaction = SequelizeRepository.getTransaction(options); + + const record = await options.database.user.findOne({ + where: { + id, + }, + transaction, + }); + + if (!record) { + throw new Error404(); + } + + return this._fillWithRelationsAndFiles(record, options, false); + } + + static async create(data, options: IRepositoryOptions) { + const transaction = SequelizeRepository.getTransaction(options); + const record = await options.database.user.create(data, { + transaction, + }); + + return this.findById(record.id, options); + } + + static async _fillWithRelationsAndFiles(record, options: IRepositoryOptions, metaOnly = true) { + if (!record) { + return record; + } + + const output = record.get({ plain: true }); + + if (metaOnly) { + return output; + } + + return output; + } } diff --git a/src/i18n/en.ts b/src/i18n/en.ts index ff5c68b..44780db 100644 --- a/src/i18n/en.ts +++ b/src/i18n/en.ts @@ -57,6 +57,10 @@ const en = { noRecords: 'No records', }, + common: { + addNew: 'Add new', + }, + entities: { user: { title: 'User', diff --git a/src/services/baseService.ts b/src/services/baseService.ts index 4dc5de6..802b6f2 100644 --- a/src/services/baseService.ts +++ b/src/services/baseService.ts @@ -2,6 +2,7 @@ import databaseInit from '@/database/databaseConnection'; export default class BaseService { database; + options; constructor() { this.database = null; @@ -9,5 +10,11 @@ export default class BaseService { async init() { this.database = await databaseInit(); + this.options = { + language: 'en', + database: this.database, + currentUser: null, + transaction: null, + }; } } diff --git a/src/services/userService.ts b/src/services/userService.ts index ddf4fee..1632d03 100644 --- a/src/services/userService.ts +++ b/src/services/userService.ts @@ -8,9 +8,27 @@ export default class UserService extends BaseService { } async getAll() { - return await UserRepository.findAndCountAll( - { filter: '', limit: undefined, offset: undefined }, - { language: 'en', currentUser: null, database: this.database, transaction: null }, - ); + return await UserRepository.findAndCountAll({ filter: '', limit: undefined, offset: undefined }, this.options); + } + + async create(data) { + const transaction = await SequelizeRepository.createTransaction(this.database); + + try { + const record = await UserRepository.create(data, { + ...this.options, + transaction, + }); + + await SequelizeRepository.commitTransaction(transaction); + + return record; + } catch (error) { + await SequelizeRepository.rollbackTransaction(transaction); + + SequelizeRepository.handleUniqueFieldError(error, this.options.language, 'user'); + + throw error; + } } } From f8cc12430c40e1b86f9153c91ebb192e68df138d Mon Sep 17 00:00:00 2001 From: james-gates-0212 Date: Mon, 8 Apr 2024 09:05:40 -0700 Subject: [PATCH 11/16] :art: fix: my table cell for render --- src/admin/common/MyTable.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/admin/common/MyTable.tsx b/src/admin/common/MyTable.tsx index 39e7a82..87069e2 100644 --- a/src/admin/common/MyTable.tsx +++ b/src/admin/common/MyTable.tsx @@ -15,6 +15,7 @@ interface IHeader { format?: { moment?: string; }; + render?: Function; } interface ICell { @@ -41,7 +42,7 @@ export default function MyTable(props: ITables) { const RenderCell = ({ props, header }: { props: ICell | number | string; header: IHeader }) => { const { value, onClick, render } = - typeof props === 'object' ? props : { value: props, onClick: undefined, render: undefined }; + typeof props === 'object' ? props : { value: props, onClick: header.onClick, render: header.render }; let content = (render && render.call(null, value)) || value; if (header.format?.moment) { content = moment(content).format(header.format.moment); From d6bc5ffc36a9f12615b11c0b4c9d1f7963718b86 Mon Sep 17 00:00:00 2001 From: james-gates-0212 Date: Mon, 8 Apr 2024 09:06:10 -0700 Subject: [PATCH 12/16] :triangular_flag_on_post: add: edit & update for user information --- src/app/admin/user/page.tsx | 127 +++++++++++++------- src/app/api/user/[id]/route.ts | 23 ++++ src/components/admin/user/Modal.tsx | 79 ++++++++---- src/database/repositories/userRepository.ts | 25 ++++ src/services/userService.ts | 25 ++++ 5 files changed, 211 insertions(+), 68 deletions(-) diff --git a/src/app/admin/user/page.tsx b/src/app/admin/user/page.tsx index 5e9c0f8..6e447c5 100644 --- a/src/app/admin/user/page.tsx +++ b/src/app/admin/user/page.tsx @@ -3,7 +3,7 @@ import { Button, Spinner } from 'flowbite-react'; import { DEFAULT_MOMENT_FORMAT } from '@/components/Commons'; import { i18n } from '@/i18n'; -import { useEffect, useState } from 'react'; +import { useEffect, useMemo, useState } from 'react'; import BreadCrumb from '@/admin/common/BreadCrumb'; import MyTable from '@/admin/common/MyTable'; import UserInfoModal from '@/components/admin/user/Modal'; @@ -31,51 +31,86 @@ export default function Page() { }, []); return ( -
-
- -
- -
-
- {loading ? ( -
- -
- ) : ( - + <> + {useMemo( + () => ( +
+
+ +
+ +
+
+ {loading ? ( +
+ +
+ ) : ( + ( + { + evt.preventDefault(); + evt.stopPropagation(); + if (modal) { + return; + } + console.log('click'); + setModal(value); + }} + > + Edit + + ), + }, + ]} + rows={data} + hasCheckBox + hoverable + /> + )} +
+ ), + [loading, data], )} - {modal && setModal(null)} />} -
+ {useMemo( + () => ( + setModal(null)} recId={modal} /> + ), + [modal], + )} + ); } diff --git a/src/app/api/user/[id]/route.ts b/src/app/api/user/[id]/route.ts index afad763..4813ecc 100644 --- a/src/app/api/user/[id]/route.ts +++ b/src/app/api/user/[id]/route.ts @@ -14,6 +14,8 @@ export async function POST(request: NextRequest, { params }: { params: { id: str if (recId <= 0) { await service.create(data); + } else { + await service.update(recId, data); } return Response.json({}); @@ -21,3 +23,24 @@ export async function POST(request: NextRequest, { params }: { params: { id: str return Response.json(error); } } + +export async function GET(request: NextRequest, { params }: { params: { id: string } }) { + try { + const service: UserService = new UserService(); + await service.init(); + + const { id } = params; + + const recId = parseInt(id) || 0; + + let result = {}; + + if (recId > 0) { + result = await service.findById(recId); + } + + return Response.json(result); + } catch (error) { + return Response.json(error); + } +} diff --git a/src/components/admin/user/Modal.tsx b/src/components/admin/user/Modal.tsx index d9fb62c..0658fd3 100644 --- a/src/components/admin/user/Modal.tsx +++ b/src/components/admin/user/Modal.tsx @@ -1,18 +1,25 @@ 'use client'; -import { Button, Label, Modal, TextInput } from 'flowbite-react'; -import { useRef, useState } from 'react'; +import { Button, Label, Modal, Spinner, TextInput } from 'flowbite-react'; +import { useEffect, useRef, useState } from 'react'; + +interface IUser { + key?: string; + value?: string; +} export default function UserInfoModal(props) { const [loading, setLoading] = useState(false); - const { onClose } = props; + const [saving, setSaving] = useState(false); + const [data, setData] = useState({}); + const { onClose, recId } = props; const keyInputRef = useRef(null); const valueInputRef = useRef(null); const onSave = () => { - setLoading(true); + setSaving(true); - fetch('/api/user/-1', { + fetch(`/api/user/${recId}`, { method: 'POST', headers: { 'Content-Type': 'application/json', @@ -23,39 +30,67 @@ export default function UserInfoModal(props) { }), }) .then(async (response) => { - setLoading(false); + setSaving(false); onClose(); }) .catch((e) => { - setLoading(false); + setSaving(false); console.error(e); }); }; + useEffect(() => { + if (recId <= 0 || loading) { + return; + } + setLoading(true); + console.log('here', recId, loading); + fetch(`/api/user/${recId}`, { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + }, + }) + .then(async (response) => { + setLoading(false); + setData(await response.json()); + }) + .catch((e) => { + setLoading(false); + console.error(e); + }); + }, [recId]); + return ( - !loading && onClose()} initialFocus={keyInputRef}> + !saving && onClose()} initialFocus={keyInputRef}> -
-
-
-
- + {loading ? ( +
+
-
-
-
+ )} - - - diff --git a/src/database/repositories/userRepository.ts b/src/database/repositories/userRepository.ts index 17f9ce8..7b06da8 100644 --- a/src/database/repositories/userRepository.ts +++ b/src/database/repositories/userRepository.ts @@ -23,6 +23,8 @@ export default class UserRepository extends BaseRepository { order: orderBy ? [orderBy.split('_')] : [['id', 'desc']], }); + rows = await Promise.all(rows.map(async (row) => this._fillWithRelationsAndFiles(row, options))); + return { rows, count }; } @@ -52,6 +54,27 @@ export default class UserRepository extends BaseRepository { return this.findById(record.id, options); } + static async update(id, data, options: IRepositoryOptions) { + const transaction = SequelizeRepository.getTransaction(options); + + let record = await options.database.user.findOne({ + where: { + id, + }, + transaction, + }); + + if (!record) { + throw new Error404(); + } + + record = await record.update(data, { + transaction, + }); + + return this.findById(record.id, options); + } + static async _fillWithRelationsAndFiles(record, options: IRepositoryOptions, metaOnly = true) { if (!record) { return record; @@ -59,6 +82,8 @@ export default class UserRepository extends BaseRepository { const output = record.get({ plain: true }); + output.action = output.id; + if (metaOnly) { return output; } diff --git a/src/services/userService.ts b/src/services/userService.ts index 1632d03..10cbada 100644 --- a/src/services/userService.ts +++ b/src/services/userService.ts @@ -11,6 +11,10 @@ export default class UserService extends BaseService { return await UserRepository.findAndCountAll({ filter: '', limit: undefined, offset: undefined }, this.options); } + async findById(id) { + return await UserRepository.findById(id, this.options); + } + async create(data) { const transaction = await SequelizeRepository.createTransaction(this.database); @@ -31,4 +35,25 @@ export default class UserService extends BaseService { throw error; } } + + async update(id, data) { + const transaction = await SequelizeRepository.createTransaction(this.database); + + try { + const record = await UserRepository.update(id, data, { + ...this.options, + transaction, + }); + + await SequelizeRepository.commitTransaction(transaction); + + return record; + } catch (error) { + await SequelizeRepository.rollbackTransaction(transaction); + + SequelizeRepository.handleUniqueFieldError(error, this.options.language, 'user'); + + throw error; + } + } } From e614513a90ed52e89b67a32d7984020653d0125b Mon Sep 17 00:00:00 2001 From: james-gates-0212 Date: Mon, 8 Apr 2024 09:16:51 -0700 Subject: [PATCH 13/16] :bug: fix: modal & user information page --- src/app/admin/user/page.tsx | 38 ++++++++++++++++------------- src/components/admin/user/Modal.tsx | 15 +++++++++--- src/i18n/en.ts | 5 ++++ 3 files changed, 37 insertions(+), 21 deletions(-) diff --git a/src/app/admin/user/page.tsx b/src/app/admin/user/page.tsx index 6e447c5..6763f44 100644 --- a/src/app/admin/user/page.tsx +++ b/src/app/admin/user/page.tsx @@ -74,25 +74,29 @@ export default function Page() { }, { key: 'action', - label: '@', - classes: ['whitespace-nowrap', 'text-right'], + label: 'common.action', + classes: ['whitespace-nowrap'], width: 0, render: (value: number) => ( - { - evt.preventDefault(); - evt.stopPropagation(); - if (modal) { - return; - } - console.log('click'); - setModal(value); - }} - > - Edit - + + { + evt.preventDefault(); + evt.stopPropagation(); + if (modal) { + return; + } + setModal(value); + }} + > + {i18n('common.edit')} + + + {i18n('common.delete')} + + ), }, ]} diff --git a/src/components/admin/user/Modal.tsx b/src/components/admin/user/Modal.tsx index 0658fd3..d7af9d6 100644 --- a/src/components/admin/user/Modal.tsx +++ b/src/components/admin/user/Modal.tsx @@ -1,5 +1,6 @@ 'use client'; +import { i18n } from '@/i18n'; import { Button, Label, Modal, Spinner, TextInput } from 'flowbite-react'; import { useEffect, useRef, useState } from 'react'; @@ -40,11 +41,17 @@ export default function UserInfoModal(props) { }; useEffect(() => { - if (recId <= 0 || loading) { + if (recId <= 0) { + setData({}); return; } + + if (loading) { + return; + } + setLoading(true); - console.log('here', recId, loading); + fetch(`/api/user/${recId}`, { method: 'GET', headers: { @@ -88,10 +95,10 @@ export default function UserInfoModal(props) { diff --git a/src/i18n/en.ts b/src/i18n/en.ts index 44780db..5c479bb 100644 --- a/src/i18n/en.ts +++ b/src/i18n/en.ts @@ -58,7 +58,12 @@ const en = { }, common: { + action: 'Action', addNew: 'Add new', + cancel: 'Cancel', + delete: 'Delete', + edit: 'Edit', + save: 'Save', }, entities: { From 59fbba1b45c7e053ea2f986089ab83b0bacab6a4 Mon Sep 17 00:00:00 2001 From: james-gates-0212 Date: Tue, 9 Apr 2024 22:20:28 -0700 Subject: [PATCH 14/16] :wrench: config: react-strict-mode as false Co-Authored-By: clive-goldminer <161703670+clive-goldminer@users.noreply.github.com> --- next.config.js | 1 + 1 file changed, 1 insertion(+) diff --git a/next.config.js b/next.config.js index 6e74678..b331b5a 100644 --- a/next.config.js +++ b/next.config.js @@ -7,6 +7,7 @@ const withBundleAnalyzer = NextBundleAnalyzer({ }); const nextConfig = withBundleAnalyzer({ + reactStrictMode: false, basePath: process.env.BASE_PATH || '', generateBuildId: async () => { // This could be anything, using the latest git hash From 7d1230542b738ccfcca946c616aaf0864a544ecd Mon Sep 17 00:00:00 2001 From: james-gates-0212 Date: Tue, 9 Apr 2024 23:32:09 -0700 Subject: [PATCH 15/16] :triangular_flag_on_post: add: delete for user information Co-Authored-By: clive-goldminer <161703670+clive-goldminer@users.noreply.github.com> --- src/admin/common/ConfirmModal.tsx | 44 ++++++++++++++++++ src/app/admin/user/page.tsx | 50 +++++++++++++++++++-- src/app/api/user/[id]/route.ts | 21 +++++++++ src/database/repositories/userRepository.ts | 19 ++++++++ src/i18n/en.ts | 7 +++ src/services/userService.ts | 19 ++++++++ 6 files changed, 157 insertions(+), 3 deletions(-) create mode 100644 src/admin/common/ConfirmModal.tsx diff --git a/src/admin/common/ConfirmModal.tsx b/src/admin/common/ConfirmModal.tsx new file mode 100644 index 0000000..ec4f8c3 --- /dev/null +++ b/src/admin/common/ConfirmModal.tsx @@ -0,0 +1,44 @@ +'use client'; + +import { i18n } from '@/i18n'; +import { Button, Modal } from 'flowbite-react'; +import { useState } from 'react'; + +interface IConfirmModal { + message?: string; + onCancel?: Function; + onClose: Function; + onConfirm?: Function; + show: boolean; + textCancel?: string; + textConfirm?: string; +} + +export default function ConfirmModal(props: IConfirmModal) { + const { message, onCancel, onClose, onConfirm, show, textCancel, textConfirm } = props; + + const handleCancel = () => (onCancel ? onCancel.call(null) : handleClose()); + const handleClose = () => onClose && onClose.call(null); + const handleConfirm = () => onConfirm && onConfirm.call(null); + + return ( + + + +
+

+ {i18n(message || 'questions.default')} +

+
+ + +
+
+
+
+ ); +} diff --git a/src/app/admin/user/page.tsx b/src/app/admin/user/page.tsx index 6763f44..8c85d5e 100644 --- a/src/app/admin/user/page.tsx +++ b/src/app/admin/user/page.tsx @@ -7,16 +7,33 @@ import { useEffect, useMemo, useState } from 'react'; import BreadCrumb from '@/admin/common/BreadCrumb'; import MyTable from '@/admin/common/MyTable'; import UserInfoModal from '@/components/admin/user/Modal'; +import ConfirmModal from '@/admin/common/ConfirmModal'; export default function Page() { const [loading, setLoading] = useState(false); const [data, setData] = useState([]); - const [modal, setModal] = useState(null); + const [deleteConfirm, setDeleteConfirm] = useState(null); + const [modal, setModal] = useState(null); - useEffect(() => { + const handleCloseDeleteConfirm = () => setDeleteConfirm(null); + const handleDeleteConfirm = () => { setLoading(true); + const delId = deleteConfirm; + setDeleteConfirm(null); + + fetch(`/api/user/${delId}`, { method: 'DELETE' }) + .then(async (response) => { + const { rows } = await response.json(); + setData(rows); + }) + .catch((e) => { + console.error(e); + }) + .finally(() => setLoading(false)); + }; + const handleRefreshTable = () => fetch('/api/user', { method: 'POST', }) @@ -28,6 +45,11 @@ export default function Page() { console.error(e); }) .finally(() => setLoading(false)); + + useEffect(() => { + setLoading(true); + + handleRefreshTable(); }, []); return ( @@ -93,7 +115,18 @@ export default function Page() { > {i18n('common.edit')} - + { + evt.preventDefault(); + evt.stopPropagation(); + if (deleteConfirm) { + return; + } + setDeleteConfirm(value); + }} + > {i18n('common.delete')} @@ -115,6 +148,17 @@ export default function Page() { ), [modal], )} + {useMemo( + () => ( + + ), + [deleteConfirm], + )} ); } diff --git a/src/app/api/user/[id]/route.ts b/src/app/api/user/[id]/route.ts index 4813ecc..72bd742 100644 --- a/src/app/api/user/[id]/route.ts +++ b/src/app/api/user/[id]/route.ts @@ -44,3 +44,24 @@ export async function GET(request: NextRequest, { params }: { params: { id: stri return Response.json(error); } } + +export async function DELETE(request: NextRequest, { params }: { params: { id: string } }) { + try { + const service: UserService = new UserService(); + await service.init(); + + const { id } = params; + + const recId = parseInt(id) || 0; + + if (recId > 0) { + await service.destroy(recId); + } + + const result = await service.getAll(); + + return Response.json(result); + } catch (error) { + return Response.json(error); + } +} diff --git a/src/database/repositories/userRepository.ts b/src/database/repositories/userRepository.ts index 7b06da8..11dcced 100644 --- a/src/database/repositories/userRepository.ts +++ b/src/database/repositories/userRepository.ts @@ -75,6 +75,25 @@ export default class UserRepository extends BaseRepository { return this.findById(record.id, options); } + static async destroy(id, options: IRepositoryOptions) { + const transaction = SequelizeRepository.getTransaction(options); + + const record = await options.database.user.findOne({ + where: { + id, + }, + transaction, + }); + + if (!record) { + throw new Error404(); + } + + await record.destroy({ + transaction, + }); + } + static async _fillWithRelationsAndFiles(record, options: IRepositoryOptions, metaOnly = true) { if (!record) { return record; diff --git a/src/i18n/en.ts b/src/i18n/en.ts index 5c479bb..4f6d0a6 100644 --- a/src/i18n/en.ts +++ b/src/i18n/en.ts @@ -63,7 +63,14 @@ const en = { cancel: 'Cancel', delete: 'Delete', edit: 'Edit', + no: 'No', save: 'Save', + yes: 'Yes', + }, + + questions: { + default: 'Are you sure?', + delete: 'Are you sure to delete this?', }, entities: { diff --git a/src/services/userService.ts b/src/services/userService.ts index 10cbada..4c5c31d 100644 --- a/src/services/userService.ts +++ b/src/services/userService.ts @@ -56,4 +56,23 @@ export default class UserService extends BaseService { throw error; } } + + async destroy(id) { + const transaction = await SequelizeRepository.createTransaction(this.database); + + try { + await UserRepository.destroy(id, { + ...this.options, + transaction, + }); + + await SequelizeRepository.commitTransaction(transaction); + } catch (error) { + await SequelizeRepository.rollbackTransaction(transaction); + + SequelizeRepository.handleUniqueFieldError(error, this.options.language, 'user'); + + throw error; + } + } } From a91adf16ef771d1e0191f7e9cf03e9f4cb9b92f8 Mon Sep 17 00:00:00 2001 From: james-gates-0212 Date: Tue, 9 Apr 2024 23:40:21 -0700 Subject: [PATCH 16/16] :triangular_flag_on_post: update: user information modal Co-Authored-By: clive-goldminer <161703670+clive-goldminer@users.noreply.github.com> --- src/app/admin/user/page.tsx | 2 +- src/components/admin/user/Modal.tsx | 21 ++++++++++++++++++--- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/src/app/admin/user/page.tsx b/src/app/admin/user/page.tsx index 8c85d5e..c3fb345 100644 --- a/src/app/admin/user/page.tsx +++ b/src/app/admin/user/page.tsx @@ -144,7 +144,7 @@ export default function Page() { )} {useMemo( () => ( - setModal(null)} recId={modal} /> + setModal(null)} recId={modal} handleRefresh={handleRefreshTable} /> ), [modal], )} diff --git a/src/components/admin/user/Modal.tsx b/src/components/admin/user/Modal.tsx index d7af9d6..c778875 100644 --- a/src/components/admin/user/Modal.tsx +++ b/src/components/admin/user/Modal.tsx @@ -13,7 +13,7 @@ export default function UserInfoModal(props) { const [loading, setLoading] = useState(false); const [saving, setSaving] = useState(false); const [data, setData] = useState({}); - const { onClose, recId } = props; + const { onClose, recId, handleRefresh } = props; const keyInputRef = useRef(null); const valueInputRef = useRef(null); @@ -33,6 +33,7 @@ export default function UserInfoModal(props) { .then(async (response) => { setSaving(false); onClose(); + handleRefresh && handleRefresh.call(null); }) .catch((e) => { setSaving(false); @@ -82,13 +83,27 @@ export default function UserInfoModal(props) {
- +
- +
)}