From 60e9d2a631f3283a03dd02a7cda12862b450f12b Mon Sep 17 00:00:00 2001 From: geonhee-min Date: Tue, 16 Dec 2025 17:26:58 +0900 Subject: [PATCH] =?UTF-8?q?issue=20#=20-=20DTO=20=ED=8C=A8=ED=82=A4?= =?UTF-8?q?=EC=A7=80=20=EB=A0=88=EC=A7=80=EC=8A=A4=ED=8A=B8=EB=A6=AC=20?= =?UTF-8?q?=EC=A0=84=ED=99=98=20=EC=A4=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 265 +++++++++++++++++- package.json | 4 +- src/network/AccountNetwork.ts | 58 ++-- src/network/BaseNetwork.ts | 58 +++- src/network/ScheduleNetwork.ts | 12 +- src/ui/component/calendar/CustomCalendar.tsx | 33 ++- .../content/ScheduleCreateContent.tsx | 49 ++-- .../content/ScheduleDetailContent.tsx | 7 +- .../schedule/content/ScheduleListContent.tsx | 17 +- .../schedule/tile/ScheduleListTile.tsx | 6 +- src/ui/page/account/login/LoginPage.tsx | 12 +- .../resetPassword/ResetPasswordPage.tsx | 126 ++++----- src/ui/page/account/signup/SignUpPage.tsx | 54 +++- 13 files changed, 512 insertions(+), 189 deletions(-) diff --git a/package-lock.json b/package-lock.json index 0c91d77..8047a23 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,7 +8,7 @@ "name": "scheduler", "version": "0.0.0", "dependencies": { - "@baekyangdan/core-utils": "^1.0.9", + "@baekyangdan/core-utils": "^1.0.21", "@diceui/mention": "^0.8.0", "@hookform/resolvers": "^5.2.2", "@radix-ui/react-accordion": "^1.2.12", @@ -40,6 +40,8 @@ "@tailwindcss/cli": "^4.1.16", "@tailwindcss/vite": "^4.1.16", "axios": "^1.13.2", + "class-transformer": "^0.5.1", + "class-validator": "^0.14.3", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "cmdk": "^1.1.1", @@ -373,11 +375,14 @@ } }, "node_modules/@baekyangdan/core-utils": { - "version": "1.0.9", - "resolved": "https://gitea.bkdhome.p-e.kr/api/packages/baekyangdan/npm/%40baekyangdan%2Fcore-utils/-/1.0.9/core-utils-1.0.9.tgz", - "integrity": "sha512-zeXQPXJlwpO2/PzmQJQrXP9A6/maZmWWISmEuW82R42fkgXwaxavT/xlI1MsYLg1tqibwNBLSgnjXd0H9vR0KQ==", + "version": "1.0.21", + "resolved": "https://gitea.bkdhome.p-e.kr/api/packages/baekyangdan/npm/%40baekyangdan%2Fcore-utils/-/1.0.21/core-utils-1.0.21.tgz", + "integrity": "sha512-LYkzavYnforDtXm/icOg6rQkRAQAgpdwlC6w8dpWAz/N7ynIHdUHJZSPRRsQc9Jy3hQp7+vtKjZI4LP3NKC0UA==", "license": "ISC", "dependencies": { + "@swc/core": "^1.15.5", + "class-transformer": "^0.5.1", + "class-validator": "^0.14.3", "date-fns": "^4.1.0", "reflect-metadata": "^0.2.2", "tsup": "^8.5.1" @@ -3121,6 +3126,220 @@ "integrity": "sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==", "license": "MIT" }, + "node_modules/@swc/core": { + "version": "1.15.5", + "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.15.5.tgz", + "integrity": "sha512-VRy+AEO0zqUkwV9uOgqXtdI5tNj3y3BZI+9u28fHNjNVTtWYVNIq3uYhoGgdBOv7gdzXlqfHKuxH5a9IFAvopQ==", + "hasInstallScript": true, + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@swc/counter": "^0.1.3", + "@swc/types": "^0.1.25" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/swc" + }, + "optionalDependencies": { + "@swc/core-darwin-arm64": "1.15.5", + "@swc/core-darwin-x64": "1.15.5", + "@swc/core-linux-arm-gnueabihf": "1.15.5", + "@swc/core-linux-arm64-gnu": "1.15.5", + "@swc/core-linux-arm64-musl": "1.15.5", + "@swc/core-linux-x64-gnu": "1.15.5", + "@swc/core-linux-x64-musl": "1.15.5", + "@swc/core-win32-arm64-msvc": "1.15.5", + "@swc/core-win32-ia32-msvc": "1.15.5", + "@swc/core-win32-x64-msvc": "1.15.5" + }, + "peerDependencies": { + "@swc/helpers": ">=0.5.17" + }, + "peerDependenciesMeta": { + "@swc/helpers": { + "optional": true + } + } + }, + "node_modules/@swc/core-darwin-arm64": { + "version": "1.15.5", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.15.5.tgz", + "integrity": "sha512-RvdpUcXrIz12yONzOdQrJbEnq23cOc2IHOU1eB8kPxPNNInlm4YTzZEA3zf3PusNpZZLxwArPVLCg0QsFQoTYw==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-darwin-x64": { + "version": "1.15.5", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.15.5.tgz", + "integrity": "sha512-ufJnz3UAff/8G5OfqZZc5cTQfGtXyXVLTB8TGT0xjkvEbfFg8jZUMDBnZT/Cn0k214JhMjiLCNl0A8aY/OKsYQ==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm-gnueabihf": { + "version": "1.15.5", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.15.5.tgz", + "integrity": "sha512-Yqu92wIT0FZKLDWes+69kBykX97hc8KmnyFwNZGXJlbKUGIE0hAIhbuBbcY64FGSwey4aDWsZ7Ojk89KUu9Kzw==", + "cpu": [ + "arm" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm64-gnu": { + "version": "1.15.5", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.15.5.tgz", + "integrity": "sha512-3gR3b5V1abe/K1GpD0vVyZgqgV+ykuB5QNecDYzVroX4QuN+amCzQaNSsVM8Aj6DbShQCBTh3hGHd2f3vZ8gCw==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm64-musl": { + "version": "1.15.5", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.15.5.tgz", + "integrity": "sha512-Of+wmVh5h47tTpN9ghHVjfL0CJrgn99XmaJjmzWFW7agPdVY6gTDgkk6zQ6q4hcDQ7hXb0BGw6YFpuanBzNPow==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-x64-gnu": { + "version": "1.15.5", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.15.5.tgz", + "integrity": "sha512-98kuPS0lZVgjmc/2uTm39r1/OfwKM0PM13ZllOAWi5avJVjRd/j1xA9rKeUzHDWt+ocH9mTCQsAT1jjKSq45bg==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-x64-musl": { + "version": "1.15.5", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.15.5.tgz", + "integrity": "sha512-Rk+OtNQP3W/dZExL74LlaakXAQn6/vbrgatmjFqJPO4RZkq+nLo5g7eDUVjyojuERh7R2yhqNvZ/ZZQe8JQqqA==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-arm64-msvc": { + "version": "1.15.5", + "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.15.5.tgz", + "integrity": "sha512-e3RTdJ769+PrN25iCAlxmsljEVu6iIWS7sE21zmlSiipftBQvSAOWuCDv2A8cH9lm5pSbZtwk8AUpIYCNsj2oQ==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-ia32-msvc": { + "version": "1.15.5", + "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.15.5.tgz", + "integrity": "sha512-NmOdl6kyAw6zMz36zCdopTgaK2tcLA53NhUsTRopBc/796Fp87XdsslRHglybQ1HyXIGOQOKv2Y14IUbeci4BA==", + "cpu": [ + "ia32" + ], + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-x64-msvc": { + "version": "1.15.5", + "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.15.5.tgz", + "integrity": "sha512-EPXJRf0A8eOi8woXf/qgVIWRl9yeSl0oN1ykGZNCGI7oElsfxUobJFmpJFJoVqKFfd1l0c+GPmWsN2xavTFkNw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/counter": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", + "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==", + "license": "Apache-2.0" + }, + "node_modules/@swc/types": { + "version": "0.1.25", + "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.25.tgz", + "integrity": "sha512-iAoY/qRhNH8a/hBvm3zKj9qQ4oc2+3w1unPJa2XvTK3XjeLXtzcCingVPw/9e5mn1+0yPqxcBGp9Jf0pkfMb1g==", + "license": "Apache-2.0", + "dependencies": { + "@swc/counter": "^0.1.3" + } + }, "node_modules/@tailwindcss/cli": { "version": "4.1.16", "resolved": "https://registry.npmjs.org/@tailwindcss/cli/-/cli-4.1.16.tgz", @@ -3550,6 +3769,12 @@ "@types/react": "^19.2.0" } }, + "node_modules/@types/validator": { + "version": "13.15.10", + "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.15.10.tgz", + "integrity": "sha512-T8L6i7wCuyoK8A/ZeLYt1+q0ty3Zb9+qbSSvrIVitzT3YjZqkTZ40IbRsPanlB4h1QB3JVL1SYCdR6ngtFYcuA==", + "license": "MIT" + }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "8.46.2", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.46.2.tgz", @@ -4154,6 +4379,23 @@ "url": "https://paulmillr.com/funding/" } }, + "node_modules/class-transformer": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/class-transformer/-/class-transformer-0.5.1.tgz", + "integrity": "sha512-SQa1Ws6hUbfC98vKGxZH3KFY0Y1lm5Zm0SY8XX9zbK7FJCyVEac3ATW0RIpwzW+oOfmHE5PMPufDG9hCfoEOMw==", + "license": "MIT" + }, + "node_modules/class-validator": { + "version": "0.14.3", + "resolved": "https://registry.npmjs.org/class-validator/-/class-validator-0.14.3.tgz", + "integrity": "sha512-rXXekcjofVN1LTOSw+u4u9WXVEUvNBVjORW154q/IdmYWy1nMbOU9aNtZB0t8m+FJQ9q91jlr2f9CwwUFdFMRA==", + "license": "MIT", + "dependencies": { + "@types/validator": "^13.15.3", + "libphonenumber-js": "^1.11.1", + "validator": "^13.15.20" + } + }, "node_modules/class-variance-authority": { "version": "0.7.1", "resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.1.tgz", @@ -5427,6 +5669,12 @@ "node": ">= 0.8.0" } }, + "node_modules/libphonenumber-js": { + "version": "1.12.31", + "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.12.31.tgz", + "integrity": "sha512-Z3IhgVgrqO1S5xPYM3K5XwbkDasU67/Vys4heW+lfSBALcUZjeIIzI8zCLifY+OCzSq+fpDdywMDa7z+4srJPQ==", + "license": "MIT" + }, "node_modules/lightningcss": { "version": "1.30.2", "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.2.tgz", @@ -7550,6 +7798,15 @@ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, + "node_modules/validator": { + "version": "13.15.23", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.15.23.tgz", + "integrity": "sha512-4yoz1kEWqUjzi5zsPbAS/903QXSYp0UOtHsPpp7p9rHAw/W+dkInskAE386Fat3oKRROwO98d9ZB0G4cObgUyw==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, "node_modules/vaul": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vaul/-/vaul-1.1.2.tgz", diff --git a/package.json b/package.json index a0b62bb..e389b62 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "preview": "vite preview" }, "dependencies": { - "@baekyangdan/core-utils": "^1.0.9", + "@baekyangdan/core-utils": "^1.0.21", "@diceui/mention": "^0.8.0", "@hookform/resolvers": "^5.2.2", "@radix-ui/react-accordion": "^1.2.12", @@ -43,6 +43,8 @@ "@tailwindcss/cli": "^4.1.16", "@tailwindcss/vite": "^4.1.16", "axios": "^1.13.2", + "class-transformer": "^0.5.1", + "class-validator": "^0.14.3", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "cmdk": "^1.1.1", diff --git a/src/network/AccountNetwork.ts b/src/network/AccountNetwork.ts index 631d8fe..b1111a6 100644 --- a/src/network/AccountNetwork.ts +++ b/src/network/AccountNetwork.ts @@ -1,34 +1,14 @@ -import { - CheckDuplicationRequest, - SendVerificationCodeRequest, - VerifyCodeRequest, - SignupRequest, - LoginRequest, - SendResetPasswordCodeRequest, - VerifyResetPasswordCodeRequest, - ResetPasswordRequest -} from "@/data/request"; -import { - CheckDuplicationResponse, - SendVerificationCodeResponse, - VerifyCodeResponse, - SignupResponse, - LoginResponse, - SendResetPasswordCodeResponse, - VerifyResetPasswordCodeResponse, - ResetPasswordResponse -} from "@/data/response"; import { BaseNetwork } from "./BaseNetwork"; -import { HttpApiUrl } from "@baekyangdan/core-utils"; +import { HttpApiUrl, SchedulerDTO as DTO } from "@baekyangdan/core-utils"; const AccountApi = HttpApiUrl.Account; export class AccountNetwork extends BaseNetwork { private baseUrl = AccountApi.base; - async checkDuplication(data: CheckDuplicationRequest) { + async checkDuplication(data: DTO.CheckDuplicationRequest) { const { type, value } = data; - return await this.get( + return await this.get( `${this.baseUrl}${AccountApi.checkDuplication}?type=${type}&value=${value}` , { authPass: true @@ -36,8 +16,8 @@ export class AccountNetwork extends BaseNetwork { ); } - async sendVerificationCode(data: SendVerificationCodeRequest) { - return await this.post( + async sendVerificationCode(data: DTO.SendEmailVerificationCodeRequest) { + return await this.post( `${this.baseUrl}${AccountApi.sendEmailVerificationCode}` , data , { @@ -46,8 +26,8 @@ export class AccountNetwork extends BaseNetwork { ); } - async verifyCode(data: VerifyCodeRequest) { - return await this.post( + async verifyCode(data: DTO.VerifyEmailVerificationCodeRequest) { + return await this.post( `${this.baseUrl}${AccountApi.verifyEmailVerificationCode}` , data , { @@ -56,8 +36,8 @@ export class AccountNetwork extends BaseNetwork { ); } - async signup(data: SignupRequest) { - return await this.post( + async signup(data: DTO.SignupRequest) { + return await this.post( `${this.baseUrl}${AccountApi.signup}` , data , { @@ -66,8 +46,8 @@ export class AccountNetwork extends BaseNetwork { ); } - async login(data: LoginRequest) { - return await this.post( + async login(data: DTO.LoginRequest) { + return await this.post( `${this.baseUrl}${AccountApi.login}` , data , { @@ -76,22 +56,22 @@ export class AccountNetwork extends BaseNetwork { ); } - async sendResetPasswordCode(data: SendResetPasswordCodeRequest) { - return await this.post( - `${this.baseUrl}${AccountApi.sendResetPasswordCode}`, + async sendPasswordResetCode(data: DTO.SendPasswordResetCodeRequest) { + return await this.post( + `${this.baseUrl}${AccountApi.sendPasswordResetCode}`, data ); } - async verifyResetPasswordCode(data: VerifyResetPasswordCodeRequest) { - return await this.post( - `${this.baseUrl}${AccountApi.verifyResetPasswordCode}`, + async verifyPasswordResetCode(data: DTO.VerifyPasswordResetCodeRequest) { + return await this.post( + `${this.baseUrl}${AccountApi.verifyPasswordResetCode}`, data ); } - async resetPassword(data: ResetPasswordRequest) { - return await this.post( + async resetPassword(data: DTO.ResetPasswordRequest) { + return await this.post( `${this.baseUrl}${AccountApi.resetPassword}`, data ); diff --git a/src/network/BaseNetwork.ts b/src/network/BaseNetwork.ts index 580d766..c5e9cd3 100644 --- a/src/network/BaseNetwork.ts +++ b/src/network/BaseNetwork.ts @@ -1,15 +1,16 @@ -import axios from 'axios'; +import type { AuthData } from '@/data/AuthData'; +import { useAuthStore } from '@/store/authStore'; +import { SchedulerDTO as DTO, HttpApiUrl, UnauthorizedCode, UnauthorizedMessage } from '@baekyangdan/core-utils'; import type { + AxiosError, AxiosInstance, AxiosRequestConfig, - AxiosError, AxiosResponse, InternalAxiosRequestConfig, } from "axios"; -import { useAuthStore } from '@/store/authStore'; -import { RefreshAccessTokenResponse } from '@/data/response/account/RefreshAccessTokenResponse'; -import type { AuthData } from '@/data/AuthData'; -import { HttpApiUrl, UnauthorizedCode, UnauthorizedMessage } from '@baekyangdan/core-utils'; +import axios from 'axios'; +import { plainToInstance } from 'class-transformer'; +import { validateOrReject } from 'class-validator'; export class BaseNetwork { protected instance: AxiosInstance; @@ -141,12 +142,45 @@ export class BaseNetwork { /** * 기본 CRUD 메서드 */ - protected async get(url: string, config?: AxiosRequestConfig & { authPass?: boolean }) { - return await this.instance.get(url, config); + protected async get( + url: string, + config?: AxiosRequestConfig & { authPass?: boolean }, + dtoClass?: new () => TData + ) { + const result = await this.instance.get(url, config); + + if (dtoClass && (result.data as any)?.success === true) { + const rawData = (result.data as any).data; + + if (rawData) { + const instance = plainToInstance(dtoClass, rawData); + await validateOrReject(instance); + (result.data as any).data = instance; + } + } + + return result.data; } - protected async post(url: string, data?: any, config?: AxiosRequestConfig & { authPass?: boolean }) { - return await this.instance.post(url, data, config); + protected async post( + url: string, + data?: any, + config?: AxiosRequestConfig & { authPass?: boolean }, + dtoClass?: new () => TData + ) { + const result = await this.instance.post(url, data, config); + + if (dtoClass && (result.data as any)?.success === true) { + const rawData = (result.data as any).data; + + if (rawData) { + const instance = plainToInstance(dtoClass, rawData); + await validateOrReject(instance); + (result.data as any).data = instance; + } + } + + return result.data; } public async refreshToken() { @@ -167,14 +201,14 @@ export class BaseNetwork { } } - const result = await this.get( + const result = await this.get( `${HttpApiUrl.Account.base}${HttpApiUrl.Account.refreshAccessToken}`, { withCredentials: true } ); - if (!result.data.success) throw new Error; + if (!result.success || !result.data) throw new Error; const newAccessToken = result.data.accessToken; diff --git a/src/network/ScheduleNetwork.ts b/src/network/ScheduleNetwork.ts index 453a5fe..ef15dd3 100644 --- a/src/network/ScheduleNetwork.ts +++ b/src/network/ScheduleNetwork.ts @@ -11,26 +11,26 @@ import { ScheduleListResponse } from "@/data/response"; import { HttpApiUrl } from "@baekyangdan/core-utils"; - +import { SchedulerDTO as DTO } from "@baekyangdan/core-utils"; const ScheduleApi = HttpApiUrl.Schedule; export class ScheduleNetwork extends BaseNetwork { private baseUrl = ScheduleApi.base; - async getList(data: ScheduleListRequest) { - return await this.post( + async getList(data: DTO.ScheduleListRequest) { + return await this.post( this.baseUrl, data ); } async getDetail(id: string) { - return await this.get( + return await this.get( `${this.baseUrl}/${id}` ); } - async create(data: CreateScheduleRequest) { - return await this.post( + async create(data: DTO.ScheduleCreateRequest) { + return await this.post( `${this.baseUrl}${ScheduleApi.create}`, data ); diff --git a/src/ui/component/calendar/CustomCalendar.tsx b/src/ui/component/calendar/CustomCalendar.tsx index 4c244d8..f8fb610 100644 --- a/src/ui/component/calendar/CustomCalendar.tsx +++ b/src/ui/component/calendar/CustomCalendar.tsx @@ -8,14 +8,13 @@ import { ScheduleNetwork } from "@/network/ScheduleNetwork"; import { ScheduleListData } from "@/data/response"; import { CustomCalendarCN } from "./CustomCalendarCN"; import { toast } from "sonner"; -import { Converter } from "@/util/Converter"; import type { SchedulePopoverMode } from "@/const/schedule/SchedulePopoverMode"; - +import { SchedulerDTO as DTO, Type } from '@baekyangdan/core-utils'; interface CustomCalendarProps { data?: any; } -interface EventBarPosition extends ScheduleListData { +interface EventBarPosition extends DTO.ScheduleList { positionStyle: React.CSSProperties; trackIndex: number; isOverflow?: boolean; @@ -41,7 +40,7 @@ export const CustomCalendar = ({ data }: CustomCalendarProps) => { const [windowSize, setWindowSize] = useState({ width: window.innerWidth, height: window.innerHeight }); const [maxVisibleEvents, setMaxVisibleEvents] = useState(3); const [overflowTrackIndex, setOverflowTrackIndex] = useState(maxVisibleEvents + 1); - const [scheduleList, setScheduleList] = useState>([]); + const [scheduleList, setScheduleList] = useState>([]); const [barPositions, setBarPositions] = useState>([]); const scheduleNetwork = new ScheduleNetwork(); const containerRef = useRef(null); @@ -93,17 +92,17 @@ export const CustomCalendar = ({ data }: CustomCalendarProps) => { const endDate = endOfWeek(monthEnd, { weekStartsOn: 0 }); const data = { - startDate: Converter.dateToUTC9(startDate), - endDate: Converter.dateToUTC9(endDate) - }; + startDate: startDate, + endDate: endDate + } as DTO.ScheduleListRequest; const result = await scheduleNetwork.getList(data); - if (result.data.success) { - if (result.data.data) { + if (result.success) { + if (result.data) { if (isSameMonth(requestedMonth, month)) { // setCurrentDataMonth(month); - setScheduleList(result.data.data!); + setScheduleList(result.data); } // setScheduleList(result.data.data); } @@ -221,7 +220,11 @@ export const CustomCalendar = ({ data }: CustomCalendarProps) => { occupiedTrackList.set(dayKey, [...occupied, assignedTrack]); } } - scheduleListWithTrack.push({ ...schedule, trackIndex: assignedTrack }); + scheduleListWithTrack.push({ + ...schedule, + startDate: schedule.startDate.toISOString(), + endDate: schedule.endDate.toISOString(), + trackIndex: assignedTrack }); } else { for (const day of eventDays) { const dayKey = format(day, DATE_FORMAT_KEY); @@ -295,8 +298,8 @@ export const CustomCalendar = ({ data }: CustomCalendarProps) => { overflowPositions.push({ id: `overflow-${dayKey}`, name: `${count} more`, - startDate: Converter.dateToUTC9(new Date()), - endDate: Converter.dateToUTC9(new Date()), + startDate: new Date(), + endDate: new Date(), style: '#9CA3AF', trackIndex: overflowTrackIndex, isOverflow: true, @@ -339,6 +342,10 @@ export const CustomCalendar = ({ data }: CustomCalendarProps) => { positions.push({ ...schedule, + type: schedule.type as Type.Type, + status: schedule.status as Type.Status, + startDate: new Date(schedule.startDate), + endDate: new Date(schedule.endDate), trackIndex: schedule.trackIndex, id: schedule.id, segmentId: `${schedule.id}-${renderStartKey}`, diff --git a/src/ui/component/schedule/content/ScheduleCreateContent.tsx b/src/ui/component/schedule/content/ScheduleCreateContent.tsx index 64bf240..d13c537 100644 --- a/src/ui/component/schedule/content/ScheduleCreateContent.tsx +++ b/src/ui/component/schedule/content/ScheduleCreateContent.tsx @@ -5,10 +5,8 @@ import { Popover, PopoverTrigger } from '@/components/ui/popover'; import { ScrollArea } from '@/components/ui/scroll-area'; import { Textarea } from '@/components/ui/textarea'; import { ToggleGroup, ToggleGroupItem } from '@/components/ui/toggle-group'; -import { type ColorPaletteType } from '@/const/ColorPalette'; -import { ScheduleDay } from '@/const/schedule/ScheduleDay'; -import type { ScheduleStatus } from '@/const/schedule/ScheduleStatus'; -import { type ScheduleType } from '@/const/schedule/ScheduleType'; +import type { ColorPaletteType } from '@/const/ColorPalette'; +import { Type } from '@baekyangdan/core-utils'; import { CreateScheduleSchema } from '@/data/form/schedule/createSchedule.schema'; import { usePalette } from '@/hooks/use-palette'; import { useRecord } from '@/hooks/use-record'; @@ -27,17 +25,18 @@ import { TimePickPopover } from '../popover/TimePickPopover'; import { TypePickPopover } from '../popover/TypePickPopover'; import type { ScheduleCreateContentProps } from './ContentProps'; import { Converter } from '@/util/Converter'; +import { SchedulerDTO as DTO } from '@baekyangdan/core-utils'; export const ScheduleCreateContent = ({ date, setMode, popoverSide, popoverAlign, refetchList }: ScheduleCreateContentProps) => { const [colorPopoverOpen, setColorPopoverOpen] = useState(false); const [isLoading, setIsLoading] = useState(false); const { getPaletteByKey } = usePalette(); const { getCurrentTimeString, standardTimeToContinentalTime } = useTime(); - const dayLabelList = useRecord(ScheduleDay).keys.map((key) => { + const dayLabelList = useRecord(Type.Day).keys.map((key) => { return { - day: Number(key), - label: ScheduleDay[Number(key)] - } as { day: number, label: string }; + day: key, + label: Type.Day[key as keyof typeof Type.Day] + } as { day: string, label: string }; }); const scheduleNetwork = new ScheduleNetwork(); @@ -66,7 +65,6 @@ export const ScheduleCreateContent = ({ date, setMode, popoverSide, popoverAlign startTime, endTime, type, - status, style, dayList, // participantList @@ -75,18 +73,29 @@ export const ScheduleCreateContent = ({ date, setMode, popoverSide, popoverAlign const reqCreate = async () => { if (isLoading) return; + // const data = { + // name, + // startDate: Converter.dateToUTC9(startDate), + // endDate: Converter.dateToUTC9(endDate), + // content, + // startTime: standardTimeToContinentalTime(startTime), + // endTime: standardTimeToContinentalTime(endTime), + // type: type as Type.Type, + // status: status as Type.Status, + // style + // } as DTO.ScheduleCreateRequest; + const data = { name, - startDate: Converter.dateToUTC9(startDate), - endDate: Converter.dateToUTC9(endDate), - content, + startDate: startDate, + endDate: endDate, + type: type as Type.Type, + style: style, startTime: standardTimeToContinentalTime(startTime), endTime: standardTimeToContinentalTime(endTime), - type: type as ScheduleType, - status: status as ScheduleStatus, dayList, - style - }; + content + } as DTO.ScheduleCreateRequest; setIsLoading(true); @@ -99,8 +108,8 @@ export const ScheduleCreateContent = ({ date, setMode, popoverSide, popoverAlign try { const res = await createPromise; - if (!res.data.success) { - throw new Error(res.data.error); + if (!res.success) { + throw new Error(res.error); } toast.success('일정이 생성되었습니다'); @@ -123,7 +132,7 @@ export const ScheduleCreateContent = ({ date, setMode, popoverSide, popoverAlign setColorPopoverOpen(false); } - const selectType = (type: ScheduleType) => { + const selectType = (type: Type.Type) => { createScheduleForm.setValue('type', type); } @@ -259,7 +268,7 @@ export const ScheduleCreateContent = ({ date, setMode, popoverSide, popoverAlign className="w-full h-full flex! flex-col! gap-4!" > diff --git a/src/ui/component/schedule/content/ScheduleDetailContent.tsx b/src/ui/component/schedule/content/ScheduleDetailContent.tsx index 7c59f5f..b541535 100644 --- a/src/ui/component/schedule/content/ScheduleDetailContent.tsx +++ b/src/ui/component/schedule/content/ScheduleDetailContent.tsx @@ -5,10 +5,11 @@ import { ArrowLeft, ChevronUp, Clock } from 'lucide-react'; import type { ScheduleDetailData } from '@/data/response'; import { cn } from '@/lib/utils'; import { ScrollArea } from '@/components/ui/scroll-area'; +import { SchedulerDTO as DTO } from '@baekyangdan/core-utils' export const ScheduleDetailContent = ({ setMode, popoverSide, popoverAlign, id }: ScheduleDetailContentProps) => { const scheduleNetwork = new ScheduleNetwork(); - const [data, setData] = useState(); + const [data, setData] = useState(); const [commentFold, setCommentFold] = useState(true); useEffect(() => { @@ -20,8 +21,8 @@ export const ScheduleDetailContent = ({ setMode, popoverSide, popoverAlign, id } const reqDetail = async () => { const result = await scheduleNetwork.getDetail(id); - if (result.data.success && result.data) { - setData(result.data.data!); + if (result.success) { + setData(result.data); } } diff --git a/src/ui/component/schedule/content/ScheduleListContent.tsx b/src/ui/component/schedule/content/ScheduleListContent.tsx index 2e3be45..79dfe84 100644 --- a/src/ui/component/schedule/content/ScheduleListContent.tsx +++ b/src/ui/component/schedule/content/ScheduleListContent.tsx @@ -13,10 +13,11 @@ import type { ScheduleType } from "@/const/schedule/ScheduleType"; import { ScheduleListData } from "@/data/response"; import { Converter } from "@/util/Converter"; import { ScheduleListTile } from "../tile/ScheduleListTile"; +import { SchedulerDTO as DTO, Type } from "@baekyangdan/core-utils"; export const ScheduleListContent = ({ date, setMode, popoverAlign, popoverSide, open, setId }: ScheduleListContentProps) => { const [isLoading, setIsLoading] = useState(false); - const [scheduleList, setScheduleList] = useState>([]); + const [scheduleList, setScheduleList] = useState>([]); const scheduleNetwork = new ScheduleNetwork(); const listScheduleForm = useForm>({ @@ -52,16 +53,16 @@ export const ScheduleListContent = ({ date, setMode, popoverAlign, popoverSide, const reqList = async () => { const data = { name, - date: Converter.dateToUTC9(searchDate), - status: status as ScheduleStatus | undefined, - styleList, - typeList: typeList as ScheduleType[] | undefined - }; + date: searchDate, + status: status as Type.Status, + typeList: typeList as Array, + styleList: styleList + } as DTO.ScheduleListRequest; const result = await scheduleNetwork.getList(data); - if (result.data.success) { - setScheduleList(result.data.data!); + if (result.success) { + setScheduleList(result.data!); } } diff --git a/src/ui/component/schedule/tile/ScheduleListTile.tsx b/src/ui/component/schedule/tile/ScheduleListTile.tsx index 217508c..d18fcae 100644 --- a/src/ui/component/schedule/tile/ScheduleListTile.tsx +++ b/src/ui/component/schedule/tile/ScheduleListTile.tsx @@ -2,10 +2,10 @@ import type { SchedulePopoverMode } from "@/const/schedule/SchedulePopoverMode"; import { ScheduleTypeLabel } from "@/const/schedule/ScheduleType"; import { ScheduleListData } from "@/data/response"; import { Converter } from "@/util/Converter"; - +import { SchedulerDTO as DTO } from "@baekyangdan/core-utils"; interface ScheduleListTileProps { setMode: (mode: SchedulePopoverMode) => void; - data: ScheduleListData; + data: DTO.ScheduleList; onClick: (id: string) => void; } export const ScheduleListTile = ({ setMode, data, onClick }: ScheduleListTileProps) => { @@ -38,7 +38,7 @@ export const ScheduleListTile = ({ setMode, data, onClick }: ScheduleListTilePro
- {formatter(data.startDate)} - {formatter(data.endDate)} + {formatter(data.startDate.toISOString())} - {formatter(data.endDate.toISOString())}
diff --git a/src/ui/page/account/login/LoginPage.tsx b/src/ui/page/account/login/LoginPage.tsx index 3901b68..05b2cb3 100644 --- a/src/ui/page/account/login/LoginPage.tsx +++ b/src/ui/page/account/login/LoginPage.tsx @@ -11,7 +11,7 @@ import { PageRouting } from '@/const/PageRouting'; import * as z from 'zod'; import { Separator } from '@/components/ui/separator'; import { Validator } from '@/util/Validator'; -import { LoginRequest } from '@/data/request/account/LoginRequest'; +import { SchedulerDTO as DTO } from '@baekyangdan/core-utils'; import { AccountNetwork } from '@/network/AccountNetwork'; import { toast } from 'sonner'; import { useAuthStore } from '@/store/authStore'; @@ -56,7 +56,11 @@ export default function LoginPage() { if (isLoading) return; const type = Validator.isEmail(id) ? 'email' : 'accountId'; - const data: LoginRequest = new LoginRequest(type, id, password); + const data = { + type, + id, + password + } as DTO.LoginRequest; setIsLoading(true); @@ -68,7 +72,7 @@ export default function LoginPage() { loading: "로그인 중입니다.", success: (res) => { setIsLoading(false); - if (res.data.success) { + if (res.success) { const data = { accessToken: res.data.accessToken! }; @@ -79,7 +83,7 @@ export default function LoginPage() { moveToHomePage(); return "로그인 성공"; } else { - throw new Error(res.data.message); + throw new Error(res.message); } }, error: (err: Error) => { diff --git a/src/ui/page/account/resetPassword/ResetPasswordPage.tsx b/src/ui/page/account/resetPassword/ResetPasswordPage.tsx index 1e17bf0..e0d7529 100644 --- a/src/ui/page/account/resetPassword/ResetPasswordPage.tsx +++ b/src/ui/page/account/resetPassword/ResetPasswordPage.tsx @@ -1,20 +1,20 @@ -import { Card, CardContent, CardHeader, CardFooter } from '@/components/ui/card'; -import { ResetPasswordSchema } from '@/data/form'; -import { Field, FieldError, FieldLabel } from '@/components/ui/field'; import { Button } from '@/components/ui/button'; +import { Card, CardContent, CardFooter, CardHeader } from '@/components/ui/card'; +import { Field, FieldError, FieldLabel } from '@/components/ui/field'; import { Input } from '@/components/ui/input'; +import { InputOTP, InputOTPGroup, InputOTPSlot } from '@/components/ui/input-otp'; +import { Label } from '@/components/ui/label'; +import { Stepper, StepperContent, StepperIndicator, StepperItem, StepperNav, StepperPanel, StepperSeparator, StepperTrigger } from '@/components/ui/stepper'; +import { PageRouting } from '@/const/PageRouting'; +import { ResetPasswordSchema } from '@/data/form'; +import { AccountNetwork } from '@/network/AccountNetwork'; import { zodResolver } from '@hookform/resolvers/zod'; -import React, { useState, useCallback } from 'react'; +import { CircleCheckBigIcon, Eye, EyeOff, LoaderCircleIcon } from 'lucide-react'; +import React, { useCallback, useState } from 'react'; import { Controller, useForm } from 'react-hook-form'; import { useNavigate } from 'react-router-dom'; -import { PageRouting } from '@/const/PageRouting'; -import { Stepper, StepperContent, StepperIndicator, StepperItem, StepperNav, StepperPanel, StepperSeparator, StepperTrigger } from '@/components/ui/stepper'; import * as z from 'zod'; -import { InputOTP, InputOTPGroup, InputOTPSlot } from '@/components/ui/input-otp'; -import { Validator } from '@/util/Validator'; -import { Eye, EyeOff, LoaderCircleIcon, CircleCheckBigIcon } from 'lucide-react'; -import { AccountNetwork } from '@/network/AccountNetwork'; -import { Label } from '@/components/ui/label'; +import { SchedulerDTO as DTO } from '@baekyangdan/core-utils'; const steps = [1, 2, 3, 4]; @@ -35,7 +35,7 @@ export default function ResetPasswordPage() { passwordConfirm: "" } }); - + const { email, code, password, passwordConfirm } = resetPasswordForm.watch(); const moveToLoginPage = useCallback(() => { @@ -63,11 +63,13 @@ export default function ResetPasswordPage() { setIsLoading(true); try { + const data = { + email + } as DTO.SendPasswordResetCodeRequest; - const response = await accountNetwork.sendResetPasswordCode({ email: email }); - const resData = response.data; - - if (!resData.success) { + const response = await accountNetwork.sendPasswordResetCode(data); + + if (!response.success) { resetPasswordForm.setError('email', { message: '서버 오류로 코드 발송에 실패하였습니다. 잠시 후 다시 시도해주십시오.' }); @@ -93,20 +95,19 @@ export default function ResetPasswordPage() { } const data = { - email: email, - code: code - } + email, + code + } as DTO.VerifyPasswordResetCodeRequest; setIsLoading(true); try { - const response = await accountNetwork.verifyResetPasswordCode(data); - const resData = response.data; - console.log(resData); - if (!resData.success || !resData.verified) { + const response = await accountNetwork.verifyPasswordResetCode(data); + + if (!response.success || !response.data.verified) { resetPasswordForm.setError('code', { type: 'value', - message: resData.error + message: response.error }); return; } @@ -131,17 +132,16 @@ export default function ResetPasswordPage() { if (!passwordConfirmValid) return; const data = { - email: email, - password: password - } + email, + password + } as DTO.ResetPasswordRequest; setIsLoading(true); try { const response = await accountNetwork.resetPassword(data); - const resData = response.data; - if (!resData.success) { + if (!response.success) { resetPasswordForm.setError('password', { message: '서버 오류로 비밀번호 변경에 실패하였습니다. 잠시 후 다시 시도해주십시오.' }); @@ -173,7 +173,7 @@ export default function ResetPasswordPage() { } return ( - { - steps.length > step - && - } + steps.length > step + && + } ))} @@ -214,26 +214,26 @@ export default function ResetPasswordPage() { 이메일 - ( - <> - - - - )} - /> - + ( + <> + + + + )} + /> + - 코드 입력
- { showPassword - ? - : + ? + : }
@@ -305,12 +305,12 @@ export default function ResetPasswordPage() { ( + render={({ field, fieldState }) => ( <>
{ showPasswordConfirm - ? - : + ? + : }
@@ -348,7 +348,7 @@ export default function ResetPasswordPage() {
-