From 8f77ccfbf3463d14caacbea4cb2f7afd512c0e1b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=ED=95=98=EC=A7=80=EB=AF=BC?=
<163178666+JiiminHa@users.noreply.github.com>
Date: Mon, 11 Aug 2025 02:29:32 +0900
Subject: [PATCH 01/42] =?UTF-8?q?chore/#14:=20assignment=20=EA=B4=80?=
=?UTF-8?q?=EB=A0=A8=20=ED=8C=8C=EC=9D=BC=EB=93=A4=20assignments=20?=
=?UTF-8?q?=ED=8F=B4=EB=8D=94=EB=A1=9C=20=EC=9D=B4=EB=8F=99?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/App.tsx | 2 +-
src/pages/admin/{ => assignments}/AssignmentSelectPage.tsx | 6 +++---
src/pages/admin/{ => assignments}/AssignmentsPage.tsx | 0
3 files changed, 4 insertions(+), 4 deletions(-)
rename src/pages/admin/{ => assignments}/AssignmentSelectPage.tsx (80%)
rename src/pages/admin/{ => assignments}/AssignmentsPage.tsx (100%)
diff --git a/src/App.tsx b/src/App.tsx
index a7039c3..09ef00f 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -4,7 +4,7 @@ import LandingPage from './pages/common/LandingPage';
import UserIdInputPage from './pages/common/UserIdInputPage';
import Dashboard from './pages/common/Dashboard';
import AssignmentsPage from './pages/admin/AssignmentsPage';
-import AssignmentSelectPage from './pages/admin/AssignmentSelectPage';
+import AssignmentSelectPage from './pages/admin/assignments/AssignmentSelectPage';
function App() {
return (
diff --git a/src/pages/admin/AssignmentSelectPage.tsx b/src/pages/admin/assignments/AssignmentSelectPage.tsx
similarity index 80%
rename from src/pages/admin/AssignmentSelectPage.tsx
rename to src/pages/admin/assignments/AssignmentSelectPage.tsx
index fb27221..f79d1ae 100644
--- a/src/pages/admin/AssignmentSelectPage.tsx
+++ b/src/pages/admin/assignments/AssignmentSelectPage.tsx
@@ -1,8 +1,8 @@
-import {coursesResponse} from '../../components/admin/assignments/dummy/response';
+import {coursesResponse} from '../../../components/admin/assignments/dummy/response';
import {useState} from 'react';
import {useParams} from 'react-router-dom';
-import type {Assignment} from '../../components/admin/assignments/dummy/types';
-import AssignmentPageLayout from '../../components/admin/assignments/AssignmentPageLayout';
+import type {Assignment} from '../../../components/admin/assignments/dummy/types';
+import AssignmentPageLayout from '../../../components/admin/assignments/AssignmentPageLayout';
const AssignmentSelectPage = () => {
// url에서 course id 가져오기
diff --git a/src/pages/admin/AssignmentsPage.tsx b/src/pages/admin/assignments/AssignmentsPage.tsx
similarity index 100%
rename from src/pages/admin/AssignmentsPage.tsx
rename to src/pages/admin/assignments/AssignmentsPage.tsx
From 44700df2af93bb1314ab8a3509ee738c7ce8045a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=ED=95=98=EC=A7=80=EB=AF=BC?=
<163178666+JiiminHa@users.noreply.github.com>
Date: Wed, 3 Sep 2025 02:22:50 +0900
Subject: [PATCH 02/42] =?UTF-8?q?#14=20chore:=20vite=EC=97=90=EC=84=9C=20@?=
=?UTF-8?q?alias=20=EA=B2=BD=EB=A1=9C=20=EC=84=A4=EC=A0=95=20=EC=B6=94?=
=?UTF-8?q?=EA=B0=80?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
package-lock.json | 105 ++++++++++++++++++++++++++++++++++++++++++++-
package.json | 5 ++-
tsconfig.app.json | 10 ++++-
tsconfig.node.json | 8 +++-
vite.config.ts | 6 +++
5 files changed, 129 insertions(+), 5 deletions(-)
diff --git a/package-lock.json b/package-lock.json
index 52bd80d..0c36197 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -16,6 +16,7 @@
},
"devDependencies": {
"@eslint/js": "^9.29.0",
+ "@types/node": "^24.2.1",
"@types/react": "^19.1.8",
"@types/react-dom": "^19.1.6",
"@vitejs/plugin-react": "^4.5.2",
@@ -23,10 +24,12 @@
"eslint-plugin-react-hooks": "^5.2.0",
"eslint-plugin-react-refresh": "^0.4.20",
"globals": "^16.2.0",
+ "tsconfig-paths": "^4.2.0",
"typescript": "~5.8.3",
"typescript-eslint": "^8.34.1",
"vite": "^7.0.0",
- "vite-plugin-svgr": "^4.3.0"
+ "vite-plugin-svgr": "^4.3.0",
+ "vite-tsconfig-paths": "^5.1.4"
}
},
"node_modules/@ampproject/remapping": {
@@ -1890,6 +1893,16 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/@types/node": {
+ "version": "24.2.1",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-24.2.1.tgz",
+ "integrity": "sha512-DRh5K+ka5eJic8CjH7td8QpYEV6Zo10gfRkjHCO3weqZHWDtAaSTFtl4+VMqOJ4N5jcuhZ9/l+yy8rVgw7BQeQ==",
+ "devOptional": true,
+ "license": "MIT",
+ "dependencies": {
+ "undici-types": "~7.10.0"
+ }
+ },
"node_modules/@types/react": {
"version": "19.1.8",
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.8.tgz",
@@ -3001,6 +3014,13 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/globrex": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/globrex/-/globrex-0.1.2.tgz",
+ "integrity": "sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/graceful-fs": {
"version": "4.2.11",
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
@@ -3539,6 +3559,16 @@
"node": "*"
}
},
+ "node_modules/minimist": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
+ "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
"node_modules/minipass": {
"version": "7.1.2",
"resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz",
@@ -4043,6 +4073,16 @@
"node": ">=0.10.0"
}
},
+ "node_modules/strip-bom": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
+ "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
"node_modules/strip-json-comments": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
@@ -4185,6 +4225,42 @@
"typescript": ">=4.8.4"
}
},
+ "node_modules/tsconfck": {
+ "version": "3.1.6",
+ "resolved": "https://registry.npmjs.org/tsconfck/-/tsconfck-3.1.6.tgz",
+ "integrity": "sha512-ks6Vjr/jEw0P1gmOVwutM3B7fWxoWBL2KRDb1JfqGVawBmO5UsvmWOQFGHBPl5yxYz4eERr19E6L7NMv+Fej4w==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "tsconfck": "bin/tsconfck.js"
+ },
+ "engines": {
+ "node": "^18 || >=20"
+ },
+ "peerDependencies": {
+ "typescript": "^5.0.0"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/tsconfig-paths": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-4.2.0.tgz",
+ "integrity": "sha512-NoZ4roiN7LnbKn9QqE1amc9DJfzvZXxF4xDavcOWt1BPkdx+m+0gJuPM+S0vCe7zTJMYUP0R8pO2XMr+Y8oLIg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "json5": "^2.2.2",
+ "minimist": "^1.2.6",
+ "strip-bom": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
"node_modules/tslib": {
"version": "2.8.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
@@ -4242,6 +4318,13 @@
"typescript": ">=4.8.4 <5.9.0"
}
},
+ "node_modules/undici-types": {
+ "version": "7.10.0",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.10.0.tgz",
+ "integrity": "sha512-t5Fy/nfn+14LuOc2KNYg75vZqClpAiqscVvMygNnlsHBFpSXdJaYtXMcdNLpl/Qvc3P2cB3s6lOV51nqsFq4ag==",
+ "devOptional": true,
+ "license": "MIT"
+ },
"node_modules/update-browserslist-db": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz",
@@ -4372,6 +4455,26 @@
"vite": ">=2.6.0"
}
},
+ "node_modules/vite-tsconfig-paths": {
+ "version": "5.1.4",
+ "resolved": "https://registry.npmjs.org/vite-tsconfig-paths/-/vite-tsconfig-paths-5.1.4.tgz",
+ "integrity": "sha512-cYj0LRuLV2c2sMqhqhGpaO3LretdtMn/BVX4cPLanIZuwwrkVl+lK84E/miEXkCHWXuq65rhNN4rXsBcOB3S4w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "debug": "^4.1.1",
+ "globrex": "^0.1.2",
+ "tsconfck": "^3.0.3"
+ },
+ "peerDependencies": {
+ "vite": "*"
+ },
+ "peerDependenciesMeta": {
+ "vite": {
+ "optional": true
+ }
+ }
+ },
"node_modules/vite/node_modules/fdir": {
"version": "6.4.6",
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.6.tgz",
diff --git a/package.json b/package.json
index df7e1d7..dfa3505 100644
--- a/package.json
+++ b/package.json
@@ -19,6 +19,7 @@
},
"devDependencies": {
"@eslint/js": "^9.29.0",
+ "@types/node": "^24.2.1",
"@types/react": "^19.1.8",
"@types/react-dom": "^19.1.6",
"@vitejs/plugin-react": "^4.5.2",
@@ -26,9 +27,11 @@
"eslint-plugin-react-hooks": "^5.2.0",
"eslint-plugin-react-refresh": "^0.4.20",
"globals": "^16.2.0",
+ "tsconfig-paths": "^4.2.0",
"typescript": "~5.8.3",
"typescript-eslint": "^8.34.1",
"vite": "^7.0.0",
- "vite-plugin-svgr": "^4.3.0"
+ "vite-plugin-svgr": "^4.3.0",
+ "vite-tsconfig-paths": "^5.1.4"
}
}
diff --git a/tsconfig.app.json b/tsconfig.app.json
index 7a844e2..1db5b44 100644
--- a/tsconfig.app.json
+++ b/tsconfig.app.json
@@ -23,7 +23,13 @@
"noFallthroughCasesInSwitch": true,
"noUncheckedSideEffectImports": true,
- "types": ["vite-plugin-svgr/client"]
+ "types": ["vite-plugin-svgr/client"],
+
+ /* Alias */
+ "baseUrl": ".",
+ "paths": {
+ "@/*": ["./src/*"]
+ }
},
- "include": ["src", "custom.d.ts"]
+ "include": ["src"]
}
diff --git a/tsconfig.node.json b/tsconfig.node.json
index f85a399..5263d90 100644
--- a/tsconfig.node.json
+++ b/tsconfig.node.json
@@ -19,7 +19,13 @@
"noUnusedParameters": true,
"erasableSyntaxOnly": true,
"noFallthroughCasesInSwitch": true,
- "noUncheckedSideEffectImports": true
+ "noUncheckedSideEffectImports": true,
+
+ /* Alias */
+ "baseUrl": ".",
+ "paths": {
+ "@/*": ["./src/*"]
+ }
},
"include": ["vite.config.ts"]
}
diff --git a/vite.config.ts b/vite.config.ts
index 08835a7..1cd313a 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -2,6 +2,7 @@ import {defineConfig} from 'vite';
import react from '@vitejs/plugin-react';
import tailwindcss from '@tailwindcss/vite';
import svgr from 'vite-plugin-svgr';
+import path from 'path';
// https://vite.dev/config/
export default defineConfig({
@@ -14,4 +15,9 @@ export default defineConfig({
},
}),
],
+ resolve: {
+ alias: {
+ '@': path.resolve(__dirname, './src'),
+ },
+ },
});
From c4998bbeec05f595a8337728857a8fda177cb037 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=ED=95=98=EC=A7=80=EB=AF=BC?=
<163178666+JiiminHa@users.noreply.github.com>
Date: Wed, 3 Sep 2025 02:56:01 +0900
Subject: [PATCH 03/42] =?UTF-8?q?#14=20feat:=20=EB=AC=B8=EC=A0=9C=20?=
=?UTF-8?q?=EC=83=9D=EC=84=B1,=20=EA=B4=80=EB=A6=AC=20=ED=8E=98=EC=9D=B4?=
=?UTF-8?q?=EC=A7=80=20=EB=A0=88=EC=9D=B4=EC=95=84=EC=9B=83=20=EA=B5=AC?=
=?UTF-8?q?=ED=98=84?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../assignments/AssignmentFormLayout.tsx | 35 +++++++++++++++++++
1 file changed, 35 insertions(+)
create mode 100644 src/components/admin/assignments/AssignmentFormLayout.tsx
diff --git a/src/components/admin/assignments/AssignmentFormLayout.tsx b/src/components/admin/assignments/AssignmentFormLayout.tsx
new file mode 100644
index 0000000..d7c5697
--- /dev/null
+++ b/src/components/admin/assignments/AssignmentFormLayout.tsx
@@ -0,0 +1,35 @@
+import Button from '@/components/common/Button';
+
+type AssignmentFormLayoutProps = {
+ title: string;
+ content: React.ReactNode;
+ onCancel: () => void;
+ onConfirm: () => void;
+};
+
+const AssignmentFormLayout = ({
+ title,
+ content,
+ onCancel,
+ onConfirm,
+}: AssignmentFormLayoutProps) => {
+ return (
+
+
+ {/* 제목 */}
+
{title}
+
+ {/* 본문 */}
+
{content}
+
+ {/* 하단 버튼 */}
+
+
+
+
+
+
+ );
+};
+
+export default AssignmentFormLayout;
From 3c6efa1afe4b8cb9528c5daaa68250c9743176c1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=ED=95=98=EC=A7=80=EB=AF=BC?=
<163178666+JiiminHa@users.noreply.github.com>
Date: Wed, 3 Sep 2025 03:15:06 +0900
Subject: [PATCH 04/42] =?UTF-8?q?#14=20feat:=20chevronDown=20svg=20?=
=?UTF-8?q?=EC=B6=94=EA=B0=80?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
public/svg/chevrondown.svg | 3 +++
src/assets/svg/Chevrondown.tsx | 17 +++++++++++++++++
src/assets/svg/index.ts | 1 +
3 files changed, 21 insertions(+)
create mode 100644 public/svg/chevrondown.svg
create mode 100644 src/assets/svg/Chevrondown.tsx
diff --git a/public/svg/chevrondown.svg b/public/svg/chevrondown.svg
new file mode 100644
index 0000000..1b7f8cc
--- /dev/null
+++ b/public/svg/chevrondown.svg
@@ -0,0 +1,3 @@
+
diff --git a/src/assets/svg/Chevrondown.tsx b/src/assets/svg/Chevrondown.tsx
new file mode 100644
index 0000000..e5bd8cb
--- /dev/null
+++ b/src/assets/svg/Chevrondown.tsx
@@ -0,0 +1,17 @@
+import * as React from 'react';
+import type {SVGProps} from 'react';
+const SvgChevrondown = (props: SVGProps) => (
+
+);
+export default SvgChevrondown;
diff --git a/src/assets/svg/index.ts b/src/assets/svg/index.ts
index 910de97..4af2b34 100644
--- a/src/assets/svg/index.ts
+++ b/src/assets/svg/index.ts
@@ -3,6 +3,7 @@ export {default as ArrowdownIcon} from './ArrowdownIcon';
export {default as ArrowleftIcon} from './ArrowleftIcon';
export {default as ArrowrightIcon} from './ArrowrightIcon';
export {default as ChatIcon} from './ChatIcon';
+export {default as Chevrondown} from './Chevrondown';
export {default as DeleteIcon} from './DeleteIcon';
export {default as DragAndDropIcon} from './DragAndDropIcon';
export {default as EditIcon} from './EditIcon';
From 54371504e94db0cfd1fd9ea6b7cce112ff6ce06f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=ED=95=98=EC=A7=80=EB=AF=BC?=
<163178666+JiiminHa@users.noreply.github.com>
Date: Wed, 3 Sep 2025 03:30:28 +0900
Subject: [PATCH 05/42] =?UTF-8?q?#14=20refactor:=20=EC=BD=9C=EB=B0=B1=20?=
=?UTF-8?q?=EA=B5=AC=EC=A1=B0=20=EB=8B=A8=EC=88=9C=ED=99=94,=20=ED=83=80?=
=?UTF-8?q?=EC=9E=85=20=EC=8B=9C=EC=8A=A4=ED=85=9C=EA=B3=BC=20=EC=9D=BC?=
=?UTF-8?q?=EC=B9=98=ED=95=98=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/pages/common/UserIdInputPage.tsx | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/src/pages/common/UserIdInputPage.tsx b/src/pages/common/UserIdInputPage.tsx
index dd1c62a..1b241b7 100644
--- a/src/pages/common/UserIdInputPage.tsx
+++ b/src/pages/common/UserIdInputPage.tsx
@@ -12,7 +12,7 @@ export default function UserIdInputPage() {
const length = 7;
const [userId, setUserId] = useState(new Array(length).fill(''));
const [activeIndex, setActiveIndex] = useState(0);
- const inputRefs = useRef>([]);
+ const inputRefs = useRef<(HTMLInputElement | null)[]>([]);
useEffect(() => {
inputRefs.current[activeIndex]?.focus();
@@ -98,7 +98,9 @@ export default function UserIdInputPage() {
{userId.map((digit, i) => (
(inputRefs.current[i] = ref)}
+ ref={(el) => {
+ inputRefs.current[i] = el;
+ }}
type='text'
inputMode='numeric'
maxLength={1}
From 654b44b7596b9df78e2c9b3d427164474851cbc2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=ED=95=98=EC=A7=80=EB=AF=BC?=
<163178666+JiiminHa@users.noreply.github.com>
Date: Thu, 4 Sep 2025 08:56:24 +0900
Subject: [PATCH 06/42] =?UTF-8?q?#14=20feat:=20AssignmentCreatePage=20UI?=
=?UTF-8?q?=20=EA=B5=AC=ED=98=84?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../assignments/AssignmentCreatePage.tsx | 58 +++++++++++++++++++
1 file changed, 58 insertions(+)
create mode 100644 src/pages/admin/assignments/AssignmentCreatePage.tsx
diff --git a/src/pages/admin/assignments/AssignmentCreatePage.tsx b/src/pages/admin/assignments/AssignmentCreatePage.tsx
new file mode 100644
index 0000000..5e2fbce
--- /dev/null
+++ b/src/pages/admin/assignments/AssignmentCreatePage.tsx
@@ -0,0 +1,58 @@
+import AssignmentFormLayout from '@/components/admin/assignments/AssignmentFormLayout';
+import LabeledInput from '@/components/admin/form/LabeledInput';
+
+const AssignmentCreatePage = () => {
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {/* 추가 버튼: 내용만큼만 */}
+
+
+ }
+ onCancel={() => {}}
+ onConfirm={() => {}}
+ />
+ );
+};
+
+export default AssignmentCreatePage;
From f040affe49afefc40689d0f1fc1f04e4cd605fd6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=ED=95=98=EC=A7=80=EB=AF=BC?=
<163178666+JiiminHa@users.noreply.github.com>
Date: Thu, 4 Sep 2025 08:57:03 +0900
Subject: [PATCH 07/42] =?UTF-8?q?#14=20feat:=20LabeledInput=20UI=20?=
=?UTF-8?q?=EA=B5=AC=ED=98=84?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/components/admin/form/LabeledInput.tsx | 73 ++++++++++++++++++++++
1 file changed, 73 insertions(+)
create mode 100644 src/components/admin/form/LabeledInput.tsx
diff --git a/src/components/admin/form/LabeledInput.tsx b/src/components/admin/form/LabeledInput.tsx
new file mode 100644
index 0000000..f5febc9
--- /dev/null
+++ b/src/components/admin/form/LabeledInput.tsx
@@ -0,0 +1,73 @@
+import {useState} from 'react';
+import {Chevrondown} from '@/assets/svg';
+
+type LabeledInputProps = {
+ label: string;
+ className?: string;
+ variant?: 'input' | 'dropdown';
+ options?: {value: string; label: string}[];
+} & React.InputHTMLAttributes;
+
+const LabeledInput = ({
+ label,
+ className,
+ variant = 'input',
+ options = [
+ {value: 'public', label: '공개'},
+ {value: 'private', label: '비공개'},
+ ],
+ ...rest
+}: LabeledInputProps) => {
+ const [isOpen, setIsOpen] = useState(false);
+ const [selectedValue, setSelectedValue] = useState('');
+ return (
+
+ );
+};
+
+export default LabeledInput;
From 0d46a9384b8c35947caea925922d730d2041e6c2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=ED=95=98=EC=A7=80=EB=AF=BC?=
<163178666+JiiminHa@users.noreply.github.com>
Date: Fri, 5 Sep 2025 03:56:26 +0900
Subject: [PATCH 08/42] =?UTF-8?q?#14=20feat:=20file.svg=20=ED=8C=8C?=
=?UTF-8?q?=EC=9D=BC=20=EC=B6=94=EA=B0=80?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
public/svg/file.svg | 4 ++++
src/assets/svg/File.tsx | 22 ++++++++++++++++++++++
src/assets/svg/index.ts | 1 +
3 files changed, 27 insertions(+)
create mode 100644 public/svg/file.svg
create mode 100644 src/assets/svg/File.tsx
diff --git a/public/svg/file.svg b/public/svg/file.svg
new file mode 100644
index 0000000..9478e7f
--- /dev/null
+++ b/public/svg/file.svg
@@ -0,0 +1,4 @@
+
diff --git a/src/assets/svg/File.tsx b/src/assets/svg/File.tsx
new file mode 100644
index 0000000..ddef665
--- /dev/null
+++ b/src/assets/svg/File.tsx
@@ -0,0 +1,22 @@
+import * as React from 'react';
+import type {SVGProps} from 'react';
+const SvgFile = (props: SVGProps) => (
+
+);
+export default SvgFile;
diff --git a/src/assets/svg/index.ts b/src/assets/svg/index.ts
index 4af2b34..f686fb5 100644
--- a/src/assets/svg/index.ts
+++ b/src/assets/svg/index.ts
@@ -8,6 +8,7 @@ export {default as DeleteIcon} from './DeleteIcon';
export {default as DragAndDropIcon} from './DragAndDropIcon';
export {default as EditIcon} from './EditIcon';
export {default as EllipsisIcon} from './EllipsisIcon';
+export {default as File} from './File';
export {default as NotificationIcon} from './NotificationIcon';
export {default as SignoutIcon} from './SignoutIcon';
export {default as SingleEllipsisIcon} from './SingleEllipsisIcon';
From 1f35460145f2a90979599a54c1ebb6b34e04355f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=ED=95=98=EC=A7=80=EB=AF=BC?=
<163178666+JiiminHa@users.noreply.github.com>
Date: Fri, 5 Sep 2025 09:48:20 +0900
Subject: [PATCH 09/42] =?UTF-8?q?#14=20feat:=20=ED=8C=8C=EC=9D=BC=20?=
=?UTF-8?q?=EC=97=85=EB=A1=9C=EB=93=9C=20=EC=BB=B4=ED=8F=AC=EB=84=8C?=
=?UTF-8?q?=ED=8A=B8=20=EA=B5=AC=ED=98=84?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/components/admin/form/FileUpload.tsx | 123 +++++++++++++++++++++++
1 file changed, 123 insertions(+)
create mode 100644 src/components/admin/form/FileUpload.tsx
diff --git a/src/components/admin/form/FileUpload.tsx b/src/components/admin/form/FileUpload.tsx
new file mode 100644
index 0000000..8b817e6
--- /dev/null
+++ b/src/components/admin/form/FileUpload.tsx
@@ -0,0 +1,123 @@
+import {useRef, useState} from 'react';
+import type {ChangeEvent} from 'react';
+import {File} from '@/assets/svg';
+
+type FileUploadProps = {
+ label: string;
+ onFileChange: (file: File | null) => void;
+ description?: string;
+ accept?: string;
+ className?: string;
+};
+
+export default function FileUpload({
+ label,
+ onFileChange,
+ description = '업로드하기',
+ accept = '.csv',
+ className,
+}: FileUploadProps) {
+ const inputRef = useRef(null);
+ const [selectedFile, setSelectedFile] = useState(null);
+ const dragCounter = useRef(0);
+
+ const prevent = (e: React.DragEvent) => {
+ e.preventDefault();
+ e.stopPropagation();
+ };
+
+ const handleDragEnter = (e: React.DragEvent) => {
+ prevent(e);
+ dragCounter.current += 1;
+ };
+
+ const handleDragLeave = (e: React.DragEvent) => {
+ prevent(e);
+ dragCounter.current -= 1;
+ };
+
+ const handleDragOver = (e: React.DragEvent) => prevent(e);
+
+ const handleDrop = (e: React.DragEvent) => {
+ prevent(e);
+ dragCounter.current = 0;
+
+ const items = e.dataTransfer?.items;
+ if (items && items.length === 0) return;
+
+ const files = Array.from(e.dataTransfer.files || []);
+ if (files.length) {
+ const file = files[0];
+ setSelectedFile(file);
+ onFileChange(file);
+ }
+ };
+
+ const handleChange = (e: ChangeEvent) => {
+ const files = Array.from(e.target.files ?? []);
+ if (files.length) {
+ const file = files[0];
+ setSelectedFile(file);
+ onFileChange(file);
+ }
+ // 값 초기화(같은 파일 재선택 허용)
+ e.target.value = '';
+ };
+
+ const handleRemoveFile = () => {
+ setSelectedFile(null);
+ onFileChange(null);
+ };
+
+ return (
+
+
+ {label}
+
+
+ {selectedFile ? (
+
+ {selectedFile.name}
+
+
+ ) : (
+
inputRef.current?.click()}
+ onKeyDown={(e) =>
+ (e.key === 'Enter' || e.key === ' ') && inputRef.current?.click()
+ }
+ onDragEnter={handleDragEnter}
+ onDragLeave={handleDragLeave}
+ onDragOver={handleDragOver}
+ onDrop={handleDrop}
+ className={[
+ 'flex h-[166px] w-full cursor-pointer items-center justify-center rounded-[9px] border-1 border-dashed border-primary bg-background',
+ 'focus:outline-none focus:ring-2 focus:ring-primary',
+ ].join(' ')}>
+
+
+ )}
+
+
+
+ );
+}
From e1557901b5d576a748520dfd04bda334415734d0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=ED=95=98=EC=A7=80=EB=AF=BC?=
<163178666+JiiminHa@users.noreply.github.com>
Date: Fri, 5 Sep 2025 10:14:15 +0900
Subject: [PATCH 10/42] =?UTF-8?q?#14=20feat:=20=EC=98=88=EC=A0=9C=20?=
=?UTF-8?q?=EC=9E=85=EB=A0=A5=EB=9E=80=20=EB=8F=99=EC=A0=81=20=EC=B6=94?=
=?UTF-8?q?=EA=B0=80=20=EB=B0=8F=20=EB=9D=BC=EB=B2=A8=20=EA=B0=84=EA=B2=A9?=
=?UTF-8?q?=20=EA=B0=9C=EC=84=A0=20=EB=B0=8F=20=ED=8C=8C=EC=9D=BC=EC=97=85?=
=?UTF-8?q?=EB=A1=9C=EB=93=9C=20=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20?=
=?UTF-8?q?=EC=B6=94=EA=B0=80?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../assignments/AssignmentCreatePage.tsx | 56 +++++++++++++------
1 file changed, 38 insertions(+), 18 deletions(-)
diff --git a/src/pages/admin/assignments/AssignmentCreatePage.tsx b/src/pages/admin/assignments/AssignmentCreatePage.tsx
index 5e2fbce..e693ebe 100644
--- a/src/pages/admin/assignments/AssignmentCreatePage.tsx
+++ b/src/pages/admin/assignments/AssignmentCreatePage.tsx
@@ -1,7 +1,15 @@
import AssignmentFormLayout from '@/components/admin/assignments/AssignmentFormLayout';
import LabeledInput from '@/components/admin/form/LabeledInput';
+import FileUpload from '@/components/admin/form/FileUpload';
+import {useState} from 'react';
const AssignmentCreatePage = () => {
+ const [examples, setExamples] = useState([{input: '', output: '', 공개: ''}]);
+
+ const handleAddExample = () => {
+ setExamples([...examples, {input: '', output: '', 공개: ''}]);
+ };
+
return (
{
placeholder='문제 설명을 입력하세요'
className='w-full'
/>
-
-
-
-
+
+ {examples.map((ex, idx) => (
+
+
+
+
+
+ ))}
- {/* 추가 버튼: 내용만큼만 */}
-
}
onCancel={() => {}}
From 418a352ced963795b2d54d1a8e0a9b12a49ee59f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=ED=95=98=EC=A7=80=EB=AF=BC?=
<163178666+JiiminHa@users.noreply.github.com>
Date: Thu, 11 Sep 2025 09:42:57 +0900
Subject: [PATCH 11/42] =?UTF-8?q?#14=20feat:=20=EA=B0=95=EC=9D=98=20?=
=?UTF-8?q?=EC=83=9D=EC=84=B1=20=ED=8E=98=EC=9D=B4=EC=A7=80=20=EA=B5=AC?=
=?UTF-8?q?=ED=98=84?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/pages/admin/courses/CourseCreatePage.tsx | 56 ++++++++++++++++++++
1 file changed, 56 insertions(+)
create mode 100644 src/pages/admin/courses/CourseCreatePage.tsx
diff --git a/src/pages/admin/courses/CourseCreatePage.tsx b/src/pages/admin/courses/CourseCreatePage.tsx
new file mode 100644
index 0000000..0e23e26
--- /dev/null
+++ b/src/pages/admin/courses/CourseCreatePage.tsx
@@ -0,0 +1,56 @@
+import AssignmentFormLayout from '@/components/admin/assignments/AssignmentFormLayout';
+import LabeledInput from '@/components/admin/form/LabeledInput';
+import FileUpload from '@/components/admin/form/FileUpload';
+import LabeledDropdown from '@/components/admin/form/LabeledDropdown';
+
+const CourseCreatePage = () => {
+ return (
+
+
+
+
+
+
+
+
+
+
+ {}}
+ className='mb-9'
+ />
+
+ }
+ onCancel={() => {}}
+ onConfirm={() => {}}
+ />
+ );
+};
+
+export default CourseCreatePage;
From 36811bfcadff8d1d36489a745d81609d816aa8eb Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=ED=95=98=EC=A7=80=EB=AF=BC?=
<163178666+JiiminHa@users.noreply.github.com>
Date: Thu, 11 Sep 2025 09:43:14 +0900
Subject: [PATCH 12/42] =?UTF-8?q?#14=20feat:=20=EA=B3=BC=EC=A0=9C=20?=
=?UTF-8?q?=EC=83=9D=EC=84=B1=20=ED=8E=98=EC=9D=B4=EC=A7=80=20=EA=B5=AC?=
=?UTF-8?q?=ED=98=84?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/pages/admin/assignments/AssignmentCreatePage.tsx | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/src/pages/admin/assignments/AssignmentCreatePage.tsx b/src/pages/admin/assignments/AssignmentCreatePage.tsx
index e693ebe..0d4180d 100644
--- a/src/pages/admin/assignments/AssignmentCreatePage.tsx
+++ b/src/pages/admin/assignments/AssignmentCreatePage.tsx
@@ -2,6 +2,7 @@ import AssignmentFormLayout from '@/components/admin/assignments/AssignmentFormL
import LabeledInput from '@/components/admin/form/LabeledInput';
import FileUpload from '@/components/admin/form/FileUpload';
import {useState} from 'react';
+import LabeledDropdown from '@/components/admin/form/LabeledDropdown';
const AssignmentCreatePage = () => {
const [examples, setExamples] = useState([{input: '', output: '', 공개: ''}]);
@@ -48,9 +49,9 @@ const AssignmentCreatePage = () => {
placeholder='입력하세요'
className='w-full'
/>
-
From 62bd4d8681b945e507beeb598c59f6879d242ee1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=ED=95=98=EC=A7=80=EB=AF=BC?=
<163178666+JiiminHa@users.noreply.github.com>
Date: Thu, 11 Sep 2025 09:44:27 +0900
Subject: [PATCH 13/42] =?UTF-8?q?#14=20feat:=20useClickOutside=20=EC=BB=A4?=
=?UTF-8?q?=EC=8A=A4=ED=85=80=ED=9B=85=20=EA=B5=AC=ED=98=84?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/hooks/useClickOutside.ts | 32 ++++++++++++++++++++++++++++++++
1 file changed, 32 insertions(+)
create mode 100644 src/hooks/useClickOutside.ts
diff --git a/src/hooks/useClickOutside.ts b/src/hooks/useClickOutside.ts
new file mode 100644
index 0000000..454fe61
--- /dev/null
+++ b/src/hooks/useClickOutside.ts
@@ -0,0 +1,32 @@
+import {useEffect} from 'react';
+
+type UseClickOutsideProps = {
+ ref: React.RefObject;
+ onClickOutside: () => void;
+ exclude?: (target: HTMLElement) => boolean;
+};
+
+const useClickOutside = ({
+ ref,
+ onClickOutside,
+ exclude,
+}: UseClickOutsideProps) => {
+ useEffect(() => {
+ const handleClickOutside = (e: MouseEvent) => {
+ const target = e.target as HTMLElement;
+
+ if (exclude?.(target)) return;
+
+ if (ref.current?.contains(target) === false) {
+ onClickOutside();
+ }
+ };
+
+ document.addEventListener('mousedown', handleClickOutside);
+ return () => {
+ document.removeEventListener('mousedown', handleClickOutside);
+ };
+ }, [ref, onClickOutside, exclude]);
+};
+
+export default useClickOutside;
From 2f50a580694bd2c84a5d5df5b3db4ca6ad838a60 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=ED=95=98=EC=A7=80=EB=AF=BC?=
<163178666+JiiminHa@users.noreply.github.com>
Date: Thu, 11 Sep 2025 09:51:17 +0900
Subject: [PATCH 14/42] =?UTF-8?q?#14=20feat:=20LabeledInput=20=EB=A6=AC?=
=?UTF-8?q?=ED=8C=A9=ED=86=A0=EB=A7=81=20=EB=B0=8F=20LabeledDropdown=20var?=
=?UTF-8?q?iant=20=EC=B6=94=EA=B0=80?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/components/admin/form/LabeledDropdown.tsx | 88 +++++++++++++++++++
src/components/admin/form/LabeledInput.tsx | 70 ++++-----------
2 files changed, 103 insertions(+), 55 deletions(-)
create mode 100644 src/components/admin/form/LabeledDropdown.tsx
diff --git a/src/components/admin/form/LabeledDropdown.tsx b/src/components/admin/form/LabeledDropdown.tsx
new file mode 100644
index 0000000..680d2df
--- /dev/null
+++ b/src/components/admin/form/LabeledDropdown.tsx
@@ -0,0 +1,88 @@
+import {useState, useRef} from 'react';
+import {Chevrondown} from '@/assets/svg';
+import useClickOutside from '@/hooks/useClickOutside';
+
+interface LabeledDropdownProps
+ extends Omit, 'onSelect'> {
+ label: string;
+ className?: string;
+ variant: 'visibility' | 'year' | 'semester';
+ placeholder?: string;
+ onSelect?: (value: string) => void;
+}
+
+const getDefaultOptions = (variant: 'visibility' | 'year' | 'semester') => {
+ switch (variant) {
+ case 'visibility':
+ return ['공개', '비공개'];
+ case 'year':
+ return ['2021', '2022', '2023', '2024', '2025'];
+ case 'semester':
+ return ['1학기', '2학기', '여름학기', '겨울학기'];
+ default:
+ return [];
+ }
+};
+
+const LabeledDropdown = ({
+ label,
+ className,
+ variant,
+ placeholder,
+ onSelect,
+ ...rest
+}: LabeledDropdownProps) => {
+ const [selectedValue, setSelectedValue] = useState('');
+ const [isOpen, setIsOpen] = useState(false);
+ const dropdownRef = useRef(null);
+
+ const optionsToUse = getDefaultOptions(variant);
+
+ useClickOutside({
+ ref: dropdownRef,
+ onClickOutside: () => setIsOpen(false),
+ });
+
+ const handleSelect = (option: string, event: React.MouseEvent) => {
+ event.stopPropagation();
+ setSelectedValue(option);
+ setIsOpen(false);
+ onSelect?.(option);
+ };
+
+ return (
+
+ );
+};
+
+export default LabeledDropdown;
diff --git a/src/components/admin/form/LabeledInput.tsx b/src/components/admin/form/LabeledInput.tsx
index f5febc9..8951b74 100644
--- a/src/components/admin/form/LabeledInput.tsx
+++ b/src/components/admin/form/LabeledInput.tsx
@@ -1,71 +1,31 @@
-import {useState} from 'react';
-import {Chevrondown} from '@/assets/svg';
-
-type LabeledInputProps = {
+interface LabeledInputProps
+ extends React.InputHTMLAttributes {
label: string;
className?: string;
- variant?: 'input' | 'dropdown';
- options?: {value: string; label: string}[];
-} & React.InputHTMLAttributes;
+ showLabel?: boolean;
+}
const LabeledInput = ({
label,
className,
- variant = 'input',
- options = [
- {value: 'public', label: '공개'},
- {value: 'private', label: '비공개'},
- ],
+ showLabel = true,
...rest
}: LabeledInputProps) => {
- const [isOpen, setIsOpen] = useState(false);
- const [selectedValue, setSelectedValue] = useState('');
return (
);
};
From 2789f8401cbf85e9a8ebae6091f859f2c01b122b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=ED=95=98=EC=A7=80=EB=AF=BC?=
<163178666+JiiminHa@users.noreply.github.com>
Date: Thu, 11 Sep 2025 09:51:59 +0900
Subject: [PATCH 15/42] =?UTF-8?q?#14=20style:=20placeholder=20=EC=A0=84?=
=?UTF-8?q?=EC=97=AD=20=EC=83=89=EC=83=81=20=EC=84=A4=EC=A0=95?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/index.css | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/src/index.css b/src/index.css
index f2a112e..9dd07ce 100644
--- a/src/index.css
+++ b/src/index.css
@@ -74,3 +74,7 @@
.text-btn {
@apply text-base font-medium;
}
+
+input::placeholder {
+ color: var(--color-light-black);
+}
From d85f49ec6d2de077d592e91835dbb6a3e4df5316 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=ED=95=98=EC=A7=80=EB=AF=BC?=
<163178666+JiiminHa@users.noreply.github.com>
Date: Thu, 11 Sep 2025 09:52:53 +0900
Subject: [PATCH 16/42] =?UTF-8?q?#14=20feat:=20=EB=9D=BC=EC=9A=B0=ED=8A=B8?=
=?UTF-8?q?=20=EA=B2=BD=EB=A1=9C=20=EC=B6=94=EA=B0=80?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/App.tsx | 15 +++++++++++----
1 file changed, 11 insertions(+), 4 deletions(-)
diff --git a/src/App.tsx b/src/App.tsx
index 09ef00f..c639b44 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -3,8 +3,10 @@ import Layout from './layout/Layout';
import LandingPage from './pages/common/LandingPage';
import UserIdInputPage from './pages/common/UserIdInputPage';
import Dashboard from './pages/common/Dashboard';
-import AssignmentsPage from './pages/admin/AssignmentsPage';
-import AssignmentSelectPage from './pages/admin/assignments/AssignmentSelectPage';
+// import AssignmentsPage from './pages/admin/assignments/AssignmentsPage';
+// import AssignmentSelectPage from './pages/admin/assignments/AssignmentSelectPage';
+import AssignmentCreatePage from './pages/admin/assignments/AssignmentCreatePage';
+import CourseCreatePage from './pages/admin/courses/CourseCreatePage';
function App() {
return (
@@ -26,8 +28,13 @@ function App() {
}>
{/* 추가 페이지들 */}
} />
- } />
- } />
+ {/* } />
+ } /> */}
+ }
+ />
+ } />
From 8a57dfa0a6088f316892e56e41767af24668663f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=ED=95=98=EC=A7=80=EB=AF=BC?=
<163178666+JiiminHa@users.noreply.github.com>
Date: Thu, 18 Sep 2025 08:57:43 +0900
Subject: [PATCH 17/42] =?UTF-8?q?#13=20chore:=20=EB=94=94=EC=9E=90?=
=?UTF-8?q?=EC=9D=B8=20=EC=BB=AC=EB=9F=AC=20=ED=8C=94=EB=A0=88=ED=8A=B8=20?=
=?UTF-8?q?=EC=83=89=EC=83=81=20=EC=97=85=EB=8D=B0=EC=9D=B4=ED=8A=B8?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/index.css | 3 +++
1 file changed, 3 insertions(+)
diff --git a/src/index.css b/src/index.css
index 9dd07ce..26c7fe3 100644
--- a/src/index.css
+++ b/src/index.css
@@ -48,6 +48,9 @@
--color-primary-black: #2c2a36;
--color-purple-stroke: #dfdbf0;
--color-hover: #b4a5ff;
+ --color-status-green: #c4ffa4;
+ --color-status-yellow: #ffe292;
+ --color-status-red: #ffb3b3;
--font-coolvetica: 'Coolvetica', sans-serif;
--shadow-card: 0px 0px 14px 0px rgba(223, 219, 240, 0.4);
From 07d7fddb845fdb74ed5ef5ba9153b158964941fd Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=ED=95=98=EC=A7=80=EB=AF=BC?=
<163178666+JiiminHa@users.noreply.github.com>
Date: Thu, 18 Sep 2025 09:15:44 +0900
Subject: [PATCH 18/42] =?UTF-8?q?#13=20chore:=20=ED=94=84=EB=A1=9C?=
=?UTF-8?q?=ED=95=84=20=EC=9D=B4=EB=AF=B8=EC=A7=80=20=EC=B6=94=EA=B0=80?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
public/svg/profileImage.svg | 80 ++++++++++++++
src/assets/svg/ProfileImage.tsx | 183 ++++++++++++++++++++++++++++++++
src/assets/svg/index.ts | 1 +
3 files changed, 264 insertions(+)
create mode 100644 public/svg/profileImage.svg
create mode 100644 src/assets/svg/ProfileImage.tsx
diff --git a/public/svg/profileImage.svg b/public/svg/profileImage.svg
new file mode 100644
index 0000000..213c774
--- /dev/null
+++ b/public/svg/profileImage.svg
@@ -0,0 +1,80 @@
+
diff --git a/src/assets/svg/ProfileImage.tsx b/src/assets/svg/ProfileImage.tsx
new file mode 100644
index 0000000..6fb793f
--- /dev/null
+++ b/src/assets/svg/ProfileImage.tsx
@@ -0,0 +1,183 @@
+import * as React from 'react';
+import type {SVGProps} from 'react';
+const SvgProfileImage = (props: SVGProps) => (
+
+);
+export default SvgProfileImage;
diff --git a/src/assets/svg/index.ts b/src/assets/svg/index.ts
index f686fb5..7a2edb8 100644
--- a/src/assets/svg/index.ts
+++ b/src/assets/svg/index.ts
@@ -10,6 +10,7 @@ export {default as EditIcon} from './EditIcon';
export {default as EllipsisIcon} from './EllipsisIcon';
export {default as File} from './File';
export {default as NotificationIcon} from './NotificationIcon';
+export {default as ProfileImage} from './ProfileImage';
export {default as SignoutIcon} from './SignoutIcon';
export {default as SingleEllipsisIcon} from './SingleEllipsisIcon';
export {default as UserIcon} from './UserIcon';
From 70d273df6ce6bba90a7533c14bcd1045fd0deb1f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=ED=95=98=EC=A7=80=EB=AF=BC?=
<163178666+JiiminHa@users.noreply.github.com>
Date: Mon, 22 Sep 2025 12:57:57 +0900
Subject: [PATCH 19/42] =?UTF-8?q?#13=20feat:=20Search=20=EC=95=84=EC=9D=B4?=
=?UTF-8?q?=EC=BD=98=20=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EC=B6=94?=
=?UTF-8?q?=EA=B0=80?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
public/svg/search.svg | 3 +++
src/assets/svg/Search.tsx | 18 ++++++++++++++++++
src/assets/svg/index.ts | 1 +
3 files changed, 22 insertions(+)
create mode 100644 public/svg/search.svg
create mode 100644 src/assets/svg/Search.tsx
diff --git a/public/svg/search.svg b/public/svg/search.svg
new file mode 100644
index 0000000..f0bf38e
--- /dev/null
+++ b/public/svg/search.svg
@@ -0,0 +1,3 @@
+
diff --git a/src/assets/svg/Search.tsx b/src/assets/svg/Search.tsx
new file mode 100644
index 0000000..216d65f
--- /dev/null
+++ b/src/assets/svg/Search.tsx
@@ -0,0 +1,18 @@
+import * as React from 'react';
+import type {SVGProps} from 'react';
+const SvgSearch = (props: SVGProps) => (
+
+);
+export default SvgSearch;
diff --git a/src/assets/svg/index.ts b/src/assets/svg/index.ts
index 7a2edb8..fa8243b 100644
--- a/src/assets/svg/index.ts
+++ b/src/assets/svg/index.ts
@@ -11,6 +11,7 @@ export {default as EllipsisIcon} from './EllipsisIcon';
export {default as File} from './File';
export {default as NotificationIcon} from './NotificationIcon';
export {default as ProfileImage} from './ProfileImage';
+export {default as Search} from './Search';
export {default as SignoutIcon} from './SignoutIcon';
export {default as SingleEllipsisIcon} from './SingleEllipsisIcon';
export {default as UserIcon} from './UserIcon';
From 45b0b972ae30b4cc59d050c7dac85947899efb2d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=ED=95=98=EC=A7=80=EB=AF=BC?=
<163178666+JiiminHa@users.noreply.github.com>
Date: Mon, 22 Sep 2025 13:14:01 +0900
Subject: [PATCH 20/42] =?UTF-8?q?#13=20feat:=20StudentManagementPage=20?=
=?UTF-8?q?=EC=B4=88=EA=B8=B0=20=EB=A0=88=EC=9D=B4=EC=95=84=EC=9B=83=20?=
=?UTF-8?q?=EB=B0=8F=20UI=20=EC=9D=BC=EB=B6=80=20=EC=B6=94=EA=B0=80?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/components/admin/assignments/AssignmentFormLayout.tsx | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/src/components/admin/assignments/AssignmentFormLayout.tsx b/src/components/admin/assignments/AssignmentFormLayout.tsx
index d7c5697..3ddd194 100644
--- a/src/components/admin/assignments/AssignmentFormLayout.tsx
+++ b/src/components/admin/assignments/AssignmentFormLayout.tsx
@@ -2,6 +2,7 @@ import Button from '@/components/common/Button';
type AssignmentFormLayoutProps = {
title: string;
+ titleExtra?: React.ReactNode;
content: React.ReactNode;
onCancel: () => void;
onConfirm: () => void;
@@ -9,6 +10,7 @@ type AssignmentFormLayoutProps = {
const AssignmentFormLayout = ({
title,
+ titleExtra,
content,
onCancel,
onConfirm,
@@ -17,7 +19,10 @@ const AssignmentFormLayout = ({
{/* 제목 */}
-
{title}
+
+
{title}
+ {titleExtra}
+
{/* 본문 */}
{content}
From 6e703f21ca75e728b6b8f2f506658b0150b987c9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=ED=95=98=EC=A7=80=EB=AF=BC?=
<163178666+JiiminHa@users.noreply.github.com>
Date: Mon, 22 Sep 2025 13:16:12 +0900
Subject: [PATCH 21/42] =?UTF-8?q?#13=20feat:=20StudentManagementPage=20?=
=?UTF-8?q?=EC=B4=88=EA=B8=B0=20=EB=A0=88=EC=9D=B4=EC=95=84=EC=9B=83=20?=
=?UTF-8?q?=EB=B0=8F=20=EA=B2=80=EC=83=89=20UI=20=EC=B6=94=EA=B0=80?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../admin/student/studentManagementPage.tsx | 30 +++++++++++++++++++
1 file changed, 30 insertions(+)
create mode 100644 src/pages/admin/student/studentManagementPage.tsx
diff --git a/src/pages/admin/student/studentManagementPage.tsx b/src/pages/admin/student/studentManagementPage.tsx
new file mode 100644
index 0000000..520304d
--- /dev/null
+++ b/src/pages/admin/student/studentManagementPage.tsx
@@ -0,0 +1,30 @@
+import AssignmentFormLayout from '@/components/admin/assignments/AssignmentFormLayout';
+import {Search} from '@/assets/svg';
+
+export default function StudentManagementPage() {
+ const titleExtra = (
+
+
+ 소프트웨어 이해 (005)
+
+
+
+
+
+
+ );
+
+ return (
+
학생 관리 기능이 여기에 들어갑니다.}
+ onCancel={() => {}}
+ onConfirm={() => {}}
+ />
+ );
+}
From ec6d43210ec3f454fa5c64d59b12ae3a56546009 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=ED=95=98=EC=A7=80=EB=AF=BC?=
<163178666+JiiminHa@users.noreply.github.com>
Date: Wed, 24 Sep 2025 14:55:06 +0900
Subject: [PATCH 22/42] =?UTF-8?q?#13=20chore:=20prettier-plugin-tailwindcs?=
=?UTF-8?q?s=20=EC=84=A4=EC=A0=95?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
package-lock.json | 105 ++++++++++++++++++++++++++++++++++++++++++++++
package.json | 2 +
2 files changed, 107 insertions(+)
diff --git a/package-lock.json b/package-lock.json
index 0c36197..109a10b 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -24,6 +24,8 @@
"eslint-plugin-react-hooks": "^5.2.0",
"eslint-plugin-react-refresh": "^0.4.20",
"globals": "^16.2.0",
+ "prettier": "^3.6.2",
+ "prettier-plugin-tailwindcss": "^0.6.14",
"tsconfig-paths": "^4.2.0",
"typescript": "~5.8.3",
"typescript-eslint": "^8.34.1",
@@ -3824,6 +3826,109 @@
"node": ">= 0.8.0"
}
},
+ "node_modules/prettier": {
+ "version": "3.6.2",
+ "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz",
+ "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "prettier": "bin/prettier.cjs"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/prettier/prettier?sponsor=1"
+ }
+ },
+ "node_modules/prettier-plugin-tailwindcss": {
+ "version": "0.6.14",
+ "resolved": "https://registry.npmjs.org/prettier-plugin-tailwindcss/-/prettier-plugin-tailwindcss-0.6.14.tgz",
+ "integrity": "sha512-pi2e/+ZygeIqntN+vC573BcW5Cve8zUB0SSAGxqpB4f96boZF4M3phPVoOFCeypwkpRYdi7+jQ5YJJUwrkGUAg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=14.21.3"
+ },
+ "peerDependencies": {
+ "@ianvs/prettier-plugin-sort-imports": "*",
+ "@prettier/plugin-hermes": "*",
+ "@prettier/plugin-oxc": "*",
+ "@prettier/plugin-pug": "*",
+ "@shopify/prettier-plugin-liquid": "*",
+ "@trivago/prettier-plugin-sort-imports": "*",
+ "@zackad/prettier-plugin-twig": "*",
+ "prettier": "^3.0",
+ "prettier-plugin-astro": "*",
+ "prettier-plugin-css-order": "*",
+ "prettier-plugin-import-sort": "*",
+ "prettier-plugin-jsdoc": "*",
+ "prettier-plugin-marko": "*",
+ "prettier-plugin-multiline-arrays": "*",
+ "prettier-plugin-organize-attributes": "*",
+ "prettier-plugin-organize-imports": "*",
+ "prettier-plugin-sort-imports": "*",
+ "prettier-plugin-style-order": "*",
+ "prettier-plugin-svelte": "*"
+ },
+ "peerDependenciesMeta": {
+ "@ianvs/prettier-plugin-sort-imports": {
+ "optional": true
+ },
+ "@prettier/plugin-hermes": {
+ "optional": true
+ },
+ "@prettier/plugin-oxc": {
+ "optional": true
+ },
+ "@prettier/plugin-pug": {
+ "optional": true
+ },
+ "@shopify/prettier-plugin-liquid": {
+ "optional": true
+ },
+ "@trivago/prettier-plugin-sort-imports": {
+ "optional": true
+ },
+ "@zackad/prettier-plugin-twig": {
+ "optional": true
+ },
+ "prettier-plugin-astro": {
+ "optional": true
+ },
+ "prettier-plugin-css-order": {
+ "optional": true
+ },
+ "prettier-plugin-import-sort": {
+ "optional": true
+ },
+ "prettier-plugin-jsdoc": {
+ "optional": true
+ },
+ "prettier-plugin-marko": {
+ "optional": true
+ },
+ "prettier-plugin-multiline-arrays": {
+ "optional": true
+ },
+ "prettier-plugin-organize-attributes": {
+ "optional": true
+ },
+ "prettier-plugin-organize-imports": {
+ "optional": true
+ },
+ "prettier-plugin-sort-imports": {
+ "optional": true
+ },
+ "prettier-plugin-style-order": {
+ "optional": true
+ },
+ "prettier-plugin-svelte": {
+ "optional": true
+ }
+ }
+ },
"node_modules/punycode": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
diff --git a/package.json b/package.json
index dfa3505..a52ebdf 100644
--- a/package.json
+++ b/package.json
@@ -27,6 +27,8 @@
"eslint-plugin-react-hooks": "^5.2.0",
"eslint-plugin-react-refresh": "^0.4.20",
"globals": "^16.2.0",
+ "prettier": "^3.6.2",
+ "prettier-plugin-tailwindcss": "^0.6.14",
"tsconfig-paths": "^4.2.0",
"typescript": "~5.8.3",
"typescript-eslint": "^8.34.1",
From 0927964f90ae608b2d6072f8c1069309873dca49 Mon Sep 17 00:00:00 2001
From: suminb99
Date: Tue, 30 Dec 2025 13:00:11 +0900
Subject: [PATCH 23/42] =?UTF-8?q?#21=20chore:=20tailwind-variants=20?=
=?UTF-8?q?=ED=8C=A8=ED=82=A4=EC=A7=80=20=EC=84=A4=EC=B9=98?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
package-lock.json | 20 ++++++++++++++++++++
package.json | 1 +
2 files changed, 21 insertions(+)
diff --git a/package-lock.json b/package-lock.json
index 0c36197..1d6b733 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -12,6 +12,7 @@
"react": "^19.1.0",
"react-dom": "^19.1.0",
"react-router-dom": "^7.6.3",
+ "tailwind-variants": "^3.2.2",
"tailwindcss": "^4.1.11"
},
"devDependencies": {
@@ -4116,6 +4117,25 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/tailwind-variants": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/tailwind-variants/-/tailwind-variants-3.2.2.tgz",
+ "integrity": "sha512-Mi4kHeMTLvKlM98XPnK+7HoBPmf4gygdFmqQPaDivc3DpYS6aIY6KiG/PgThrGvii5YZJqRsPz0aPyhoFzmZgg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=16.x",
+ "pnpm": ">=7.x"
+ },
+ "peerDependencies": {
+ "tailwind-merge": ">=3.0.0",
+ "tailwindcss": "*"
+ },
+ "peerDependenciesMeta": {
+ "tailwind-merge": {
+ "optional": true
+ }
+ }
+ },
"node_modules/tailwindcss": {
"version": "4.1.11",
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.11.tgz",
diff --git a/package.json b/package.json
index dfa3505..ccbe2de 100644
--- a/package.json
+++ b/package.json
@@ -15,6 +15,7 @@
"react": "^19.1.0",
"react-dom": "^19.1.0",
"react-router-dom": "^7.6.3",
+ "tailwind-variants": "^3.2.2",
"tailwindcss": "^4.1.11"
},
"devDependencies": {
From 77c081efcb703a65c7b293f762e7dc951e0f4378 Mon Sep 17 00:00:00 2001
From: suminb99
Date: Tue, 30 Dec 2025 14:06:09 +0900
Subject: [PATCH 24/42] =?UTF-8?q?#21=20refactor:=20Button=20=EC=BB=B4?=
=?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=20=EC=8A=A4=ED=83=80=EC=9D=BC=20?=
=?UTF-8?q?=EB=A1=9C=EC=A7=81=EC=9D=84=20tailwind-variants=EB=A1=9C=20?=
=?UTF-8?q?=EA=B0=9C=EC=84=A0?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/components/common/Button.tsx | 49 +++++++++++++++++++-------------
1 file changed, 29 insertions(+), 20 deletions(-)
diff --git a/src/components/common/Button.tsx b/src/components/common/Button.tsx
index 4be7e34..5225644 100644
--- a/src/components/common/Button.tsx
+++ b/src/components/common/Button.tsx
@@ -1,27 +1,36 @@
-interface ButtonProps {
- theme: string;
- text: string;
- icon?: React.ReactElement;
- onClick?: () => void;
-}
+import {tv, type VariantProps} from 'tailwind-variants/lite';
-interface ButtonTheme {
- [key: string]: string;
-}
+const button = tv({
+ base: 'flex-center px-3 py-1.5 text-center text-base font-medium whitespace-nowrap rounded-[10px] border',
+ variants: {
+ color: {
+ primary: 'bg-primary text-white border-primary',
+ secondary: 'bg-white text-primary-black border-purple-stroke',
+ outlinePurple: 'bg-white text-primary border-primary',
+ outlineWhite: 'bg-transparent text-white border-white',
+ tonal: 'bg-purple-stroke text-secondary-black border-purple-stroke',
+ },
+ isIcon: {
+ true: 'rounded-full',
+ },
+ },
+ defaultVariants: {
+ color: 'primary',
+ isIcon: false,
+ },
+});
-const buttonTheme: ButtonTheme = {
- primaryPurple: 'primary-btn bg-primary text-white',
- primaryWhite: 'primary-btn bg-white text-primary border',
- primaryTransparent: 'primary-btn text-white border border-white',
- secondaryPurpleStroke: 'secondary-btn bg-purple-stroke text-secondary-black',
-};
+type ButtonVariants = VariantProps;
+
+interface ButtonProps extends ButtonVariants {
+ children: React.ReactNode;
+ onClick?: () => void;
+}
-const Button = ({theme, text, icon}: ButtonProps) => {
+const Button = ({children, onClick, ...props}: ButtonProps) => {
return (
-
- {icon}
- {text}
+
+ {children}
);
};
From 6aa8fb19eb2906f44643f11e34f136301401b4e5 Mon Sep 17 00:00:00 2001
From: suminb99
Date: Tue, 30 Dec 2025 20:37:37 +0900
Subject: [PATCH 25/42] =?UTF-8?q?(#21)=20feat:=20Button=20=EC=BB=B4?=
=?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=20props=20=EC=B6=94=EA=B0=80=20?=
=?UTF-8?q?=EB=B0=8F=20variants=20=ED=99=95=EC=9E=A5?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/components/common/Button.tsx | 30 ++++++++++++++++++++++++++----
1 file changed, 26 insertions(+), 4 deletions(-)
diff --git a/src/components/common/Button.tsx b/src/components/common/Button.tsx
index 5225644..92f64f0 100644
--- a/src/components/common/Button.tsx
+++ b/src/components/common/Button.tsx
@@ -1,14 +1,23 @@
import {tv, type VariantProps} from 'tailwind-variants/lite';
const button = tv({
- base: 'flex-center px-3 py-1.5 text-center text-base font-medium whitespace-nowrap rounded-[10px] border',
+ base: 'cursor-pointer flex-center text-center gap-2 text-base font-medium whitespace-nowrap rounded-[10px] border',
variants: {
color: {
primary: 'bg-primary text-white border-primary',
- secondary: 'bg-white text-primary-black border-purple-stroke',
+ secondary:
+ 'bg-white text-primary-black border-purple-stroke hover:bg-hover hover:text-white',
outlinePurple: 'bg-white text-primary border-primary',
outlineWhite: 'bg-transparent text-white border-white',
tonal: 'bg-purple-stroke text-secondary-black border-purple-stroke',
+ ghost: 'bg-transparent text-black border-none',
+ ghostWhite: 'bg-white text-secondary-black border-none',
+ },
+ size: {
+ default: 'w-24 h-10 px-3 py-1.5',
+ compact: 'w-fit leading-5 px-3 py-1.5',
+ wide: 'w-40 py-[15px]',
+ none: 'w-fit p-0',
},
isIcon: {
true: 'rounded-full',
@@ -16,6 +25,8 @@ const button = tv({
},
defaultVariants: {
color: 'primary',
+ size: 'default',
+ disabled: false,
isIcon: false,
},
});
@@ -24,12 +35,23 @@ type ButtonVariants = VariantProps;
interface ButtonProps extends ButtonVariants {
children: React.ReactNode;
+ className?: string;
+ type?: 'button' | 'submit';
+ disabled?: boolean;
onClick?: () => void;
+ onMouseEnter?: () => void;
+ onMouseLeave?: () => void;
}
-const Button = ({children, onClick, ...props}: ButtonProps) => {
+const Button = ({
+ children,
+ onClick,
+ type = 'button',
+ disabled = false,
+ ...props
+}: ButtonProps) => {
return (
-
+
{children}
);
From e81750a4aed2ccdb28ee73c16c13ad485fb41d2d Mon Sep 17 00:00:00 2001
From: suminb99
Date: Tue, 30 Dec 2025 20:40:38 +0900
Subject: [PATCH 26/42] =?UTF-8?q?#21=20feat:=20=EB=A6=AC=ED=8E=99=ED=86=A0?=
=?UTF-8?q?=EB=A7=81=ED=95=9C=20=EB=B2=84=ED=8A=BC=20=EC=BB=B4=ED=8F=AC?=
=?UTF-8?q?=EB=84=8C=ED=8A=B8=20=EC=A0=84=EC=97=AD=20=EC=A0=81=EC=9A=A9?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../assignments/AssignmentFormLayout.tsx | 8 +++--
.../assignments/AssignmentPageLayout.tsx | 13 ++++----
.../CourseOverview/CourseActionsBar.tsx | 10 ++----
.../common/Dashboard/CourseList.tsx | 10 ++++--
.../assignments/AssignmentCreatePage.tsx | 11 ++++---
src/pages/common/LandingPage.tsx | 32 +++++++++++--------
src/pages/common/UserIdInputPage.tsx | 22 +++++++------
7 files changed, 57 insertions(+), 49 deletions(-)
diff --git a/src/components/admin/assignments/AssignmentFormLayout.tsx b/src/components/admin/assignments/AssignmentFormLayout.tsx
index d7c5697..86e18db 100644
--- a/src/components/admin/assignments/AssignmentFormLayout.tsx
+++ b/src/components/admin/assignments/AssignmentFormLayout.tsx
@@ -24,8 +24,12 @@ const AssignmentFormLayout = ({
{/* 하단 버튼 */}
-
-
+
+ 취소
+
+
+ 저장
+
diff --git a/src/components/admin/assignments/AssignmentPageLayout.tsx b/src/components/admin/assignments/AssignmentPageLayout.tsx
index e72355a..325c153 100644
--- a/src/components/admin/assignments/AssignmentPageLayout.tsx
+++ b/src/components/admin/assignments/AssignmentPageLayout.tsx
@@ -32,15 +32,14 @@ const AssignmentPageLayout = ({
{!selectMode && (
-
}
- />
+
+
+ 문제 추가
+
)}
-
-
+ 취소
+ 등록
diff --git a/src/components/common/CourseOverview/CourseActionsBar.tsx b/src/components/common/CourseOverview/CourseActionsBar.tsx
index 1c2bc9c..b7344ef 100644
--- a/src/components/common/CourseOverview/CourseActionsBar.tsx
+++ b/src/components/common/CourseOverview/CourseActionsBar.tsx
@@ -3,14 +3,8 @@ import Button from '../Button';
const CourseActionsBar = ({isActive}: {isActive: boolean}) => {
return (
-
-
+ 학생 목록
+ 단원 추가
);
};
diff --git a/src/components/common/Dashboard/CourseList.tsx b/src/components/common/Dashboard/CourseList.tsx
index 403e373..e1c9577 100644
--- a/src/components/common/Dashboard/CourseList.tsx
+++ b/src/components/common/Dashboard/CourseList.tsx
@@ -2,6 +2,7 @@ import logo from '../../../assets/images/snowCode_logo_mini.svg';
import CourseCard from './CourseCard';
import {AddIcon} from '../../../assets/svg';
import type {Course, UserType} from './types';
+import Button from '../Button';
const courses: Course[] = [
{
@@ -45,10 +46,13 @@ const CourseList = ({userType}: CourseListProps) => {
강의 목록
{userType === 'admin' && (
-
+
- 추가
-
+ 추가
+
)}
diff --git a/src/pages/admin/assignments/AssignmentCreatePage.tsx b/src/pages/admin/assignments/AssignmentCreatePage.tsx
index 0d4180d..1b27a2b 100644
--- a/src/pages/admin/assignments/AssignmentCreatePage.tsx
+++ b/src/pages/admin/assignments/AssignmentCreatePage.tsx
@@ -3,6 +3,8 @@ import LabeledInput from '@/components/admin/form/LabeledInput';
import FileUpload from '@/components/admin/form/FileUpload';
import {useState} from 'react';
import LabeledDropdown from '@/components/admin/form/LabeledDropdown';
+import Button from '@/components/common/Button';
+import {AddIcon} from '@/assets/svg';
const AssignmentCreatePage = () => {
const [examples, setExamples] = useState([{input: '', output: '', 공개: ''}]);
@@ -58,11 +60,10 @@ const AssignmentCreatePage = () => {
))}
-
- + 추가
-
+
+
+ 추가
+
{}}
diff --git a/src/pages/common/LandingPage.tsx b/src/pages/common/LandingPage.tsx
index 539aba3..fac5388 100644
--- a/src/pages/common/LandingPage.tsx
+++ b/src/pages/common/LandingPage.tsx
@@ -6,7 +6,7 @@ import snowCodeStudent from '/src/assets/images/snowCode_student.svg';
import snowCodeAdmin from '/src/assets/images/snowCode_admin.svg';
import googleLogo from '/src/assets/images/google_logo.svg';
import {ArrowrightIcon} from '../../assets/svg';
-import ActionButton from '../../components/common/ActionButton';
+import Button from '@/components/common/Button';
type HoverState = 'none' | 'student' | 'admin';
@@ -39,11 +39,13 @@ export default function LandingPage() {
{/* 상단 오른쪽 "다음으로" 버튼 */}
-
+ size='none'
+ className='leading-7 text-lg'>
다음으로
-
+
{/* 로고 이미지 (선택/호버에 따라 이미지 변경) */}
@@ -68,20 +70,22 @@ export default function LandingPage() {
-
setSelected('student')}
onMouseEnter={() => setHover('student')}
- onMouseLeave={() => selected === 'none' && setHover('none')}
- selected={selected === 'student'}
- />
- selected === 'none' && setHover('none')}>
+ 학생
+
+ setSelected('admin')}
onMouseEnter={() => setHover('admin')}
- onMouseLeave={() => selected === 'none' && setHover('none')}
- selected={selected === 'admin'}
- />
+ onMouseLeave={() => selected === 'none' && setHover('none')}>
+ 관리자
+
diff --git a/src/pages/common/UserIdInputPage.tsx b/src/pages/common/UserIdInputPage.tsx
index 1b241b7..d14d8fe 100644
--- a/src/pages/common/UserIdInputPage.tsx
+++ b/src/pages/common/UserIdInputPage.tsx
@@ -1,8 +1,8 @@
import {useState, useRef, useEffect} from 'react';
import {useNavigate, useLocation} from 'react-router-dom';
-import ActionButton from '../../components/common/ActionButton';
import SnowCodeEntryMini from '../../assets/images/snowCode_entry_mini.svg';
import {ArrowleftIcon} from '../../assets/svg';
+import Button from '@/components/common/Button';
export default function UserIdInputPage() {
const navigate = useNavigate();
@@ -78,13 +78,14 @@ export default function UserIdInputPage() {
-
+ size='none'
+ className='leading-7 text-lg'>
이전으로
-
+
-
@@ -121,13 +122,14 @@ export default function UserIdInputPage() {
/>
))}
-
-
+ className='disabled:cursor-not-allowed'>
+ 확인
+
);
From 2993ec36e8f0eeab09d7d6a9063f94c5685ad4f8 Mon Sep 17 00:00:00 2001
From: suminb99
Date: Tue, 30 Dec 2025 22:38:26 +0900
Subject: [PATCH 27/42] =?UTF-8?q?#21=20feat:=20Header=EC=97=90=20IconButto?=
=?UTF-8?q?n=20=EB=8C=80=EC=8B=A0=20Button=20=EC=BB=B4=ED=8F=AC=EB=84=8C?=
=?UTF-8?q?=ED=8A=B8=EB=A1=9C=20=EA=B5=90=EC=B2=B4=20=EB=B0=8F=20=EC=A0=81?=
=?UTF-8?q?=EC=9A=A9?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/layout/Layout.tsx | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/src/layout/Layout.tsx b/src/layout/Layout.tsx
index ea5f579..1abf2c6 100644
--- a/src/layout/Layout.tsx
+++ b/src/layout/Layout.tsx
@@ -2,7 +2,7 @@
import {useLocation, Outlet} from 'react-router-dom';
import BaseHeader from '../components/common/BaseHeader';
import {NotificationIcon, SignoutIcon, UserIcon, ChatIcon} from '../assets/svg';
-import IconButton from '../components/common/IconButton';
+import Button from '@/components/common/Button';
const Layout = () => {
const location = useLocation();
@@ -42,7 +42,9 @@ const Layout = () => {
);
From 27fbea93f008805cc2dfbaa0406f8e3ac25d588b Mon Sep 17 00:00:00 2001
From: suminb99
Date: Tue, 30 Dec 2025 22:39:38 +0900
Subject: [PATCH 28/42] =?UTF-8?q?#21=20feat:=20Button=20=EC=BB=B4=ED=8F=AC?=
=?UTF-8?q?=EB=84=8C=ED=8A=B8=20variant=20=EC=86=8D=EC=84=B1=20=EC=88=98?=
=?UTF-8?q?=EC=A0=95?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/components/common/Button.tsx | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/components/common/Button.tsx b/src/components/common/Button.tsx
index 92f64f0..1812ea9 100644
--- a/src/components/common/Button.tsx
+++ b/src/components/common/Button.tsx
@@ -17,10 +17,10 @@ const button = tv({
default: 'w-24 h-10 px-3 py-1.5',
compact: 'w-fit leading-5 px-3 py-1.5',
wide: 'w-40 py-[15px]',
- none: 'w-fit p-0',
+ none: 'p-0',
},
isIcon: {
- true: 'rounded-full',
+ true: 'rounded-full w-16 h-16',
},
},
defaultVariants: {
From 194434eaf352ee78deedb2e7571ccc23e12d7001 Mon Sep 17 00:00:00 2001
From: suminb99
Date: Tue, 30 Dec 2025 23:26:03 +0900
Subject: [PATCH 29/42] =?UTF-8?q?#21=20refactor:=20=EB=B2=84=ED=8A=BC=20?=
=?UTF-8?q?=EC=BB=B4=ED=8F=AC=EB=84=8C=ED=8A=B8=20color=20/=20size=20/=20c?=
=?UTF-8?q?ontent=20variant=20=EA=B5=AC=EC=A1=B0=EB=A1=9C=20=ED=99=95?=
=?UTF-8?q?=EC=9E=A5=20=EB=B0=8F=20=EB=B6=84=EB=A6=AC?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/components/common/Button.tsx | 17 ++++++++++-------
1 file changed, 10 insertions(+), 7 deletions(-)
diff --git a/src/components/common/Button.tsx b/src/components/common/Button.tsx
index 1812ea9..34bdec6 100644
--- a/src/components/common/Button.tsx
+++ b/src/components/common/Button.tsx
@@ -1,7 +1,7 @@
import {tv, type VariantProps} from 'tailwind-variants/lite';
const button = tv({
- base: 'cursor-pointer flex-center text-center gap-2 text-base font-medium whitespace-nowrap rounded-[10px] border',
+ base: 'cursor-pointer rounded-[10px] border',
variants: {
color: {
primary: 'bg-primary text-white border-primary',
@@ -15,19 +15,22 @@ const button = tv({
},
size: {
default: 'w-24 h-10 px-3 py-1.5',
- compact: 'w-fit leading-5 px-3 py-1.5',
+ compact: 'w-fit px-3 py-1.5 leading-5',
wide: 'w-40 py-[15px]',
- none: 'p-0',
+ none: 'w-fit p-0',
+ icon: 'w-16 h-16 p-0 rounded-full', // 아이콘 버튼 rounded 속성 적용
},
- isIcon: {
- true: 'rounded-full w-16 h-16',
+ content: {
+ text: 'text-center text-base font-medium whitespace-nowrap',
+ icon: 'flex-center',
+ mixed:
+ 'flex-center gap-2 text-center text-base font-medium whitespace-nowrap',
},
},
defaultVariants: {
color: 'primary',
size: 'default',
- disabled: false,
- isIcon: false,
+ content: 'text',
},
});
From 39a5da2b139dfb75b13fbe2dfdc1486496440441 Mon Sep 17 00:00:00 2001
From: suminb99
Date: Tue, 30 Dec 2025 23:26:32 +0900
Subject: [PATCH 30/42] =?UTF-8?q?##21=20feat:=20content=20=EC=86=8D?=
=?UTF-8?q?=EC=84=B1=20=EC=84=A4=EC=A0=95?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/components/admin/assignments/AssignmentPageLayout.tsx | 2 +-
src/layout/Layout.tsx | 6 +++++-
src/pages/admin/assignments/AssignmentCreatePage.tsx | 6 +++++-
3 files changed, 11 insertions(+), 3 deletions(-)
diff --git a/src/components/admin/assignments/AssignmentPageLayout.tsx b/src/components/admin/assignments/AssignmentPageLayout.tsx
index 325c153..2df8863 100644
--- a/src/components/admin/assignments/AssignmentPageLayout.tsx
+++ b/src/components/admin/assignments/AssignmentPageLayout.tsx
@@ -32,7 +32,7 @@ const AssignmentPageLayout = ({
{!selectMode && (
-
+
문제 추가
diff --git a/src/layout/Layout.tsx b/src/layout/Layout.tsx
index 1abf2c6..ad1aa54 100644
--- a/src/layout/Layout.tsx
+++ b/src/layout/Layout.tsx
@@ -42,7 +42,11 @@ const Layout = () => {
-
+
추가
From fd763bb42feaa2d7fa61976ad0e2fe24baf1ea28 Mon Sep 17 00:00:00 2001
From: suminb99
Date: Thu, 1 Jan 2026 21:32:25 +0900
Subject: [PATCH 31/42] =?UTF-8?q?#21=20feat:=20tailwind-variants=20?=
=?UTF-8?q?=EC=A0=81=EC=9A=A9=20Badge=20=EC=BB=B4=ED=8F=AC=EB=84=8C?=
=?UTF-8?q?=ED=8A=B8=20=EC=B6=94=EA=B0=80?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/components/common/Badge.tsx | 84 +++++++++++++++++++++++++++++++++
1 file changed, 84 insertions(+)
create mode 100644 src/components/common/Badge.tsx
diff --git a/src/components/common/Badge.tsx b/src/components/common/Badge.tsx
new file mode 100644
index 0000000..19d4e4a
--- /dev/null
+++ b/src/components/common/Badge.tsx
@@ -0,0 +1,84 @@
+import {tv, type VariantProps} from 'tailwind-variants/lite';
+
+const badgeStyles = tv({
+ base: 'rounded-[35px] px-3.5 py-1.5 text-center text-base font-medium border whitespace-nowrap',
+});
+
+const scheduleBadgeStyles = tv({
+ extend: badgeStyles,
+ variants: {
+ schedule: {
+ upcoming:
+ 'bg-radial-[50%_50%_at_50%_50%] from-[#7D63FF] from-38% to-[#AB9AFF] to-100% border-0 text-white',
+ later: 'bg-[#403D4D] border-[#5C5B7F] text-white',
+ },
+ },
+});
+
+const submissionBadgeStyles = tv({
+ extend: badgeStyles,
+ base: 'bg-transparent flex-center',
+ variants: {
+ status: {
+ correct: 'border-primary text-primary',
+ incorrect: 'border-[#FF6F6F] text-[#FF6F6F]',
+ pending: 'border-light-black text-light-black',
+ },
+ onlyIcon: {
+ true: 'rounded-full',
+ false: 'gap-2',
+ },
+ },
+ defaultVariants: {
+ onlyIcon: false,
+ },
+});
+
+const indexBadgeStyles = tv({
+ extend: badgeStyles,
+ variants: {
+ kind: {
+ unit: 'bg-secondary-black border-secondary-black text-white',
+ problem: 'bg-light-black border-light-black text-white',
+ },
+ },
+});
+
+type ScheduleBadgeProps = {
+ variant: 'schedule';
+} & VariantProps;
+
+type SubmissionBadgeProps = {
+ variant: 'submission';
+} & VariantProps;
+
+type IndexBadgeProps = {
+ variant: 'index';
+} & VariantProps;
+
+type BadgeProps = (
+ | ScheduleBadgeProps
+ | SubmissionBadgeProps
+ | IndexBadgeProps
+) & {
+ children: React.ReactNode;
+ className?: string;
+};
+
+const Badge = ({children, variant, className, ...props}: BadgeProps) => {
+ const renderBadgeVariant = () => {
+ if (variant === 'schedule') {
+ // 일정 배지
+ return scheduleBadgeStyles({className, ...props});
+ } else if (variant === 'submission') {
+ // 제출 상태 배지
+ return submissionBadgeStyles({className, ...props});
+ } else if (variant === 'index') {
+ // 인덱스 배지 (단원, 문제 등)
+ return indexBadgeStyles({className, ...props});
+ }
+ };
+ return {children};
+};
+
+export default Badge;
From 381dc0f98018cc4782c77223812e85eb081e101b Mon Sep 17 00:00:00 2001
From: suminb99
Date: Thu, 1 Jan 2026 21:48:36 +0900
Subject: [PATCH 32/42] =?UTF-8?q?#21=20style:=20Badge=20=EC=BB=B4=ED=8F=AC?=
=?UTF-8?q?=EB=84=8C=ED=8A=B8=20leading=20=EC=86=8D=EC=84=B1=20=EC=B6=94?=
=?UTF-8?q?=EA=B0=80?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/components/common/Badge.tsx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/components/common/Badge.tsx b/src/components/common/Badge.tsx
index 19d4e4a..b45d542 100644
--- a/src/components/common/Badge.tsx
+++ b/src/components/common/Badge.tsx
@@ -1,7 +1,7 @@
import {tv, type VariantProps} from 'tailwind-variants/lite';
const badgeStyles = tv({
- base: 'rounded-[35px] px-3.5 py-1.5 text-center text-base font-medium border whitespace-nowrap',
+ base: 'rounded-full px-3.5 py-1.5 leading-[19px] text-center text-base font-medium border whitespace-nowrap',
});
const scheduleBadgeStyles = tv({
From 9c4624db18e58c050e68f1590ce51f9c0a62f215 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=ED=95=98=EC=A7=80=EB=AF=BC?=
<163178666+JiiminHa@users.noreply.github.com>
Date: Tue, 6 Jan 2026 14:55:42 +0900
Subject: [PATCH 33/42] =?UTF-8?q?#13=20fix:=20=EA=B3=BC=EC=A0=9C=20?=
=?UTF-8?q?=EA=B4=80=EB=A0=A8=20=ED=8E=98=EC=9D=B4=EC=A7=80=20=EB=B9=8C?=
=?UTF-8?q?=EB=93=9C=20=EC=98=A4=EB=A5=98=20=EC=88=98=EC=A0=95?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../common/CourseOverview/EmptyCourse.tsx | 2 +-
.../assignments/AssignmentCreatePage.tsx | 2 +-
.../assignments/AssignmentSelectPage.tsx | 19 -------------------
.../admin/assignments/AssignmentsPage.tsx | 4 ++--
4 files changed, 4 insertions(+), 23 deletions(-)
diff --git a/src/components/common/CourseOverview/EmptyCourse.tsx b/src/components/common/CourseOverview/EmptyCourse.tsx
index e2aca67..613e118 100644
--- a/src/components/common/CourseOverview/EmptyCourse.tsx
+++ b/src/components/common/CourseOverview/EmptyCourse.tsx
@@ -11,7 +11,7 @@ const EmptyCourse = () => {
아직 생성된 단원이 없어요
-
+
);
};
diff --git a/src/pages/admin/assignments/AssignmentCreatePage.tsx b/src/pages/admin/assignments/AssignmentCreatePage.tsx
index 0d4180d..b8cb713 100644
--- a/src/pages/admin/assignments/AssignmentCreatePage.tsx
+++ b/src/pages/admin/assignments/AssignmentCreatePage.tsx
@@ -35,7 +35,7 @@ const AssignmentCreatePage = () => {
className='w-full'
/>
- {examples.map((ex, idx) => (
+ {examples.map((_, idx) => (
diff --git a/src/pages/admin/assignments/AssignmentSelectPage.tsx b/src/pages/admin/assignments/AssignmentSelectPage.tsx
index f79d1ae..32b9304 100644
--- a/src/pages/admin/assignments/AssignmentSelectPage.tsx
+++ b/src/pages/admin/assignments/AssignmentSelectPage.tsx
@@ -1,7 +1,5 @@
import {coursesResponse} from '../../../components/admin/assignments/dummy/response';
-import {useState} from 'react';
import {useParams} from 'react-router-dom';
-import type {Assignment} from '../../../components/admin/assignments/dummy/types';
import AssignmentPageLayout from '../../../components/admin/assignments/AssignmentPageLayout';
const AssignmentSelectPage = () => {
@@ -18,28 +16,11 @@ const AssignmentSelectPage = () => {
(course) => course.title === courseTitle
);
- // 선택된 문제 업데이트
- const [linkedAssignments, setLinkedAssignments] = useState
([]);
-
- const onLinkAssignments = (
- id: number,
- title: string,
- isSelected: boolean
- ) => {
- setLinkedAssignments((prev) => {
- if (isSelected) {
- return [...prev, {id, title}];
- }
- return prev.filter((a) => a.id !== id);
- });
- };
-
return (
);
};
diff --git a/src/pages/admin/assignments/AssignmentsPage.tsx b/src/pages/admin/assignments/AssignmentsPage.tsx
index 4f3b0a7..965388b 100644
--- a/src/pages/admin/assignments/AssignmentsPage.tsx
+++ b/src/pages/admin/assignments/AssignmentsPage.tsx
@@ -1,5 +1,5 @@
-import {coursesResponse} from '../../components/admin/assignments/dummy/response';
-import AssignmentPageLayout from '../../components/admin/assignments/AssignmentPageLayout';
+import {coursesResponse} from '../../../components/admin/assignments/dummy/response';
+import AssignmentPageLayout from '../../../components/admin/assignments/AssignmentPageLayout';
const AssignmentsPage = () => {
// 전체 강의 가져오기
From 631b4ba24ed2403f89706a2a19763e3435fc46d1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=ED=95=98=EC=A7=80=EB=AF=BC?=
<163178666+JiiminHa@users.noreply.github.com>
Date: Tue, 6 Jan 2026 14:56:45 +0900
Subject: [PATCH 34/42] =?UTF-8?q?#13=20refactor:=20svgr=20=EC=82=AC?=
=?UTF-8?q?=EC=9A=A9=ED=95=98=EB=8F=84=EB=A1=9D=20SVG=20=ED=95=B8=EB=93=A4?=
=?UTF-8?q?=EB=A7=81=20=EB=B0=A9=EC=8B=9D=20=EB=B3=80=EA=B2=BD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/App.tsx | 15 +-
src/assets/svg/AddIcon.tsx | 17 --
src/assets/svg/ArrowdownIcon.tsx | 17 --
src/assets/svg/ArrowleftIcon.tsx | 15 --
src/assets/svg/ArrowrightIcon.tsx | 15 --
src/assets/svg/ChatIcon.tsx | 18 --
src/assets/svg/Chevrondown.tsx | 17 --
src/assets/svg/DeleteIcon.tsx | 17 --
src/assets/svg/DragAndDropIcon.tsx | 17 --
src/assets/svg/EditIcon.tsx | 18 --
src/assets/svg/EllipsisIcon.tsx | 14 --
src/assets/svg/File.tsx | 22 ---
src/assets/svg/NotificationIcon.tsx | 15 --
src/assets/svg/ProfileImage.tsx | 183 ------------------
src/assets/svg/Search.tsx | 18 --
src/assets/svg/SignoutIcon.tsx | 19 --
src/assets/svg/SingleEllipsisIcon.tsx | 11 --
src/assets/svg/UserIcon.tsx | 18 --
src/assets/svg/index.ts | 17 --
.../admin/assignments/AssignmentCard.tsx | 10 +-
.../assignments/AssignmentPageLayout.tsx | 2 +-
.../admin/assignments/CourseSelector.tsx | 2 +-
src/components/admin/form/FileUpload.tsx | 4 +-
src/components/admin/form/LabeledDropdown.tsx | 2 +-
src/components/common/Button.tsx | 3 +-
.../CourseOverview/CourseActionsBar.tsx | 16 +-
.../common/CourseOverview/CourseHero.tsx | 6 +-
.../common/CourseOverview/CourseStat.tsx | 2 +-
.../common/Dashboard/CourseCard.tsx | 2 +-
.../common/Dashboard/CourseList.tsx | 2 +-
src/components/common/IconButton.tsx | 2 +-
.../admin/student/studentManagementPage.tsx | 8 +-
src/pages/common/LandingPage.tsx | 2 +-
src/pages/common/UserIdInputPage.tsx | 2 +-
34 files changed, 50 insertions(+), 498 deletions(-)
delete mode 100644 src/assets/svg/AddIcon.tsx
delete mode 100644 src/assets/svg/ArrowdownIcon.tsx
delete mode 100644 src/assets/svg/ArrowleftIcon.tsx
delete mode 100644 src/assets/svg/ArrowrightIcon.tsx
delete mode 100644 src/assets/svg/ChatIcon.tsx
delete mode 100644 src/assets/svg/Chevrondown.tsx
delete mode 100644 src/assets/svg/DeleteIcon.tsx
delete mode 100644 src/assets/svg/DragAndDropIcon.tsx
delete mode 100644 src/assets/svg/EditIcon.tsx
delete mode 100644 src/assets/svg/EllipsisIcon.tsx
delete mode 100644 src/assets/svg/File.tsx
delete mode 100644 src/assets/svg/NotificationIcon.tsx
delete mode 100644 src/assets/svg/ProfileImage.tsx
delete mode 100644 src/assets/svg/Search.tsx
delete mode 100644 src/assets/svg/SignoutIcon.tsx
delete mode 100644 src/assets/svg/SingleEllipsisIcon.tsx
delete mode 100644 src/assets/svg/UserIcon.tsx
delete mode 100644 src/assets/svg/index.ts
diff --git a/src/App.tsx b/src/App.tsx
index 0830cfa..313d1b5 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -3,11 +3,12 @@ import Layout from './layout/Layout';
import LandingPage from './pages/common/LandingPage';
import UserIdInputPage from './pages/common/UserIdInputPage';
import Dashboard from './pages/common/Dashboard';
-import AssignmentsPage from './pages/admin/AssignmentsPage';
-import AssignmentSelectPage from './pages/admin/AssignmentSelectPage';
+import AssignmentsPage from './pages/admin/assignments/AssignmentsPage';
+import AssignmentSelectPage from './pages/admin/assignments/AssignmentSelectPage';
import CourseOverviewPage from './pages/common/CourseOverviewPage';
import AssignmentCreatePage from './pages/admin/assignments/AssignmentCreatePage';
import CourseCreatePage from './pages/admin/courses/CourseCreatePage';
+import StudentManagementPage from './pages/admin/student/studentManagementPage';
function App() {
return (
@@ -16,7 +17,7 @@ function App() {
{/* 공통 영역 */}
}>
} />
- } />
+ } />
{/* 학생 전용 영역 */}
@@ -31,13 +32,11 @@ function App() {
{/* 추가 페이지들 */}
} />
} />
+ } />
} />
} />
- }
- />
- } />
+ } />
+ } />
diff --git a/src/assets/svg/AddIcon.tsx b/src/assets/svg/AddIcon.tsx
deleted file mode 100644
index 71637e0..0000000
--- a/src/assets/svg/AddIcon.tsx
+++ /dev/null
@@ -1,17 +0,0 @@
-import * as React from 'react';
-import type {SVGProps} from 'react';
-const SvgAddIcon = (props: SVGProps) => (
-
-);
-export default SvgAddIcon;
diff --git a/src/assets/svg/ArrowdownIcon.tsx b/src/assets/svg/ArrowdownIcon.tsx
deleted file mode 100644
index 6f96561..0000000
--- a/src/assets/svg/ArrowdownIcon.tsx
+++ /dev/null
@@ -1,17 +0,0 @@
-import * as React from 'react';
-import type {SVGProps} from 'react';
-const SvgArrowdownIcon = (props: SVGProps) => (
-
-);
-export default SvgArrowdownIcon;
diff --git a/src/assets/svg/ArrowleftIcon.tsx b/src/assets/svg/ArrowleftIcon.tsx
deleted file mode 100644
index 4793458..0000000
--- a/src/assets/svg/ArrowleftIcon.tsx
+++ /dev/null
@@ -1,15 +0,0 @@
-import * as React from 'react';
-import type {SVGProps} from 'react';
-const SvgArrowleftIcon = (props: SVGProps) => (
-
-);
-export default SvgArrowleftIcon;
diff --git a/src/assets/svg/ArrowrightIcon.tsx b/src/assets/svg/ArrowrightIcon.tsx
deleted file mode 100644
index c0a119e..0000000
--- a/src/assets/svg/ArrowrightIcon.tsx
+++ /dev/null
@@ -1,15 +0,0 @@
-import * as React from 'react';
-import type {SVGProps} from 'react';
-const SvgArrowrightIcon = (props: SVGProps) => (
-
-);
-export default SvgArrowrightIcon;
diff --git a/src/assets/svg/ChatIcon.tsx b/src/assets/svg/ChatIcon.tsx
deleted file mode 100644
index e3f8a0f..0000000
--- a/src/assets/svg/ChatIcon.tsx
+++ /dev/null
@@ -1,18 +0,0 @@
-import * as React from 'react';
-import type {SVGProps} from 'react';
-const SvgChatIcon = (props: SVGProps) => (
-
-);
-export default SvgChatIcon;
diff --git a/src/assets/svg/Chevrondown.tsx b/src/assets/svg/Chevrondown.tsx
deleted file mode 100644
index e5bd8cb..0000000
--- a/src/assets/svg/Chevrondown.tsx
+++ /dev/null
@@ -1,17 +0,0 @@
-import * as React from 'react';
-import type {SVGProps} from 'react';
-const SvgChevrondown = (props: SVGProps) => (
-
-);
-export default SvgChevrondown;
diff --git a/src/assets/svg/DeleteIcon.tsx b/src/assets/svg/DeleteIcon.tsx
deleted file mode 100644
index fce305c..0000000
--- a/src/assets/svg/DeleteIcon.tsx
+++ /dev/null
@@ -1,17 +0,0 @@
-import * as React from 'react';
-import type {SVGProps} from 'react';
-const SvgDeleteIcon = (props: SVGProps) => (
-
-);
-export default SvgDeleteIcon;
diff --git a/src/assets/svg/DragAndDropIcon.tsx b/src/assets/svg/DragAndDropIcon.tsx
deleted file mode 100644
index 6fda321..0000000
--- a/src/assets/svg/DragAndDropIcon.tsx
+++ /dev/null
@@ -1,17 +0,0 @@
-import * as React from 'react';
-import type {SVGProps} from 'react';
-const SvgDragAndDropIcon = (props: SVGProps) => (
-
-);
-export default SvgDragAndDropIcon;
diff --git a/src/assets/svg/EditIcon.tsx b/src/assets/svg/EditIcon.tsx
deleted file mode 100644
index 1bd6f31..0000000
--- a/src/assets/svg/EditIcon.tsx
+++ /dev/null
@@ -1,18 +0,0 @@
-import * as React from 'react';
-import type {SVGProps} from 'react';
-const SvgEditIcon = (props: SVGProps) => (
-
-);
-export default SvgEditIcon;
diff --git a/src/assets/svg/EllipsisIcon.tsx b/src/assets/svg/EllipsisIcon.tsx
deleted file mode 100644
index e679841..0000000
--- a/src/assets/svg/EllipsisIcon.tsx
+++ /dev/null
@@ -1,14 +0,0 @@
-import * as React from 'react';
-import type {SVGProps} from 'react';
-const SvgEllipsisIcon = (props: SVGProps) => (
-
-);
-export default SvgEllipsisIcon;
diff --git a/src/assets/svg/File.tsx b/src/assets/svg/File.tsx
deleted file mode 100644
index ddef665..0000000
--- a/src/assets/svg/File.tsx
+++ /dev/null
@@ -1,22 +0,0 @@
-import * as React from 'react';
-import type {SVGProps} from 'react';
-const SvgFile = (props: SVGProps) => (
-
-);
-export default SvgFile;
diff --git a/src/assets/svg/NotificationIcon.tsx b/src/assets/svg/NotificationIcon.tsx
deleted file mode 100644
index 3b7f13e..0000000
--- a/src/assets/svg/NotificationIcon.tsx
+++ /dev/null
@@ -1,15 +0,0 @@
-import * as React from 'react';
-import type {SVGProps} from 'react';
-const SvgNotificationIcon = (props: SVGProps) => (
-
-);
-export default SvgNotificationIcon;
diff --git a/src/assets/svg/ProfileImage.tsx b/src/assets/svg/ProfileImage.tsx
deleted file mode 100644
index 6fb793f..0000000
--- a/src/assets/svg/ProfileImage.tsx
+++ /dev/null
@@ -1,183 +0,0 @@
-import * as React from 'react';
-import type {SVGProps} from 'react';
-const SvgProfileImage = (props: SVGProps) => (
-
-);
-export default SvgProfileImage;
diff --git a/src/assets/svg/Search.tsx b/src/assets/svg/Search.tsx
deleted file mode 100644
index 216d65f..0000000
--- a/src/assets/svg/Search.tsx
+++ /dev/null
@@ -1,18 +0,0 @@
-import * as React from 'react';
-import type {SVGProps} from 'react';
-const SvgSearch = (props: SVGProps) => (
-
-);
-export default SvgSearch;
diff --git a/src/assets/svg/SignoutIcon.tsx b/src/assets/svg/SignoutIcon.tsx
deleted file mode 100644
index feba1ab..0000000
--- a/src/assets/svg/SignoutIcon.tsx
+++ /dev/null
@@ -1,19 +0,0 @@
-import * as React from 'react';
-import type {SVGProps} from 'react';
-const SvgSignoutIcon = (props: SVGProps) => (
-
-);
-export default SvgSignoutIcon;
diff --git a/src/assets/svg/SingleEllipsisIcon.tsx b/src/assets/svg/SingleEllipsisIcon.tsx
deleted file mode 100644
index 2194480..0000000
--- a/src/assets/svg/SingleEllipsisIcon.tsx
+++ /dev/null
@@ -1,11 +0,0 @@
-import type {SVGProps} from 'react';
-const SvgSingleEllipsisIcon = (props: SVGProps) => (
-
-);
-export default SvgSingleEllipsisIcon;
diff --git a/src/assets/svg/UserIcon.tsx b/src/assets/svg/UserIcon.tsx
deleted file mode 100644
index e2cc120..0000000
--- a/src/assets/svg/UserIcon.tsx
+++ /dev/null
@@ -1,18 +0,0 @@
-import * as React from 'react';
-import type {SVGProps} from 'react';
-const SvgUserIcon = (props: SVGProps) => (
-
-);
-export default SvgUserIcon;
diff --git a/src/assets/svg/index.ts b/src/assets/svg/index.ts
deleted file mode 100644
index fa8243b..0000000
--- a/src/assets/svg/index.ts
+++ /dev/null
@@ -1,17 +0,0 @@
-export {default as AddIcon} from './AddIcon';
-export {default as ArrowdownIcon} from './ArrowdownIcon';
-export {default as ArrowleftIcon} from './ArrowleftIcon';
-export {default as ArrowrightIcon} from './ArrowrightIcon';
-export {default as ChatIcon} from './ChatIcon';
-export {default as Chevrondown} from './Chevrondown';
-export {default as DeleteIcon} from './DeleteIcon';
-export {default as DragAndDropIcon} from './DragAndDropIcon';
-export {default as EditIcon} from './EditIcon';
-export {default as EllipsisIcon} from './EllipsisIcon';
-export {default as File} from './File';
-export {default as NotificationIcon} from './NotificationIcon';
-export {default as ProfileImage} from './ProfileImage';
-export {default as Search} from './Search';
-export {default as SignoutIcon} from './SignoutIcon';
-export {default as SingleEllipsisIcon} from './SingleEllipsisIcon';
-export {default as UserIcon} from './UserIcon';
diff --git a/src/components/admin/assignments/AssignmentCard.tsx b/src/components/admin/assignments/AssignmentCard.tsx
index 75b3727..264cb48 100644
--- a/src/components/admin/assignments/AssignmentCard.tsx
+++ b/src/components/admin/assignments/AssignmentCard.tsx
@@ -1,10 +1,8 @@
import {useState} from 'react';
-import {
- SingleEllipsisIcon,
- DragAndDropIcon,
- DeleteIcon,
- EditIcon,
-} from '../../../assets/svg';
+import SingleEllipsisIcon from '/public/svg/singleEllipsisIcon.svg?react';
+import DragAndDropIcon from '/public/svg/dragAndDropIcon.svg?react';
+import DeleteIcon from '/public/svg/deleteIcon.svg?react';
+import EditIcon from '/public/svg/editIcon.svg?react';
import type {Assignment} from './dummy/types';
interface AssignmentCardProps extends Assignment {
diff --git a/src/components/admin/assignments/AssignmentPageLayout.tsx b/src/components/admin/assignments/AssignmentPageLayout.tsx
index e72355a..be99646 100644
--- a/src/components/admin/assignments/AssignmentPageLayout.tsx
+++ b/src/components/admin/assignments/AssignmentPageLayout.tsx
@@ -2,7 +2,7 @@ import CourseSelector from './CourseSelector';
import AssignmentList from './AssignmentList';
import Button from '../../common/Button';
import type {Course} from './dummy/types';
-import {AddIcon} from '../../../assets/svg';
+import AddIcon from '/public/svg/addIcon.svg?react';
import {useState} from 'react';
interface AssignmentPageLayoutProps {
diff --git a/src/components/admin/assignments/CourseSelector.tsx b/src/components/admin/assignments/CourseSelector.tsx
index db13060..c1fdb46 100644
--- a/src/components/admin/assignments/CourseSelector.tsx
+++ b/src/components/admin/assignments/CourseSelector.tsx
@@ -1,5 +1,5 @@
import {useState} from 'react';
-import {ArrowdownIcon} from '../../../assets/svg';
+import ArrowdownIcon from '/public/svg/arrowdownIcon.svg?react';
import type {Course} from './dummy/types';
interface CourseSelectorProps {
diff --git a/src/components/admin/form/FileUpload.tsx b/src/components/admin/form/FileUpload.tsx
index 8b817e6..c119837 100644
--- a/src/components/admin/form/FileUpload.tsx
+++ b/src/components/admin/form/FileUpload.tsx
@@ -1,6 +1,6 @@
import {useRef, useState} from 'react';
import type {ChangeEvent} from 'react';
-import {File} from '@/assets/svg';
+import FileIcon from '/public/svg/file.svg?react';
type FileUploadProps = {
label: string;
@@ -103,7 +103,7 @@ export default function FileUpload({
'focus:outline-none focus:ring-2 focus:ring-primary',
].join(' ')}>
-
+
{description}
diff --git a/src/components/admin/form/LabeledDropdown.tsx b/src/components/admin/form/LabeledDropdown.tsx
index 680d2df..de05fef 100644
--- a/src/components/admin/form/LabeledDropdown.tsx
+++ b/src/components/admin/form/LabeledDropdown.tsx
@@ -1,5 +1,5 @@
import {useState, useRef} from 'react';
-import {Chevrondown} from '@/assets/svg';
+import Chevrondown from '/public/svg/chevrondown.svg?react';
import useClickOutside from '@/hooks/useClickOutside';
interface LabeledDropdownProps
diff --git a/src/components/common/Button.tsx b/src/components/common/Button.tsx
index 4be7e34..9ff4b1c 100644
--- a/src/components/common/Button.tsx
+++ b/src/components/common/Button.tsx
@@ -16,9 +16,10 @@ const buttonTheme: ButtonTheme = {
secondaryPurpleStroke: 'secondary-btn bg-purple-stroke text-secondary-black',
};
-const Button = ({theme, text, icon}: ButtonProps) => {
+const Button = ({theme, text, icon, onClick}: ButtonProps) => {
return (
{icon}
{text}
diff --git a/src/components/common/CourseOverview/CourseActionsBar.tsx b/src/components/common/CourseOverview/CourseActionsBar.tsx
index 1c2bc9c..2562536 100644
--- a/src/components/common/CourseOverview/CourseActionsBar.tsx
+++ b/src/components/common/CourseOverview/CourseActionsBar.tsx
@@ -1,11 +1,25 @@
+import {useNavigate} from 'react-router-dom';
import Button from '../Button';
-const CourseActionsBar = ({isActive}: {isActive: boolean}) => {
+interface CourseActionsBarProps {
+ isActive: boolean;
+ title: string;
+ section: string;
+}
+
+const CourseActionsBar = ({isActive, title, section}: CourseActionsBarProps) => {
+ const navigate = useNavigate();
+
+ const handleStudentListClick = () => {
+ navigate(`/admin/student?course=${title} (${section})`);
+ };
+
return (
{isAdmin && isActiveCourse && (
- {' '}
+
)}
diff --git a/src/components/common/CourseOverview/CourseStat.tsx b/src/components/common/CourseOverview/CourseStat.tsx
index f7daa61..4d3046a 100644
--- a/src/components/common/CourseOverview/CourseStat.tsx
+++ b/src/components/common/CourseOverview/CourseStat.tsx
@@ -10,7 +10,7 @@ const CourseStat = ({
studentCount,
}: CourseStatProps) => {
return (
-
+
{`${unitCount}단원 | ${assignmentCount}문제 ${
studentCount ? `| ${studentCount}명` : ''
}`}
diff --git a/src/components/common/Dashboard/CourseCard.tsx b/src/components/common/Dashboard/CourseCard.tsx
index 80242ed..a060786 100644
--- a/src/components/common/Dashboard/CourseCard.tsx
+++ b/src/components/common/Dashboard/CourseCard.tsx
@@ -1,5 +1,5 @@
import {useState} from 'react';
-import {EllipsisIcon} from '../../../assets/svg';
+import EllipsisIcon from '/public/svg/ellipsisIcon.svg?react';
import type {Course, UserType} from './types';
import {getSemesterLabel} from '../../../utils/getSemesterLabel';
diff --git a/src/components/common/Dashboard/CourseList.tsx b/src/components/common/Dashboard/CourseList.tsx
index 403e373..e16eba5 100644
--- a/src/components/common/Dashboard/CourseList.tsx
+++ b/src/components/common/Dashboard/CourseList.tsx
@@ -1,6 +1,6 @@
import logo from '../../../assets/images/snowCode_logo_mini.svg';
import CourseCard from './CourseCard';
-import {AddIcon} from '../../../assets/svg';
+import AddIcon from '/public/svg/addIcon.svg?react';
import type {Course, UserType} from './types';
const courses: Course[] = [
diff --git a/src/components/common/IconButton.tsx b/src/components/common/IconButton.tsx
index 5624350..8845a84 100644
--- a/src/components/common/IconButton.tsx
+++ b/src/components/common/IconButton.tsx
@@ -8,4 +8,4 @@ const IconButton = ({icon}: IconButtonProps) => {
return
{icon};
};
-export default IconButton;
+export default IconButton;
\ No newline at end of file
diff --git a/src/pages/admin/student/studentManagementPage.tsx b/src/pages/admin/student/studentManagementPage.tsx
index 520304d..3659dfa 100644
--- a/src/pages/admin/student/studentManagementPage.tsx
+++ b/src/pages/admin/student/studentManagementPage.tsx
@@ -1,11 +1,15 @@
import AssignmentFormLayout from '@/components/admin/assignments/AssignmentFormLayout';
-import {Search} from '@/assets/svg';
+import Search from '/public/svg/search.svg?react';
+import {useSearchParams} from 'react-router-dom';
export default function StudentManagementPage() {
+ const [searchParams] = useSearchParams();
+ const course = searchParams.get('course');
+
const titleExtra = (
- 소프트웨어 이해 (005)
+ {course || '전체'}
Date: Tue, 6 Jan 2026 14:57:32 +0900
Subject: [PATCH 35/42] =?UTF-8?q?refactor:=20Layout=20=EC=BB=B4=ED=8F=AC?=
=?UTF-8?q?=EB=84=8C=ED=8A=B8=20=ED=83=80=EC=9E=85=20=EC=B6=94=EA=B0=80=20?=
=?UTF-8?q?=EB=B0=8F=20SVG=20import=20=EB=B0=A9=EC=8B=9D=20=EB=B3=80?=
=?UTF-8?q?=EA=B2=BD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/layout/Layout.tsx | 29 +++++++++++++++++++++++------
1 file changed, 23 insertions(+), 6 deletions(-)
diff --git a/src/layout/Layout.tsx b/src/layout/Layout.tsx
index ea5f579..d55051e 100644
--- a/src/layout/Layout.tsx
+++ b/src/layout/Layout.tsx
@@ -1,8 +1,19 @@
// Layout.js
import {useLocation, Outlet} from 'react-router-dom';
import BaseHeader from '../components/common/BaseHeader';
-import {NotificationIcon, SignoutIcon, UserIcon, ChatIcon} from '../assets/svg';
+import NotificationIcon from '/public/svg/notificationIcon.svg?react';
+import SignoutIcon from '/public/svg/signoutIcon.svg?react';
+import UserIcon from '/public/svg/userIcon.svg?react';
+import ChatIcon from '/public/svg/chatIcon.svg?react';
import IconButton from '../components/common/IconButton';
+import React from 'react';
+
+type UserType = 'admin' | 'student';
+
+interface NavButton {
+ icon: React.ReactElement;
+ label: string;
+}
const Layout = () => {
const location = useLocation();
@@ -13,8 +24,8 @@ const Layout = () => {
const showHeader = !noHeaderPages.includes(pathname);
// 네비게이션 아이콘 버튼들
- const getNavigationButtons = (userType) => {
- const commonButtons = [
+ const getNavigationButtons = (userType: UserType): NavButton[] => {
+ const commonButtons: NavButton[] = [
{icon:
, label: '알림'},
{icon:
, label: '프로필'},
{icon:
, label: '로그아웃'},
@@ -31,14 +42,20 @@ const Layout = () => {
};
// 사용자 환영 메시지
- const getWelcomeMessage = (userName) => (
+ const getWelcomeMessage = (userName: string) => (
{userName}님 환영합니다!
);
// 네비게이션 컴포넌트
- const NavigationBar = ({buttons, width}) => (
+ const NavigationBar = ({
+ buttons,
+ width,
+ }: {
+ buttons: NavButton[];
+ width: string;
+ }) => (
);
};
diff --git a/src/components/common/CourseOverview/UnitHeader.tsx b/src/components/common/CourseOverview/UnitHeader.tsx
index e46f5ef..3f3bf47 100644
--- a/src/components/common/CourseOverview/UnitHeader.tsx
+++ b/src/components/common/CourseOverview/UnitHeader.tsx
@@ -1,5 +1,5 @@
-import UnitLabel from './UnitLabel';
-import lock from '../../../assets/images/lock.svg';
+import lock from '@/assets/images/lock.svg';
+import Badge from '../Badge';
interface UnitHeaderProps {
index: number;
@@ -19,7 +19,9 @@ const UnitHeader = ({
return (
-
+
+ {index}
+
{title}
diff --git a/src/components/common/CourseOverview/UnitLabel.tsx b/src/components/common/CourseOverview/UnitLabel.tsx
deleted file mode 100644
index 71ba934..0000000
--- a/src/components/common/CourseOverview/UnitLabel.tsx
+++ /dev/null
@@ -1,13 +0,0 @@
-interface UnitLabelProps {
- unitNo: number;
-}
-
-const UnitLabel = ({unitNo}: UnitLabelProps) => {
- return (
-
- {`${unitNo}단원`}
-
- );
-};
-
-export default UnitLabel;
diff --git a/src/components/common/Dashboard/ScheduleCard.tsx b/src/components/common/Dashboard/ScheduleCard.tsx
index 5d9ff04..17b2de1 100644
--- a/src/components/common/Dashboard/ScheduleCard.tsx
+++ b/src/components/common/Dashboard/ScheduleCard.tsx
@@ -1,3 +1,4 @@
+import Badge from '../Badge';
import type {Assignment} from './types';
interface ScheduleCardProps extends Assignment {
remainingDays: number;
@@ -11,14 +12,11 @@ const ScheduleCard = ({
}: ScheduleCardProps) => {
return (
-
- {`${remainingDays}일 전`}
-
+
+ {remainingDays}
+
{`${course} (${section})`}
From 204867d1c4caebc8e8749790ba8cacf0c129133a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=ED=95=98=EC=A7=80=EB=AF=BC?=
<163178666+JiiminHa@users.noreply.github.com>
Date: Tue, 6 Jan 2026 18:04:57 +0900
Subject: [PATCH 42/42] =?UTF-8?q?#13=20feat:=20coderabbit=20=EC=84=A4?=
=?UTF-8?q?=EC=A0=95=20=ED=8C=8C=EC=9D=BC=20=EC=B6=94=EA=B0=80?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.coderabbit.yaml | 123 +++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 123 insertions(+)
create mode 100644 .coderabbit.yaml
diff --git a/.coderabbit.yaml b/.coderabbit.yaml
new file mode 100644
index 0000000..c4c61b3
--- /dev/null
+++ b/.coderabbit.yaml
@@ -0,0 +1,123 @@
+language: 'ko-KR'
+early_access: true
+
+tone_instructions: |
+ 1. 피드백은 명확하고 구체적으로 작성하고, 문제 원인과 개선 방법을 함께 제시하세요.
+ 2. 리뷰는 교육적인 방향을 지향하며, 관련 공식 문서 링크를 함께 추천하세요.
+ 3. 비판보다는 개선 제안을 우선하세요.
+ 4. 칭찬은 짧고 위트 있게 작성하세요.
+
+reviews:
+ profile: educational
+ request_changes_workflow: true
+ high_level_summary: true
+ high_level_summary_placeholder: |
+ @coderabbitai summary
+ (졸업작품 평가 기준: 구조 / 타입 안정성 / 접근성 관점 요약)
+ poem: false
+ review_status: true
+ suggested_labels: true
+ commit_status: true
+ fail_commit_status: false
+ abort_on_close: true
+ high_level_summary_in_walkthrough: true
+ high_level_summary_placeholder: '@coderabbitai summary'
+ collapse_walkthrough: true
+
+ path_instructions:
+ - path: 'src/**/components/**/*.tsx'
+ instructions: |
+ React 컴포넌트 리뷰 시:
+ - 파일명은 PascalCase인지 확인
+ - Props 타입은 컴포넌트명Props 형식인지 확인
+ - 단일 export는 default export 권장 (팀 규칙 우선)
+ - 이벤트 핸들러는 handle 접두사 사용
+ - 접근성 고려 (label 연결, aria-* 속성)
+ - 접근성 관련 지적 시, 사용자 영향 예시 포함
+ - 불필요한 re-render 방지를 위한 memo/useCallback 검토
+ - premature optimization 지양
+
+ - path: 'src/**/hooks/**/*.ts'
+ instructions: |
+ 커스텀 훅 리뷰 시:
+ - 파일명과 함수명은 use* 접두사 사용
+ - 반환 타입의 일관성 유지 (object 또는 tuple)
+ - 에러/로딩 상태 명확히 분리
+ - TanStack Query 훅 네이밍 규칙 준수
+ (use + Action + Target + Query/Mutation)
+
+ - path: 'src/**/*.ts'
+ instructions: |
+ TypeScript 코드 리뷰 시:
+ - 타입/인터페이스는 PascalCase 사용
+ - interface는 객체 타입, type은 union/alias에 사용
+ - API 응답 타입은 XXXResponse 네이밍
+ - var 사용 금지, const 우선
+ - type-only import 적극 사용
+ - any 사용 시 사유 명확히 작성
+
+ - path: 'src/**'
+ instructions: |
+ 프로젝트 전반 리뷰 기준:
+
+ # 기본 기술 스택
+ - React + TypeScript 사용
+ - 스타일링은 Tailwind CSS v4 사용
+ - 절대 경로 임포트(@/...) 사용
+ - Client / Server 컴포넌트 구분 명확
+
+ # 네이밍 컨벤션
+ - 컴포넌트: PascalCase
+ - 폴더명: kebab-case
+ - 일반 파일명: kebab-case
+ - 변수/함수: camelCase
+ - 상수: BIG_SNAKE_CASE
+ - Props 타입: 컴포넌트명Props
+
+ # 환경 변수
+ - 환경 변수는 process.env.NEXT_PUBLIC_* 사용
+ - 민감 정보 하드코딩 금지
+
+ # 성능
+ - 불필요한 클라이언트 컴포넌트 사용 지양
+ - 리스트 렌더링 시 key 안정성 확인
+
+ # Git 규칙
+ - 커밋 메시지: #issue type: subject
+ (예: #12 feat: 로그인 기능 추가)
+ - 브랜치 패턴: type/issuenumber-issue-short-description
+ (예: feat/1-auth)
+
+ auto_review:
+ enabled: true
+ drafts: false
+ base_branches:
+ - 'main'
+ - 'develop'
+ labels:
+ - '!wip'
+ - '!draft'
+ ignore_usernames:
+ - 'dependabot'
+ - 'renovate'
+
+ tools:
+ eslint:
+ enabled: true
+ markdownlint:
+ enabled: true
+ gitleaks:
+ enabled: true
+ yamllint:
+ enabled: true
+ actionlint:
+ enabled: true
+
+chat:
+ auto_reply: true
+
+knowledge_base:
+ code_guidelines:
+ enabled: true
+ filePatterns:
+ - 'README.md'