From 19604ab66b8c6dfffe8fa560b84b34a28ff21d7f Mon Sep 17 00:00:00 2001 From: duekzim Date: Sun, 3 Nov 2024 01:20:15 +0900 Subject: [PATCH 01/29] feat: admission detail type store --- .../admin/admission-detail-type-store.ts | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 src/store/admin/admission-detail-type-store.ts diff --git a/src/store/admin/admission-detail-type-store.ts b/src/store/admin/admission-detail-type-store.ts new file mode 100644 index 0000000..5fc323d --- /dev/null +++ b/src/store/admin/admission-detail-type-store.ts @@ -0,0 +1,24 @@ +import { create } from 'zustand'; + +export interface AdmissionDetailTypeState { + type: 'SUSI' | 'PYEONIP' | 'JEONGSI'; + detailTypeId: number; + detailTypeName: string; +} + +interface AdmissionDetailTypeStoreState { + detailTypeData: AdmissionDetailTypeState[]; + updateDetailTypeData: (data: AdmissionDetailTypeState[]) => void; + deleteDetailType: (detailTypeId: number) => void; +} + +const useAdmissionDetailTypeStore = create((set) => ({ + detailTypeData: [], + updateDetailTypeData: (data) => set({ detailTypeData: data }), + deleteDetailType: (id) => + set((state) => ({ + detailTypeData: state.detailTypeData.filter((data) => data.detailTypeId !== id), + })), +})); + +export default useAdmissionDetailTypeStore; From 668aab2244ba9684c7160ec7f98b94341753676e Mon Sep 17 00:00:00 2001 From: duekzim Date: Sun, 3 Nov 2024 01:20:38 +0900 Subject: [PATCH 02/29] refactor: detail type interface change --- src/api/admin/admin-admission-type-detail.query.ts | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/src/api/admin/admin-admission-type-detail.query.ts b/src/api/admin/admin-admission-type-detail.query.ts index bad52fe..8bba8fa 100644 --- a/src/api/admin/admin-admission-type-detail.query.ts +++ b/src/api/admin/admin-admission-type-detail.query.ts @@ -1,13 +1,8 @@ +import { AdmissionDetailTypeState } from '../../store/admin/admission-detail-type-store'; import { server_axiosInstance } from '../../utils/axios'; import { getCookie } from '../../utils/cookies'; -import { TypeStatusProps } from './question-type-status/change-type-status'; -export interface AdmissionTypeDetailProps extends TypeStatusProps { - detailTypeId: number; - detailTypeName: string; -} - -export const updateAdmissionTypeDetail = async ({ detailTypeId, detailTypeName }: AdmissionTypeDetailProps) => { +export const updateAdmissionTypeDetail = async ({ detailTypeId, detailTypeName }: AdmissionDetailTypeState) => { try { const response = server_axiosInstance.put( `/api/admin/admissions/${detailTypeId}`, @@ -26,7 +21,7 @@ export const updateAdmissionTypeDetail = async ({ detailTypeId, detailTypeName } } }; -export const deleteAdmissionTypeDetail = async ({ detailTypeId }: AdmissionTypeDetailProps) => { +export const deleteAdmissionTypeDetail = async ({ detailTypeId }: AdmissionDetailTypeState) => { try { const response = server_axiosInstance.delete(`/api/admin/admission/${detailTypeId}`, { headers: { @@ -39,7 +34,7 @@ export const deleteAdmissionTypeDetail = async ({ detailTypeId }: AdmissionTypeD } }; -export const generateAdmissionTypeDetail = async ({ type, detailTypeName }: AdmissionTypeDetailProps) => { +export const generateAdmissionTypeDetail = async ({ type, detailTypeName }: AdmissionDetailTypeState) => { try { const response = server_axiosInstance.put( `/api/admin/admissions/detail`, From ec5e985a92c791bbf1a458caf539dfedafa2bfbb Mon Sep 17 00:00:00 2001 From: duekzim Date: Sun, 3 Nov 2024 01:50:06 +0900 Subject: [PATCH 03/29] feat: detail type check page publish --- src/ui/pages/admin/detail-type-check.tsx | 54 ++++++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 src/ui/pages/admin/detail-type-check.tsx diff --git a/src/ui/pages/admin/detail-type-check.tsx b/src/ui/pages/admin/detail-type-check.tsx new file mode 100644 index 0000000..83b014e --- /dev/null +++ b/src/ui/pages/admin/detail-type-check.tsx @@ -0,0 +1,54 @@ +import { Button, Divider, Popconfirm, Table, TableProps } from 'antd'; +import react, { useMemo } from 'react'; +import useAdmissionDetailTypeStore, { + AdmissionDetailTypeState, +} from '../../../store/admin/admission-detail-type-store'; +import React from 'react'; + +const DetailTypeCheck = () => { + const { detailTypeData, updateDetailTypeData, deleteDetailType } = useAdmissionDetailTypeStore(); + const columns: TableProps['columns'] = useMemo( + () => [ + { + title: '대전형', + dataIndex: 'type', + key: 'type', + }, + { + title: '세부전형', + dataIndex: 'detail-type', + key: 'detail-type', + }, + { + title: '수정', + key: 'edit', + width: 150, + render: () => , + }, + { + title: '삭제', + key: 'action', + width: 150, + render: () => ( + + + + ), + }, + ], + [], + ); + + return ( +
+ 세부 전형 설정 +
+ + + + ); +}; + +export default DetailTypeCheck; From b5d172a8611f87d9ac87d75c7fd18f8db42dc47e Mon Sep 17 00:00:00 2001 From: duekzim Date: Sun, 3 Nov 2024 01:50:25 +0900 Subject: [PATCH 04/29] refactor: add detail type check page --- src/routes/admin-routes.tsx | 2 ++ src/ui/components/admin/slide-menu/slide-menu.tsx | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/routes/admin-routes.tsx b/src/routes/admin-routes.tsx index 21004e7..fcbfe1a 100644 --- a/src/routes/admin-routes.tsx +++ b/src/routes/admin-routes.tsx @@ -8,6 +8,7 @@ import { getCookie } from '../utils/cookies'; import QuestionCheck from '../ui/pages/admin/question-check'; import GenerateQuestion from '../ui/pages/admin/generate-question'; import TypeDisabled from '../ui/pages/admin/type-disabled'; +import DetailTypeCheck from '../ui/pages/admin/detail-type-check'; const AdminRoutes: React.FC = () => { const token = getCookie('accessToken'); return ( @@ -15,6 +16,7 @@ const AdminRoutes: React.FC = () => { } /> } authenticated={token} />}> } /> + } /> } /> } /> } /> diff --git a/src/ui/components/admin/slide-menu/slide-menu.tsx b/src/ui/components/admin/slide-menu/slide-menu.tsx index c999157..d964598 100644 --- a/src/ui/components/admin/slide-menu/slide-menu.tsx +++ b/src/ui/components/admin/slide-menu/slide-menu.tsx @@ -16,6 +16,8 @@ export default function SlideMenu(props: MenuProps) { { key: '/admin/generate/question', label: '질문답변 생성' }, { type: 'divider' }, { key: '/admin/setting/type', label: '전형 활성화 선택' }, + { type: 'divider' }, + { key: '/admin/setting/detail/type', label: '세부 전형 설정' }, ]; }, []); From 2eb2ba737986be1da218b8bdfed76215873ba3c2 Mon Sep 17 00:00:00 2001 From: sangmaaaaan Date: Sun, 3 Nov 2024 17:49:11 +0900 Subject: [PATCH 05/29] Feat: add radix ui library --- package-lock.json | 424 +++++++++++++++++++++++++++++++++++++++++++--- package.json | 1 + 2 files changed, 404 insertions(+), 21 deletions(-) diff --git a/package-lock.json b/package-lock.json index 366ce2a..db4e306 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,6 +9,7 @@ "version": "0.1.0", "dependencies": { "@ant-design/icons": "^5.3.7", + "@radix-ui/react-dropdown-menu": "^2.1.2", "@testing-library/jest-dom": "^5.17.0", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", @@ -3133,6 +3134,40 @@ "integrity": "sha512-cEee/Z+I12mZcFJshKcCqC8tuX5hG3s+d+9nZ3LabqKF1vKdF41B92pJVCBggjAGORAeOzyyDDKrZwIkLffeOQ==", "dev": true }, + "node_modules/@floating-ui/core": { + "version": "1.6.8", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.8.tgz", + "integrity": "sha512-7XJ9cPU+yI2QeLS+FCSlqNFZJq8arvswefkZrYI1yQBbftw6FyrZOxYSh+9S7z7TpeWlRt9zJ5IhM1WIL334jA==", + "dependencies": { + "@floating-ui/utils": "^0.2.8" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.6.12", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.12.tgz", + "integrity": "sha512-NP83c0HjokcGVEMeoStg317VD9W7eDlGK7457dMBANbKA6GJZdc7rjujdgqzTaz93jkGgc5P/jeWbaCHnMNc+w==", + "dependencies": { + "@floating-ui/core": "^1.6.0", + "@floating-ui/utils": "^0.2.8" + } + }, + "node_modules/@floating-ui/react-dom": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.2.tgz", + "integrity": "sha512-06okr5cgPzMNBy+Ycse2A6udMi4bqwW/zgBF/rwjcNqWkyr82Mcg8b0vjX8OJpZFy/FKjJmw6wV7t44kK6kW7A==", + "dependencies": { + "@floating-ui/dom": "^1.0.0" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.2.8", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.8.tgz", + "integrity": "sha512-kym7SodPp8/wloecOpcmSnWJsK7M0E5Wg8UcFA+uO4B9s5d0ywXOEro/8HM9x0rW+TljRzul/14UYz3TleT3ig==" + }, "node_modules/@humanwhocodes/config-array": { "version": "0.11.14", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", @@ -4560,14 +4595,59 @@ "node_modules/@radix-ui/primitive": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.0.tgz", - "integrity": "sha512-4Z8dn6Upk0qk4P74xBhZ6Hd/w0mPEzOOLxy4xiPXOXqjF7jZS0VAKk7/x/H6FyY2zCkYJqePf1G5KmkmNJ4RBA==", - "dev": true + "integrity": "sha512-4Z8dn6Upk0qk4P74xBhZ6Hd/w0mPEzOOLxy4xiPXOXqjF7jZS0VAKk7/x/H6FyY2zCkYJqePf1G5KmkmNJ4RBA==" + }, + "node_modules/@radix-ui/react-arrow": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.0.tgz", + "integrity": "sha512-FmlW1rCg7hBpEBwFbjHwCW6AmWLQM6g/v0Sn8XbP9NvmSZ2San1FpQeyPtufzOMSIx7Y4dzjlHoifhp+7NkZhw==", + "dependencies": { + "@radix-ui/react-primitive": "2.0.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-collection": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.0.tgz", + "integrity": "sha512-GZsZslMJEyo1VKm5L1ZJY8tGDxZNPAoUeQUIbKeJfoi7Q4kmig5AsgLMYYuyYbfjd8fBmFORAIwYAkXMnXZgZw==", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.0", + "@radix-ui/react-context": "1.1.0", + "@radix-ui/react-primitive": "2.0.0", + "@radix-ui/react-slot": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } }, "node_modules/@radix-ui/react-compose-refs": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.0.tgz", "integrity": "sha512-b4inOtiaOnYf9KWyO3jAeeCG6FeyfY6ldiEPanbUjWd+xIk5wZeHa8yVwmrJ2vderhu/BQvzCrJI0lHd+wIiqw==", - "dev": true, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" @@ -4582,7 +4662,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.0.tgz", "integrity": "sha512-OKrckBy+sMEgYM/sMmqmErVn0kZqrHPJze+Ql3DzYsDDp0hl0L62nx/2122/Bvps1qz645jlcu2tD9lrRSdf8A==", - "dev": true, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" @@ -4629,6 +4708,20 @@ } } }, + "node_modules/@radix-ui/react-direction": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.0.tgz", + "integrity": "sha512-BUuBvgThEiAXh2DWu93XsT+a3aWrGqolGlqqw5VU1kG7p/ZH2cuDlM1sRLNnY3QcBS69UIz2mcKhMxDsdewhjg==", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-dismissable-layer": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.0.tgz", @@ -4656,6 +4749,48 @@ } } }, + "node_modules/@radix-ui/react-dropdown-menu": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.1.2.tgz", + "integrity": "sha512-GVZMR+eqK8/Kes0a36Qrv+i20bAPXSn8rCBTHx30w+3ECnR5o3xixAlqcVaYvLeyKUsm0aqyhWfmUcqufM8nYA==", + "dependencies": { + "@radix-ui/primitive": "1.1.0", + "@radix-ui/react-compose-refs": "1.1.0", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-id": "1.1.0", + "@radix-ui/react-menu": "2.1.2", + "@radix-ui/react-primitive": "2.0.0", + "@radix-ui/react-use-controllable-state": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dropdown-menu/node_modules/@radix-ui/react-context": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.1.tgz", + "integrity": "sha512-UASk9zi+crv9WteK/NU4PLvOoL3OuE6BWVKNF6hPRBtYBDXQ2u5iu3O59zUlJiTVvkyuycnqrztsHVJwcK9K+Q==", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-focus-guards": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.0.tgz", @@ -4675,7 +4810,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.0.tgz", "integrity": "sha512-200UD8zylvEyL8Bx+z76RJnASR2gRMuxlgFCPAe/Q/679a/r0eK3MBVYMb7vZODZcffZBdob1EGnky78xmVvcA==", - "dev": true, "dependencies": { "@radix-ui/react-compose-refs": "1.1.0", "@radix-ui/react-primitive": "2.0.0", @@ -4700,7 +4834,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.0.tgz", "integrity": "sha512-EJUrI8yYh7WOjNOqpoJaf1jlFIH2LvtgAl+YcFqNCa+4hj64ZXmPkAKOFs/ukjz3byN6bdb/AVUqHkI8/uWWMA==", - "dev": true, "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.0" }, @@ -4714,6 +4847,200 @@ } } }, + "node_modules/@radix-ui/react-menu": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-menu/-/react-menu-2.1.2.tgz", + "integrity": "sha512-lZ0R4qR2Al6fZ4yCCZzu/ReTFrylHFxIqy7OezIpWF4bL0o9biKo0pFIvkaew3TyZ9Fy5gYVrR5zCGZBVbO1zg==", + "dependencies": { + "@radix-ui/primitive": "1.1.0", + "@radix-ui/react-collection": "1.1.0", + "@radix-ui/react-compose-refs": "1.1.0", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-direction": "1.1.0", + "@radix-ui/react-dismissable-layer": "1.1.1", + "@radix-ui/react-focus-guards": "1.1.1", + "@radix-ui/react-focus-scope": "1.1.0", + "@radix-ui/react-id": "1.1.0", + "@radix-ui/react-popper": "1.2.0", + "@radix-ui/react-portal": "1.1.2", + "@radix-ui/react-presence": "1.1.1", + "@radix-ui/react-primitive": "2.0.0", + "@radix-ui/react-roving-focus": "1.1.0", + "@radix-ui/react-slot": "1.1.0", + "@radix-ui/react-use-callback-ref": "1.1.0", + "aria-hidden": "^1.1.1", + "react-remove-scroll": "2.6.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-menu/node_modules/@radix-ui/react-context": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.1.tgz", + "integrity": "sha512-UASk9zi+crv9WteK/NU4PLvOoL3OuE6BWVKNF6hPRBtYBDXQ2u5iu3O59zUlJiTVvkyuycnqrztsHVJwcK9K+Q==", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-menu/node_modules/@radix-ui/react-dismissable-layer": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.1.tgz", + "integrity": "sha512-QSxg29lfr/xcev6kSz7MAlmDnzbP1eI/Dwn3Tp1ip0KT5CUELsxkekFEMVBEoykI3oV39hKT4TKZzBNMbcTZYQ==", + "dependencies": { + "@radix-ui/primitive": "1.1.0", + "@radix-ui/react-compose-refs": "1.1.0", + "@radix-ui/react-primitive": "2.0.0", + "@radix-ui/react-use-callback-ref": "1.1.0", + "@radix-ui/react-use-escape-keydown": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-menu/node_modules/@radix-ui/react-focus-guards": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.1.tgz", + "integrity": "sha512-pSIwfrT1a6sIoDASCSpFwOasEwKTZWDw/iBdtnqKO7v6FeOzYJ7U53cPzYFVR3geGGXgVHaH+CdngrrAzqUGxg==", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-menu/node_modules/@radix-ui/react-portal": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.2.tgz", + "integrity": "sha512-WeDYLGPxJb/5EGBoedyJbT0MpoULmwnIPMJMSldkuiMsBAv7N1cRdsTWZWht9vpPOiN3qyiGAtbK2is47/uMFg==", + "dependencies": { + "@radix-ui/react-primitive": "2.0.0", + "@radix-ui/react-use-layout-effect": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-menu/node_modules/@radix-ui/react-presence": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.1.tgz", + "integrity": "sha512-IeFXVi4YS1K0wVZzXNrbaaUvIJ3qdY+/Ih4eHFhWA9SwGR9UDX7Ck8abvL57C4cv3wwMvUE0OG69Qc3NCcTe/A==", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.0", + "@radix-ui/react-use-layout-effect": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-menu/node_modules/react-remove-scroll": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.6.0.tgz", + "integrity": "sha512-I2U4JVEsQenxDAKaVa3VZ/JeJZe0/2DxPWL8Tj8yLKctQJQiZM52pn/GWFpSp8dftjM3pSAHVJZscAnC/y+ySQ==", + "dependencies": { + "react-remove-scroll-bar": "^2.3.6", + "react-style-singleton": "^2.2.1", + "tslib": "^2.1.0", + "use-callback-ref": "^1.3.0", + "use-sidecar": "^1.1.2" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-popper": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.0.tgz", + "integrity": "sha512-ZnRMshKF43aBxVWPWvbj21+7TQCvhuULWJ4gNIKYpRlQt5xGRhLx66tMp8pya2UkGHTSlhpXwmjqltDYHhw7Vg==", + "dependencies": { + "@floating-ui/react-dom": "^2.0.0", + "@radix-ui/react-arrow": "1.1.0", + "@radix-ui/react-compose-refs": "1.1.0", + "@radix-ui/react-context": "1.1.0", + "@radix-ui/react-primitive": "2.0.0", + "@radix-ui/react-use-callback-ref": "1.1.0", + "@radix-ui/react-use-layout-effect": "1.1.0", + "@radix-ui/react-use-rect": "1.1.0", + "@radix-ui/react-use-size": "1.1.0", + "@radix-ui/rect": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-portal": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.1.tgz", @@ -4766,7 +5093,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.0.0.tgz", "integrity": "sha512-ZSpFm0/uHa8zTvKBDjLFWLo8dkr4MBsiDLz0g3gMUwqgLHz9rTaRRGYDgvZPtBJgYCBKXkS9fzmoySgr8CO6Cw==", - "dev": true, "dependencies": { "@radix-ui/react-slot": "1.1.0" }, @@ -4785,11 +5111,40 @@ } } }, + "node_modules/@radix-ui/react-roving-focus": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.0.tgz", + "integrity": "sha512-EA6AMGeq9AEeQDeSH0aZgG198qkfHSbvWTf1HvoDmOB5bBG/qTxjYMWUKMnYiV6J/iP/J8MEFSuB2zRU2n7ODA==", + "dependencies": { + "@radix-ui/primitive": "1.1.0", + "@radix-ui/react-collection": "1.1.0", + "@radix-ui/react-compose-refs": "1.1.0", + "@radix-ui/react-context": "1.1.0", + "@radix-ui/react-direction": "1.1.0", + "@radix-ui/react-id": "1.1.0", + "@radix-ui/react-primitive": "2.0.0", + "@radix-ui/react-use-callback-ref": "1.1.0", + "@radix-ui/react-use-controllable-state": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-slot": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.0.tgz", "integrity": "sha512-FUCf5XMfmW4dtYl69pdS4DbxKy8nj4M7SafBgPllysxmdachynNflAdp/gCsnYWNDnge6tI9onzMp5ARYc1KNw==", - "dev": true, "dependencies": { "@radix-ui/react-compose-refs": "1.1.0" }, @@ -4807,7 +5162,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.0.tgz", "integrity": "sha512-CasTfvsy+frcFkbXtSJ2Zu9JHpN8TYKxkgJGWbjiZhFivxaeW7rMeZt7QELGVLaYVfFMsKHjb7Ak0nMEe+2Vfw==", - "dev": true, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" @@ -4822,7 +5176,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.1.0.tgz", "integrity": "sha512-MtfMVJiSr2NjzS0Aa90NPTnvTSg6C/JLCV7ma0W6+OMV78vd8OyRpID+Ng9LxzsPbLeuBnWBA1Nq30AtBIDChw==", - "dev": true, "dependencies": { "@radix-ui/react-use-callback-ref": "1.1.0" }, @@ -4840,7 +5193,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.0.tgz", "integrity": "sha512-L7vwWlR1kTTQ3oh7g1O0CBF3YCyyTj8NmhLR+phShpyA50HCfBFKVJTpshm9PzLiKmehsrQzTYTpX9HvmC9rhw==", - "dev": true, "dependencies": { "@radix-ui/react-use-callback-ref": "1.1.0" }, @@ -4858,7 +5210,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.0.tgz", "integrity": "sha512-+FPE0rOdziWSrH9athwI1R0HDVbWlEhd+FR+aSDk4uWGmSJ9Z54sdZVDQPZAinJhJXwfT+qnj969mCsT2gfm5w==", - "dev": true, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" @@ -4869,6 +5220,45 @@ } } }, + "node_modules/@radix-ui/react-use-rect": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.1.0.tgz", + "integrity": "sha512-0Fmkebhr6PiseyZlYAOtLS+nb7jLmpqTrJyv61Pe68MKYW6OWdRE2kI70TaYY27u7H0lajqM3hSMMLFq18Z7nQ==", + "dependencies": { + "@radix-ui/rect": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-size": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.0.tgz", + "integrity": "sha512-XW3/vWuIXHa+2Uwcc2ABSfcCledmXhhQPlGbfcRXbiUQI5Icjcg19BGCZVKKInYbvUCut/ufbbLLPFC5cbb1hw==", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/rect": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.1.0.tgz", + "integrity": "sha512-A9+lCBZoaMJlVKcRBz2YByCG+Cp2t6nAnMnNba+XiWxnj6r4JUFqfsgwocMBZU9LPtdxC6wB56ySYpc7LQIoJg==" + }, "node_modules/@rc-component/async-validator": { "version": "5.0.4", "resolved": "https://registry.npmjs.org/@rc-component/async-validator/-/async-validator-5.0.4.tgz", @@ -12698,7 +13088,6 @@ "version": "1.2.4", "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.4.tgz", "integrity": "sha512-y+CcFFwelSXpLZk/7fMB2mUbGtX9lKycf1MWJ7CaTIERyitVlyQx6C+sxcROU2BAJ24OiZyK+8wj2i8AlBoS3A==", - "dev": true, "dependencies": { "tslib": "^2.0.0" }, @@ -16093,8 +16482,7 @@ "node_modules/detect-node-es": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz", - "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==", - "dev": true + "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==" }, "node_modules/detect-package-manager": { "version": "2.0.1", @@ -18360,7 +18748,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz", "integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==", - "dev": true, "engines": { "node": ">=6" } @@ -19262,7 +19649,6 @@ "version": "2.2.4", "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", - "dev": true, "dependencies": { "loose-envify": "^1.0.0" } @@ -27417,7 +27803,6 @@ "version": "2.3.6", "resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.6.tgz", "integrity": "sha512-DtSYaao4mBmX+HDo5YWYdBWQwYIQQshUV/dVxFxK+KM26Wjwp1gZ6rv6OC3oujI6Bfu6Xyg3TwK533AQutsn/g==", - "dev": true, "dependencies": { "react-style-singleton": "^2.2.1", "tslib": "^2.0.0" @@ -27541,7 +27926,6 @@ "version": "2.2.1", "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.1.tgz", "integrity": "sha512-ZWj0fHEMyWkHzKYUr2Bs/4zU6XLmq9HsgBURm7g5pAVfyn49DgUiNgY2d4lXRlYSiCif9YBGpQleewkcqddc7g==", - "dev": true, "dependencies": { "get-nonce": "^1.0.0", "invariant": "^2.2.4", @@ -30574,7 +30958,6 @@ "version": "1.3.2", "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.2.tgz", "integrity": "sha512-elOQwe6Q8gqZgDA8mrh44qRTQqpIHDcZ3hXTLjBe1i4ph8XpNJnO+aQf3NaG+lriLopI4HMx9VjQLfPQ6vhnoA==", - "dev": true, "dependencies": { "tslib": "^2.0.0" }, @@ -30595,7 +30978,6 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.2.tgz", "integrity": "sha512-epTbsLuzZ7lPClpz2TyryBfztm7m+28DlEv2ZCQ3MDr5ssiwyOwGH/e5F9CkfWjJ1t4clvI58yF822/GUkjjhw==", - "dev": true, "dependencies": { "detect-node-es": "^1.1.0", "tslib": "^2.0.0" diff --git a/package.json b/package.json index 0af0259..8f8c72a 100644 --- a/package.json +++ b/package.json @@ -4,6 +4,7 @@ "private": true, "dependencies": { "@ant-design/icons": "^5.3.7", + "@radix-ui/react-dropdown-menu": "^2.1.2", "@testing-library/jest-dom": "^5.17.0", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", From 6cd1bf1952c9c14bcf781c9e8bd9e53b862824ee Mon Sep 17 00:00:00 2001 From: sangmaaaaan Date: Sun, 3 Nov 2024 17:50:23 +0900 Subject: [PATCH 06/29] Feat: add dropdown storybook --- .../components/atom/dropdown/dropdown.stories.tsx | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 src/ui/components/atom/dropdown/dropdown.stories.tsx diff --git a/src/ui/components/atom/dropdown/dropdown.stories.tsx b/src/ui/components/atom/dropdown/dropdown.stories.tsx new file mode 100644 index 0000000..5b00997 --- /dev/null +++ b/src/ui/components/atom/dropdown/dropdown.stories.tsx @@ -0,0 +1,15 @@ +import React from 'react'; +import { Meta, StoryFn } from '@storybook/react'; +import Dropdown from './dropdown'; + +export default { + title: 'Components/Dropdown', + component: Dropdown, +} as Meta; + +const Template: StoryFn = (args) => ; + +export const Default = Template.bind({}); +Default.args = { + items: ['일반', '학사', '농어촌학생', '특성화고교', '재외국민', '특성화고등졸재직자'], +}; From 0e5b50fa7db34260e9e1076b71c521d888473579 Mon Sep 17 00:00:00 2001 From: sangmaaaaan Date: Sun, 3 Nov 2024 17:50:38 +0900 Subject: [PATCH 07/29] Feat: add dropdown component --- src/ui/components/atom/dropdown/dropdown.tsx | 29 ++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 src/ui/components/atom/dropdown/dropdown.tsx diff --git a/src/ui/components/atom/dropdown/dropdown.tsx b/src/ui/components/atom/dropdown/dropdown.tsx new file mode 100644 index 0000000..d6c2544 --- /dev/null +++ b/src/ui/components/atom/dropdown/dropdown.tsx @@ -0,0 +1,29 @@ +import React from 'react'; +import * as DropdownMenu from '@radix-ui/react-dropdown-menu'; + +interface DropdownProps { + items: string[]; +} + +const Dropdown: React.FC = ({ items }) => { + return ( + + + + + + + {items.map((item, index) => ( + + {item} + + ))} + + + + ); +}; + +export default Dropdown; From 7ef52a99ba2ec741069183e68c81badba3de8a39 Mon Sep 17 00:00:00 2001 From: Choi JunHo Date: Sun, 3 Nov 2024 23:51:54 +0900 Subject: [PATCH 08/29] feat: add custom modal in admission type detail --- .../pages/admin/detail-type-check/modal.tsx | 91 +++++++++++++++++++ 1 file changed, 91 insertions(+) create mode 100644 src/ui/pages/admin/detail-type-check/modal.tsx diff --git a/src/ui/pages/admin/detail-type-check/modal.tsx b/src/ui/pages/admin/detail-type-check/modal.tsx new file mode 100644 index 0000000..975e5b9 --- /dev/null +++ b/src/ui/pages/admin/detail-type-check/modal.tsx @@ -0,0 +1,91 @@ +import { Button, Modal, Select, Input } from 'antd'; +import React, { useEffect, useState } from 'react'; +import { + generateAdmissionTypeDetail, + updateAdmissionTypeDetail, +} from '../../../../api/admin/admin-admission-type-detail.query'; +import { AdmissionDetailTypeState } from '../../../../store/admin/admission-detail-type-store'; + +interface CustomModalProps { + open: boolean; + setOpen: React.Dispatch>; + isEditMode: boolean; + selectedDetailType?: AdmissionDetailTypeState; + updateDetailTypeData: (data: AdmissionDetailTypeState[]) => void; +} + +const CustomModal = ({ open, setOpen, isEditMode, selectedDetailType }: CustomModalProps) => { + const [loading, setLoading] = useState(false); + const [detailTypeName, setDetailTypeName] = useState(''); + const [selectedType, setSelectedType] = useState<'SUSI' | 'JEONGSI' | 'PYEONIP'>('SUSI'); + + useEffect(() => { + if (isEditMode && selectedDetailType) { + setDetailTypeName(selectedDetailType.name || ''); + setSelectedType(selectedDetailType.type || 'SUSI'); + } else { + setDetailTypeName(''); + setSelectedType('SUSI'); + } + }, [isEditMode, selectedDetailType]); + + const handleSubmit = async () => { + setLoading(true); + try { + if (isEditMode && selectedDetailType) { + await updateAdmissionTypeDetail({ + id: selectedDetailType.id, + name: detailTypeName, + }); + } else { + await generateAdmissionTypeDetail({ type: selectedType, name: detailTypeName }); + } + } catch (error) { + console.error('Error:', error); + } finally { + setLoading(false); + setOpen(false); + } + }; + + return ( + setOpen(false)} + footer={[ + , + , + ]} + > +
+ 전형 선택: + setDetailTypeName(e.target.value)} + /> +
+
+ ); +}; + +export default CustomModal; From 5b53ba1adef0343991031d0db61860d67024f911 Mon Sep 17 00:00:00 2001 From: Choi JunHo Date: Sun, 3 Nov 2024 23:52:21 +0900 Subject: [PATCH 09/29] refactor: detail-type-check folder move and add post,put,delete --- src/ui/pages/admin/detail-type-check.tsx | 54 -------- .../detail-type-check/detail-type-check.tsx | 128 ++++++++++++++++++ 2 files changed, 128 insertions(+), 54 deletions(-) delete mode 100644 src/ui/pages/admin/detail-type-check.tsx create mode 100644 src/ui/pages/admin/detail-type-check/detail-type-check.tsx diff --git a/src/ui/pages/admin/detail-type-check.tsx b/src/ui/pages/admin/detail-type-check.tsx deleted file mode 100644 index 83b014e..0000000 --- a/src/ui/pages/admin/detail-type-check.tsx +++ /dev/null @@ -1,54 +0,0 @@ -import { Button, Divider, Popconfirm, Table, TableProps } from 'antd'; -import react, { useMemo } from 'react'; -import useAdmissionDetailTypeStore, { - AdmissionDetailTypeState, -} from '../../../store/admin/admission-detail-type-store'; -import React from 'react'; - -const DetailTypeCheck = () => { - const { detailTypeData, updateDetailTypeData, deleteDetailType } = useAdmissionDetailTypeStore(); - const columns: TableProps['columns'] = useMemo( - () => [ - { - title: '대전형', - dataIndex: 'type', - key: 'type', - }, - { - title: '세부전형', - dataIndex: 'detail-type', - key: 'detail-type', - }, - { - title: '수정', - key: 'edit', - width: 150, - render: () => , - }, - { - title: '삭제', - key: 'action', - width: 150, - render: () => ( - - - - ), - }, - ], - [], - ); - - return ( -
- 세부 전형 설정 -
-
- - - ); -}; - -export default DetailTypeCheck; diff --git a/src/ui/pages/admin/detail-type-check/detail-type-check.tsx b/src/ui/pages/admin/detail-type-check/detail-type-check.tsx new file mode 100644 index 0000000..93f33dc --- /dev/null +++ b/src/ui/pages/admin/detail-type-check/detail-type-check.tsx @@ -0,0 +1,128 @@ +import { Button, Divider, Popconfirm, Table, TableProps } from 'antd'; +import React, { useEffect, useMemo, useState } from 'react'; +import useAdmissionDetailTypeStore, { + AdmissionDetailTypeState, +} from '../../../../store/admin/admission-detail-type-store'; +import { getAllDetailType } from '../../../../api/get-admission-detail-type.query'; +import CustomModal from './modal'; +import { deleteAdmissionTypeDetail } from '../../../../api/admin/admin-admission-type-detail.query'; + +const DetailTypeCheck = () => { + const { detailTypeData, deleteDetailType, updateDetailTypeData, loading, setLoading } = useAdmissionDetailTypeStore(); + const [modalOpen, setModalOpen] = useState(false); + const [isEditMode, setIsEditMode] = useState(false); + const [selectedDetailType, setSelectedDetailType] = useState(undefined); + + const handleDelete = async (id: number) => { + try { + await deleteAdmissionTypeDetail(id); + deleteDetailType(id); + } catch (error) { + console.error('delete failed', error); + } + }; + useEffect(() => { + const fetchData = async () => { + if (setLoading) setLoading(true); + try { + const response = await getAllDetailType(); + updateDetailTypeData(response); + } catch (error) { + console.error('Error fetching data:', error); + } finally { + if (setLoading) setLoading(false); + } + }; + + fetchData(); + }, [modalOpen]); + + const columns: TableProps['columns'] = useMemo( + () => [ + { + title: '전형분류', + dataIndex: 'type', + key: 'type', + width: 150, + }, + { + title: '세부전형', + dataIndex: 'name', + key: 'name', + editable: true, + }, + { + title: '수정', + key: 'edit', + render: (record: AdmissionDetailTypeState) => ( + + ), + width: 250, + }, + { + title: '삭제', + key: 'action', + width: 250, + render: (record: AdmissionDetailTypeState) => ( + { + console.log(record.id); + if (record.id !== undefined) { + handleDelete(record.id); + } + }} + > + + + ), + }, + ], + [deleteDetailType], + ); + + return ( +
+ 세부 전형 설정 + +
+
({ ...item, key: item.id }))} + loading={loading} + /> + + + + ); +}; + +export default DetailTypeCheck; From cb2c2f0c4f097008f9b2fade284f5f4dff497520 Mon Sep 17 00:00:00 2001 From: Choi JunHo Date: Sun, 3 Nov 2024 23:52:45 +0900 Subject: [PATCH 10/29] refactor: store name change by server --- .../admin/admin-admission-type-detail.query.ts | 16 ++++++++-------- src/store/admin/admission-detail-type-store.ts | 12 ++++++++---- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/src/api/admin/admin-admission-type-detail.query.ts b/src/api/admin/admin-admission-type-detail.query.ts index 8bba8fa..cdb09d8 100644 --- a/src/api/admin/admin-admission-type-detail.query.ts +++ b/src/api/admin/admin-admission-type-detail.query.ts @@ -2,12 +2,12 @@ import { AdmissionDetailTypeState } from '../../store/admin/admission-detail-typ import { server_axiosInstance } from '../../utils/axios'; import { getCookie } from '../../utils/cookies'; -export const updateAdmissionTypeDetail = async ({ detailTypeId, detailTypeName }: AdmissionDetailTypeState) => { +export const updateAdmissionTypeDetail = async ({ id, name }: AdmissionDetailTypeState) => { try { const response = server_axiosInstance.put( - `/api/admin/admissions/${detailTypeId}`, + `/api/admin/admissions/${id}`, { - detailTypeName, + name, }, { headers: { @@ -21,9 +21,9 @@ export const updateAdmissionTypeDetail = async ({ detailTypeId, detailTypeName } } }; -export const deleteAdmissionTypeDetail = async ({ detailTypeId }: AdmissionDetailTypeState) => { +export const deleteAdmissionTypeDetail = async (id: number) => { try { - const response = server_axiosInstance.delete(`/api/admin/admission/${detailTypeId}`, { + const response = server_axiosInstance.delete(`/api/admin/admission/${id}`, { headers: { Authorization: `Bearer ${getCookie('accessToken')}`, }, @@ -34,12 +34,12 @@ export const deleteAdmissionTypeDetail = async ({ detailTypeId }: AdmissionDetai } }; -export const generateAdmissionTypeDetail = async ({ type, detailTypeName }: AdmissionDetailTypeState) => { +export const generateAdmissionTypeDetail = async ({ type, name }: AdmissionDetailTypeState) => { try { - const response = server_axiosInstance.put( + const response = server_axiosInstance.post( `/api/admin/admissions/detail`, { - detail: detailTypeName, + detail: name, type: type, }, { diff --git a/src/store/admin/admission-detail-type-store.ts b/src/store/admin/admission-detail-type-store.ts index 5fc323d..ea73903 100644 --- a/src/store/admin/admission-detail-type-store.ts +++ b/src/store/admin/admission-detail-type-store.ts @@ -1,15 +1,17 @@ import { create } from 'zustand'; export interface AdmissionDetailTypeState { - type: 'SUSI' | 'PYEONIP' | 'JEONGSI'; - detailTypeId: number; - detailTypeName: string; + type?: 'SUSI' | 'PYEONIP' | 'JEONGSI'; + id?: number; + name?: string; } interface AdmissionDetailTypeStoreState { detailTypeData: AdmissionDetailTypeState[]; updateDetailTypeData: (data: AdmissionDetailTypeState[]) => void; deleteDetailType: (detailTypeId: number) => void; + loading: boolean; + setLoading: (load: boolean) => void; } const useAdmissionDetailTypeStore = create((set) => ({ @@ -17,8 +19,10 @@ const useAdmissionDetailTypeStore = create((set) updateDetailTypeData: (data) => set({ detailTypeData: data }), deleteDetailType: (id) => set((state) => ({ - detailTypeData: state.detailTypeData.filter((data) => data.detailTypeId !== id), + detailTypeData: state.detailTypeData.filter((data) => data.id !== id), })), + loading: false, + setLoading: (load) => set({ loading: load }), })); export default useAdmissionDetailTypeStore; From 91dd1216603d2b4a593368e0fa164f7ced0c2319 Mon Sep 17 00:00:00 2001 From: Choi JunHo Date: Sun, 3 Nov 2024 23:52:58 +0900 Subject: [PATCH 11/29] refactor: import route change --- src/routes/admin-routes.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/routes/admin-routes.tsx b/src/routes/admin-routes.tsx index fcbfe1a..7f78340 100644 --- a/src/routes/admin-routes.tsx +++ b/src/routes/admin-routes.tsx @@ -8,7 +8,7 @@ import { getCookie } from '../utils/cookies'; import QuestionCheck from '../ui/pages/admin/question-check'; import GenerateQuestion from '../ui/pages/admin/generate-question'; import TypeDisabled from '../ui/pages/admin/type-disabled'; -import DetailTypeCheck from '../ui/pages/admin/detail-type-check'; +import DetailTypeCheck from '../ui/pages/admin/detail-type-check/detail-type-check'; const AdminRoutes: React.FC = () => { const token = getCookie('accessToken'); return ( From d73bcd16fee1f1a26647e6ea625b313fe1eac5a4 Mon Sep 17 00:00:00 2001 From: Choi JunHo Date: Sun, 3 Nov 2024 23:58:31 +0900 Subject: [PATCH 12/29] refactor: add type mapping function --- src/ui/pages/admin/detail-type-check/detail-type-check.tsx | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/ui/pages/admin/detail-type-check/detail-type-check.tsx b/src/ui/pages/admin/detail-type-check/detail-type-check.tsx index 93f33dc..3a78f91 100644 --- a/src/ui/pages/admin/detail-type-check/detail-type-check.tsx +++ b/src/ui/pages/admin/detail-type-check/detail-type-check.tsx @@ -37,6 +37,12 @@ const DetailTypeCheck = () => { fetchData(); }, [modalOpen]); + const typeMapping: Record = { + SUSI: '수시', + JEONGSI: '정시', + PYEONIP: '편입', + }; + const columns: TableProps['columns'] = useMemo( () => [ { @@ -44,6 +50,7 @@ const DetailTypeCheck = () => { dataIndex: 'type', key: 'type', width: 150, + render: (type: string) => typeMapping[type] || type, }, { title: '세부전형', From 19d35a605876dfe016c1255324634e7e95b92bc5 Mon Sep 17 00:00:00 2001 From: Choi JunHo Date: Mon, 4 Nov 2024 00:36:58 +0900 Subject: [PATCH 13/29] refactor: add filter method --- .../admin/detail-type-check/detail-type-check.tsx | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/ui/pages/admin/detail-type-check/detail-type-check.tsx b/src/ui/pages/admin/detail-type-check/detail-type-check.tsx index 3a78f91..d0c6602 100644 --- a/src/ui/pages/admin/detail-type-check/detail-type-check.tsx +++ b/src/ui/pages/admin/detail-type-check/detail-type-check.tsx @@ -51,6 +51,21 @@ const DetailTypeCheck = () => { key: 'type', width: 150, render: (type: string) => typeMapping[type] || type, + filters: [ + { + text: '수시', + value: 'SUSI', + }, + { + text: '정시', + value: 'JEONGSI', + }, + { + text: '편입', + value: 'PYEONIP', + }, + ], + onFilter: (value, record) => record.type === value, }, { title: '세부전형', From 5b7d527acc13ed43d2bf7c3095fc83e1e3cf2023 Mon Sep 17 00:00:00 2001 From: Choi JunHo Date: Mon, 4 Nov 2024 00:37:26 +0900 Subject: [PATCH 14/29] refactor: delete url path corrections --- src/api/admin/admin-admission-type-detail.query.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/api/admin/admin-admission-type-detail.query.ts b/src/api/admin/admin-admission-type-detail.query.ts index cdb09d8..cd145fa 100644 --- a/src/api/admin/admin-admission-type-detail.query.ts +++ b/src/api/admin/admin-admission-type-detail.query.ts @@ -23,7 +23,7 @@ export const updateAdmissionTypeDetail = async ({ id, name }: AdmissionDetailTyp export const deleteAdmissionTypeDetail = async (id: number) => { try { - const response = server_axiosInstance.delete(`/api/admin/admission/${id}`, { + const response = server_axiosInstance.delete(`/api/admin/admissions/${id}`, { headers: { Authorization: `Bearer ${getCookie('accessToken')}`, }, From 6701b1c30a876efe07461b1c20e2414d87bd1221 Mon Sep 17 00:00:00 2001 From: Choi JunHo Date: Mon, 4 Nov 2024 00:50:54 +0900 Subject: [PATCH 15/29] refactor: add table sorter and default sorter --- src/ui/pages/admin/detail-type-check/detail-type-check.tsx | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/ui/pages/admin/detail-type-check/detail-type-check.tsx b/src/ui/pages/admin/detail-type-check/detail-type-check.tsx index d0c6602..c175192 100644 --- a/src/ui/pages/admin/detail-type-check/detail-type-check.tsx +++ b/src/ui/pages/admin/detail-type-check/detail-type-check.tsx @@ -66,6 +66,13 @@ const DetailTypeCheck = () => { }, ], onFilter: (value, record) => record.type === value, + sorter: (a, b) => { + if (a.name !== undefined && b.name !== undefined) { + return a.name.localeCompare(b.name); + } + return 0; + }, + defaultSortOrder: 'descend', }, { title: '세부전형', From 264594ded8bdd95b28912d657d386be545f111db Mon Sep 17 00:00:00 2001 From: Choi JunHo Date: Mon, 4 Nov 2024 19:38:19 +0900 Subject: [PATCH 16/29] faat: add detail name customhook --- src/hooks/use-detail-type-prompt.hooks.ts | 47 +++++++++++++++++++++++ src/store/user-detail-type-store.ts | 22 +++++++++++ 2 files changed, 69 insertions(+) create mode 100644 src/hooks/use-detail-type-prompt.hooks.ts create mode 100644 src/store/user-detail-type-store.ts diff --git a/src/hooks/use-detail-type-prompt.hooks.ts b/src/hooks/use-detail-type-prompt.hooks.ts new file mode 100644 index 0000000..85125f6 --- /dev/null +++ b/src/hooks/use-detail-type-prompt.hooks.ts @@ -0,0 +1,47 @@ +import { useEffect, useState } from 'react'; +import { TypeStatusProps } from '../api/admin/question-type-status/change-type-status'; +import { getDetailType } from '../api/get-admission-detail-type.query'; +import { useUserDetailTypeStore } from '../store/user-detail-type-store'; + +export const useDetailTypePrompt = ({ type }: TypeStatusProps) => { + const [middleNameArray, setMiddleNameArray] = useState([]); + const [lastNameArray, setLastNameArray] = useState([]); + const { detailTypeData, updateDetailTypeData } = useUserDetailTypeStore(); + + useEffect(() => { + const fetchDetailType = async () => { + try { + const response = await getDetailType({ type }); + updateDetailTypeData(response); + } catch (error) { + console.error('Fetching Failed'); + } + }; + + fetchDetailType(); + }, [type, updateDetailTypeData]); + + useEffect(() => { + if (detailTypeData.length > 0) { + const middleNameSet = new Set(); + const lastNameArrayTemp: string[] = []; + + detailTypeData.forEach((item) => { + const parts = item.name.split('('); + const middle = parts[0]; + middleNameSet.add(middle); + + const last = parts.slice(1).join('(').replace(/\)$/, ''); + lastNameArrayTemp.push(last); + }); + + setMiddleNameArray(Array.from(middleNameSet)); + setLastNameArray(lastNameArrayTemp); + } + }, [detailTypeData]); + + return { + middleNameArray, + lastNameArray, + }; +}; diff --git a/src/store/user-detail-type-store.ts b/src/store/user-detail-type-store.ts new file mode 100644 index 0000000..d5d94c5 --- /dev/null +++ b/src/store/user-detail-type-store.ts @@ -0,0 +1,22 @@ +import { create } from 'zustand'; + +//TSK-53 admission-detail-type-store.ts와 일부 겹침 나중에 병합 필요성 +export interface UserDetailTypeState { + type: 'SUSI' | 'JEONGSI' | 'PYEONIP'; + id: number; + name: string; +} + +interface UserDetailTypeStoreState { + detailTypeData: UserDetailTypeState[]; + updateDetailTypeData: (data: UserDetailTypeState[]) => void; + loading: boolean; + setLoading: (load: boolean) => void; +} + +export const useUserDetailTypeStore = create((set) => ({ + detailTypeData: [], + loading: false, + updateDetailTypeData: (data) => set({ detailTypeData: data }), + setLoading: (load) => set({ loading: load }), +})); From 1d52b7f4e791078956e9cc7b451b7e67db6f2f99 Mon Sep 17 00:00:00 2001 From: sangmaaaaan Date: Mon, 4 Nov 2024 21:49:26 +0900 Subject: [PATCH 17/29] Feat: add react-icons dependency --- package-lock.json | 9 +++++++++ package.json | 1 + 2 files changed, 10 insertions(+) diff --git a/package-lock.json b/package-lock.json index db4e306..afef438 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "dependencies": { "@ant-design/icons": "^5.3.7", "@radix-ui/react-dropdown-menu": "^2.1.2", + "@radix-ui/react-icons": "^1.3.1", "@testing-library/jest-dom": "^5.17.0", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", @@ -4830,6 +4831,14 @@ } } }, + "node_modules/@radix-ui/react-icons": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-icons/-/react-icons-1.3.1.tgz", + "integrity": "sha512-QvYompk0X+8Yjlo/Fv4McrzxohDdM5GgLHyQcPpcsPvlOSXCGFjdbuyGL5dzRbg0GpknAjQJJZzdiRK7iWVuFQ==", + "peerDependencies": { + "react": "^16.x || ^17.x || ^18.x || ^19.x" + } + }, "node_modules/@radix-ui/react-id": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.0.tgz", diff --git a/package.json b/package.json index 8f8c72a..8119945 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,7 @@ "dependencies": { "@ant-design/icons": "^5.3.7", "@radix-ui/react-dropdown-menu": "^2.1.2", + "@radix-ui/react-icons": "^1.3.1", "@testing-library/jest-dom": "^5.17.0", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", From 8e5d26d899694a62cdc78969dbba60688c7f4691 Mon Sep 17 00:00:00 2001 From: sangmaaaaan Date: Mon, 4 Nov 2024 21:49:59 +0900 Subject: [PATCH 18/29] Feat: add detail type items --- .../atom/dropdown/dropdown.stories.tsx | 58 ++++++++++++++++++- 1 file changed, 55 insertions(+), 3 deletions(-) diff --git a/src/ui/components/atom/dropdown/dropdown.stories.tsx b/src/ui/components/atom/dropdown/dropdown.stories.tsx index 5b00997..41640c3 100644 --- a/src/ui/components/atom/dropdown/dropdown.stories.tsx +++ b/src/ui/components/atom/dropdown/dropdown.stories.tsx @@ -5,11 +5,63 @@ import Dropdown from './dropdown'; export default { title: 'Components/Dropdown', component: Dropdown, + argTypes: { + type: { + control: { type: 'select' }, + options: ['SUSI', 'JEONGSI', 'PYEONIP'], + }, + }, } as Meta; const Template: StoryFn = (args) => ; -export const Default = Template.bind({}); -Default.args = { - items: ['일반', '학사', '농어촌학생', '특성화고교', '재외국민', '특성화고등졸재직자'], +export const SUSI = Template.bind({}); +SUSI.args = { + type: 'SUSI', + items: [ + { + middleName: '학생부교과', + lastNames: ['학교장추천', '교과면접', '기회균형', '특성화고교', '만학도', '특성화고등졸재직자', '특수교육대상자'], + }, + { + middleName: '학생부종합', + lastNames: ['명지인재면접', '명지인재서류', '크리스천리더', '사회적배려대상자', '농어촌학생'], + }, + { + middleName: '실기/실적', + lastNames: ['실기우수자', '특기자-문학/체육'], + }, + ], +}; + +export const JEONGSI = Template.bind({}); +JEONGSI.args = { + type: 'JEONGSI', + items: [ + { + middleName: '수능', + lastNames: ['일반-가/나/다', '실기-가/나/다', '농어촌학생', '특성화고교'], + }, + { + middleName: '실기/실적', + lastNames: ['실기우수자'], + }, + { + middleName: '학생부교과', + lastNames: ['만학도', '특성화고등졸재직자'], + }, + ], +}; + +export const PYEONIP = Template.bind({}); +PYEONIP.args = { + type: 'PYEONIP', + items: [ + { middleName: '일반', lastNames: [] }, + { middleName: '학사', lastNames: [] }, + { middleName: '농어촌학생', lastNames: [] }, + { middleName: '특성화고교', lastNames: [] }, + { middleName: '재외국민', lastNames: [] }, + { middleName: '특성화고등졸재직자', lastNames: [] }, + ], }; From abf84b9f88a4cccfd8ecd906c52b464ea29b2866 Mon Sep 17 00:00:00 2001 From: sangmaaaaan Date: Mon, 4 Nov 2024 21:50:44 +0900 Subject: [PATCH 19/29] Refactor: fix detail type props --- src/ui/components/atom/dropdown/dropdown.tsx | 44 ++++++++++++++------ 1 file changed, 32 insertions(+), 12 deletions(-) diff --git a/src/ui/components/atom/dropdown/dropdown.tsx b/src/ui/components/atom/dropdown/dropdown.tsx index d6c2544..4080faf 100644 --- a/src/ui/components/atom/dropdown/dropdown.tsx +++ b/src/ui/components/atom/dropdown/dropdown.tsx @@ -1,25 +1,45 @@ import React from 'react'; import * as DropdownMenu from '@radix-ui/react-dropdown-menu'; +import { ChevronRightIcon } from '@radix-ui/react-icons'; interface DropdownProps { - items: string[]; + type: 'SUSI' | 'JEONGSI' | 'PYEONIP'; + items: { middleName: string; lastNames: string[] }[]; } -const Dropdown: React.FC = ({ items }) => { +const Dropdown: React.FC = ({ type, items }) => { return ( - + - + - + {type === 'PYEONIP' ? // 편입의 경우, 한 번에 전체 목록 표시 items.map((item, index) => ( - + handleNameClick(item.middleName)} + > {item.middleName} )) @@ -30,9 +46,13 @@ const Dropdown: React.FC = ({ type, items }) => { - + {item.lastNames.map((lastName, subIndex) => ( - + handleNameClick(lastName)} + > {lastName} ))} From 838b70a66bbfdb1bc68489b71019f01b1448f3aa Mon Sep 17 00:00:00 2001 From: Choi JunHo Date: Tue, 5 Nov 2024 23:51:52 +0900 Subject: [PATCH 27/29] refactor: change chat form conditional --- src/ui/pages/maru-egg.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/ui/pages/maru-egg.tsx b/src/ui/pages/maru-egg.tsx index 1a3596d..5871d92 100644 --- a/src/ui/pages/maru-egg.tsx +++ b/src/ui/pages/maru-egg.tsx @@ -5,9 +5,11 @@ import useTypeStore from '../../store/type-category-store'; import ChatForm from '../components/molecule/chat-form/chat-form'; import ChatSection from '../components/molecule/chat-section/chat-section'; import Onboarding from '../components/molecule/onboarding/onboarding'; +import { useUserDetailTypeStore } from '../../store/user-detail-type-store'; const MaruEgg: React.FC = () => { - const { type, category } = useTypeStore(); + const { type } = useTypeStore(); + const { selectedName } = useUserDetailTypeStore(); const [showOnboarding, setShowOnboarding] = useState(false); useEffect(() => { @@ -30,7 +32,7 @@ const MaruEgg: React.FC = () => { {showOnboarding && }
- {type !== undefined && category !== undefined && ( + {selectedName !== '' && (
From dd75275f7d1d2713df0122d4ffd94b25fc7f75bb Mon Sep 17 00:00:00 2001 From: Choi JunHo Date: Tue, 5 Nov 2024 23:52:07 +0900 Subject: [PATCH 28/29] refactor: add handleDetailTypeButtonClick --- src/hooks/use-preset-button.hooks.ts | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/hooks/use-preset-button.hooks.ts b/src/hooks/use-preset-button.hooks.ts index b563dff..d43e4b7 100644 --- a/src/hooks/use-preset-button.hooks.ts +++ b/src/hooks/use-preset-button.hooks.ts @@ -46,9 +46,24 @@ const usePresetButton = () => { } }; + const handleDetailTypeButtonClick = async (detailTypeName: string) => { + try { + addMessage({ content: detailTypeName, role: 'user' }); + addMessage({ content: 'loading', role: 'system' }); + setLoading(true); + + const response = await fetchResponse(`${detailTypeName}에 대해 설명해줘`); + updateStateWithResponse(response); + } catch (error) { + setLoading(false); + updateLastMessage('답변 생성에 실패했습니다. 새로고침해주세요'); + } + }; + return { handleReferenceButtonClick, handleButtonClick, + handleDetailTypeButtonClick, }; }; From 3cee920c3094f40af47d69ef3adc83b5c7ccaca3 Mon Sep 17 00:00:00 2001 From: Choi JunHo Date: Tue, 5 Nov 2024 23:52:31 +0900 Subject: [PATCH 29/29] refactor: chat section refactor --- .../molecule/chat-section/chat-section.tsx | 62 ++++++++----------- 1 file changed, 25 insertions(+), 37 deletions(-) diff --git a/src/ui/components/molecule/chat-section/chat-section.tsx b/src/ui/components/molecule/chat-section/chat-section.tsx index 3d375ad..ef7ff99 100644 --- a/src/ui/components/molecule/chat-section/chat-section.tsx +++ b/src/ui/components/molecule/chat-section/chat-section.tsx @@ -2,13 +2,14 @@ import * as React from 'react'; import ChatCard from '../../atom/chat-card/chat-card'; import useTypeStore from '../../../../store/type-category-store'; import useChatStore from '../../../../store/chat-store'; -import useChatSection from '../../../../hooks/use-chat-section.hooks'; import { useTypeDisabledStore } from '../../../../store/type-disabled-store'; import { getTypeStatus } from '../../../../api/admin/question-type-status/get-type-status'; import useMessage from 'antd/es/message/useMessage'; import { TypePresetButtons } from '../../user-domain/type-preset-buttons'; -import { CategoryPresetButtons } from '../../user-domain/category-preset-buttons'; import { QuestionPresetButtons } from '../../user-domain/question-preset-buttons'; +import Dropdown from '../../atom/dropdown/dropdown'; +import { getDetailType } from '../../../../api/get-admission-detail-type.query'; +import { useUserDetailTypeStore } from '../../../../store/user-detail-type-store'; const ChatSection: React.FC = () => { const { type, category } = useTypeStore(); @@ -16,6 +17,7 @@ const ChatSection: React.FC = () => { const { activeSusi, activeJeongsi, activePyeonip, setSusiDisabled, setJeongsiDisabled, setPyeonipDisabled } = useTypeDisabledStore(); const [messageApi, contextHolder] = useMessage(); + const { updateDetailTypeData, itemsArray, selectedName } = useUserDetailTypeStore(); const messageEndRef = React.useRef(null); @@ -93,6 +95,21 @@ const ChatSection: React.FC = () => { updateCategoryStatus(); }, []); + React.useEffect(() => { + const fetchDetailType = async () => { + try { + if (type !== undefined) { + const response = await getDetailType({ type }); + updateDetailTypeData(response); + } + } catch (error) { + console.error('Fetching Failed', error); + } + }; + + fetchDetailType(); + }, [type]); + React.useEffect(() => { showCategoryStatus(); }, [activeSusi, activeJeongsi, activePyeonip]); @@ -108,50 +125,21 @@ const ChatSection: React.FC = () => { role="system" /> - {type && } {type && ( <> + - + content={`어떤 세부 학과가 궁금하신가요? 아래에서 세부 전형을 선택해주세요!`} + > +
{selectedName ? <> : }
)} - {category && ( - - )} - {type && category && ( - - )} + {messages.map((msg, index) => ( ))} - {category && } + {selectedName && }
);