Compare commits
10 Commits
38d10217f1
...
928250a291
| Author | SHA1 | Date | |
|---|---|---|---|
| 928250a291 | |||
| 81ce79957d | |||
| 9e37299966 | |||
| c7ff6f8d13 | |||
|
|
a51566ffae | ||
| ca1ebd2985 | |||
| 2f7b1d4653 | |||
| ad9226a2f1 | |||
| 4173cbef9b | |||
| 58bdea1e60 |
@@ -6,17 +6,20 @@ variables:
|
||||
GIT_CHECKOUT: "true"
|
||||
GIT_SSL_NO_VERIFY: "true"
|
||||
|
||||
cache:
|
||||
paths:
|
||||
- node_modules/
|
||||
|
||||
build:
|
||||
stage: build
|
||||
image: node:25.1.0
|
||||
tags:
|
||||
- local-runner
|
||||
script:
|
||||
- export PATH="$PATH:$NVM_HOME/versions/node/v25.1.0/bin"
|
||||
- [ -s "$NVM_HOME/nvm.sh" ] && \. "$NVM_HOME/nvm.sh"
|
||||
- node -v
|
||||
- npm install
|
||||
- npm run build
|
||||
- pwd
|
||||
- ls
|
||||
- docker cp -r $pwd/dist/* scheduler_front:/usr/share/nginx/html/
|
||||
- whoami
|
||||
- sudo cp -r $PWD/dist/. $DOCKER_VOLUME/scheduler/front/service/
|
||||
|
||||
19
package-lock.json
generated
19
package-lock.json
generated
@@ -108,6 +108,7 @@
|
||||
"integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "^7.27.1",
|
||||
"@babel/generator": "^7.28.5",
|
||||
@@ -3448,6 +3449,7 @@
|
||||
"integrity": "sha512-QoiaXANRkSXK6p0Duvt56W208du4P9Uye9hWLWgGMDTEoKPhuenzNcC4vGUmrNkiOKTlIrBoyNQYNpSwfEZXSg==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"undici-types": "~7.16.0"
|
||||
}
|
||||
@@ -3458,6 +3460,7 @@
|
||||
"integrity": "sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"csstype": "^3.0.2"
|
||||
}
|
||||
@@ -3468,6 +3471,7 @@
|
||||
"integrity": "sha512-9KQPoO6mZCi7jcIStSnlOWn2nEF3mNmyr3rIAsGnAbQKYbRLyqmeSc39EVgtxXVia+LMT8j3knZLAZAh+xLmrw==",
|
||||
"devOptional": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"peerDependencies": {
|
||||
"@types/react": "^19.2.0"
|
||||
}
|
||||
@@ -3518,6 +3522,7 @@
|
||||
"integrity": "sha512-BnOroVl1SgrPLywqxyqdJ4l3S2MsKVLDVxZvjI1Eoe8ev2r3kGDo+PcMihNmDE+6/KjkTubSJnmqGZZjQSBq/g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@typescript-eslint/scope-manager": "8.46.2",
|
||||
"@typescript-eslint/types": "8.46.2",
|
||||
@@ -3770,6 +3775,7 @@
|
||||
"integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"acorn": "bin/acorn"
|
||||
},
|
||||
@@ -3937,6 +3943,7 @@
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"baseline-browser-mapping": "^2.8.19",
|
||||
"caniuse-lite": "^1.0.30001751",
|
||||
@@ -4307,7 +4314,8 @@
|
||||
"version": "8.6.0",
|
||||
"resolved": "https://registry.npmjs.org/embla-carousel/-/embla-carousel-8.6.0.tgz",
|
||||
"integrity": "sha512-SjWyZBHJPbqxHOzckOfo8lHisEaJWmwd23XppYFYVh10bU66/Pn5tkVkbkCMZVdbUE5eTCI2nD8OyIP4Z+uwkA==",
|
||||
"license": "MIT"
|
||||
"license": "MIT",
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/embla-carousel-react": {
|
||||
"version": "8.6.0",
|
||||
@@ -4414,6 +4422,7 @@
|
||||
"integrity": "sha512-t5aPOpmtJcZcz5UJyY2GbvpDlsK5E8JqRqoKtfiKE3cNh437KIqfJr3A3AKf5k64NPx6d0G3dno6XDY05PqPtw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@eslint-community/eslint-utils": "^4.8.0",
|
||||
"@eslint-community/regexpp": "^4.12.1",
|
||||
@@ -5578,6 +5587,7 @@
|
||||
}
|
||||
],
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"nanoid": "^3.3.11",
|
||||
"picocolors": "^1.1.1",
|
||||
@@ -5657,6 +5667,7 @@
|
||||
"resolved": "https://registry.npmjs.org/react/-/react-19.2.0.tgz",
|
||||
"integrity": "sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
@@ -5687,6 +5698,7 @@
|
||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.0.tgz",
|
||||
"integrity": "sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"scheduler": "^0.27.0"
|
||||
},
|
||||
@@ -5699,6 +5711,7 @@
|
||||
"resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.66.0.tgz",
|
||||
"integrity": "sha512-xXBqsWGKrY46ZqaHDo+ZUYiMUgi8suYu5kdrS20EG8KiL7VRQitEbNjm+UcrDYrNi1YLyfpmAeGjCZYXLT9YBw==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=18.0.0"
|
||||
},
|
||||
@@ -6155,6 +6168,7 @@
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
|
||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
@@ -6222,6 +6236,7 @@
|
||||
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
|
||||
"dev": true,
|
||||
"license": "Apache-2.0",
|
||||
"peer": true,
|
||||
"bin": {
|
||||
"tsc": "bin/tsc",
|
||||
"tsserver": "bin/tsserver"
|
||||
@@ -6394,6 +6409,7 @@
|
||||
"resolved": "https://registry.npmjs.org/vite/-/vite-7.1.12.tgz",
|
||||
"integrity": "sha512-ZWyE8YXEXqJrrSLvYgrRP7p62OziLW7xI5HYGWFzOvupfAlrLvURSzv/FyGyy0eidogEM3ujU+kUG1zuHgb6Ug==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"esbuild": "^0.25.0",
|
||||
"fdir": "^6.5.0",
|
||||
@@ -6485,6 +6501,7 @@
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
|
||||
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
|
||||
@@ -7,6 +7,7 @@ import Layout from './layouts/Layout';
|
||||
import { BrowserRouter as Router, Routes, Route, Navigate } from 'react-router-dom';
|
||||
import { useAuthStore } from './store/authStore';
|
||||
import { PageRouting } from './data/RoutingData';
|
||||
import LoginPage from './ui/page/login/LoginPage';
|
||||
|
||||
function App() {
|
||||
const { authData } = useAuthStore();
|
||||
@@ -15,8 +16,9 @@ function App() {
|
||||
<Router>
|
||||
<Routes>
|
||||
<Route element={<Layout />}>
|
||||
<Route element={<LoginPage />} path={PageRouting["LOGIN"].path} />
|
||||
<Route element={<SignUpPage />} path={PageRouting["SIGN_UP"].path} />
|
||||
{!(authData?.isLogedIn) ? <Route element={<Navigate to={`/${PageRouting["SIGN_UP"].path}`} />} path="*" /> : null}
|
||||
{!(authData?.isLogedIn) ? <Route element={<Navigate to={PageRouting["LOGIN"].path} />} path="*" /> : null}
|
||||
</Route>
|
||||
</Routes>
|
||||
</Router>
|
||||
|
||||
@@ -4,17 +4,17 @@ type PageRoutingInfo = {
|
||||
}
|
||||
|
||||
export const PageRouting: Record<string, PageRoutingInfo> = {
|
||||
LOGIN: { path: "login", title: "" },
|
||||
SIGN_UP: { path: "signup", title: "" },
|
||||
RESET_PASSWORD: { path: "reset-password", title: "" },
|
||||
HOME: { path: "home", title: "홈" },
|
||||
SCHEDULES: { path: "schedules", title: "일정" },
|
||||
SCHEDULES_NEW: { path: "schedules/new", title: "일정 생성" },
|
||||
SCHEDULES_EDIT: { path: "schedules/edit", title: "일정 수정" },
|
||||
SCHEDULES_DETAIL: { path: "schedules/detail", title: "일정 상세" },
|
||||
USER_INFO: { path: "info", title: "사용자 정보" },
|
||||
USER_FOLLOWING: { path: "info/following", title: "팔로잉 목록" },
|
||||
USER_FOLLOWER: { path: "info/follower", title: "팔로워 목록" },
|
||||
SETTINGS: { path: "settings", title: "설정" },
|
||||
NOT_FOUD: { path: "not-found", title: "존재하지 않는 페이지" },
|
||||
LOGIN: { path: "/login", title: "" },
|
||||
SIGN_UP: { path: "/signup", title: "" },
|
||||
RESET_PASSWORD: { path: "/reset-password", title: "" },
|
||||
HOME: { path: "/home", title: "홈" },
|
||||
SCHEDULES: { path: "/schedules", title: "일정" },
|
||||
SCHEDULES_NEW: { path: "/schedules/new", title: "일정 생성" },
|
||||
SCHEDULES_EDIT: { path: "/schedules/edit", title: "일정 수정" },
|
||||
SCHEDULES_DETAIL: { path: "/schedules/detail", title: "일정 상세" },
|
||||
USER_INFO: { path: "/info", title: "사용자 정보" },
|
||||
USER_FOLLOWING: { path: "/info/following", title: "팔로잉 목록" },
|
||||
USER_FOLLOWER: { path: "/info/follower", title: "팔로워 목록" },
|
||||
SETTINGS: { path: "/settings", title: "설정" },
|
||||
NOT_FOUD: { path: "/not-found", title: "존재하지 않는 페이지" },
|
||||
} as const;
|
||||
@@ -3,6 +3,7 @@ import * as z from 'zod';
|
||||
export const LoginSchema = z.object({
|
||||
email: z
|
||||
.email()
|
||||
.min(1, "이메일을 입력해주십시오.")
|
||||
, password: z
|
||||
.string()
|
||||
.min(8, "비밀번호는 8-12 자리여야 합니다.")
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
import { Card, CardContent, CardHeader, CardFooter } from '@/components/ui/card';
|
||||
import { LoginSchema } from '@/data/form';
|
||||
import { Field, FieldError, FieldGroup, FieldLabel, FieldLegend } from '@/components/ui/field';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { useState, useCallback } from 'react';
|
||||
import { Controller, useForm } from 'react-hook-form';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { PageRouting } from '@/data/RoutingData';
|
||||
import * as z from 'zod';
|
||||
|
||||
export default function LoginPage() {
|
||||
const navigate = useNavigate();
|
||||
|
||||
const loginForm = useForm<z.infer<typeof LoginSchema>>({
|
||||
resolver: zodResolver(LoginSchema),
|
||||
defaultValues: {
|
||||
email: "",
|
||||
password: ""
|
||||
}
|
||||
});
|
||||
|
||||
const moveToSignUpPage = useCallback(() => {
|
||||
navigate(PageRouting["SIGN_UP"].path);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="w-full h-full flex flex-col justify-center items-center">
|
||||
<Card className="w-md pl-2 pr-2">
|
||||
<CardHeader>
|
||||
로그인
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<form id="form-login">
|
||||
<Controller
|
||||
name="email"
|
||||
control={loginForm.control}
|
||||
render={({ field, fieldState }) => (
|
||||
<Field data-invalid={fieldState.invalid}>
|
||||
<FieldLabel htmlFor="form-login-email">이메일</FieldLabel>
|
||||
<Input
|
||||
{...field}
|
||||
type="email"
|
||||
id="form-login-email"
|
||||
aria-invalid={fieldState.invalid}
|
||||
/>
|
||||
<FieldError errors={[fieldState.error]} />
|
||||
</Field>
|
||||
)}
|
||||
>
|
||||
</Controller>
|
||||
<Controller
|
||||
name="password"
|
||||
control={loginForm.control}
|
||||
render={({ field, fieldState }) => (
|
||||
<Field data-invalid={fieldState.invalid}>
|
||||
<FieldLabel htmlFor="form-login-password">비밀번호</FieldLabel>
|
||||
<Input
|
||||
{...field}
|
||||
type="password"
|
||||
id="form-login-password"
|
||||
aria-invalid={fieldState.invalid}
|
||||
/>
|
||||
<FieldError errors={[fieldState.error]} />
|
||||
</Field>
|
||||
)}
|
||||
>
|
||||
</Controller>
|
||||
</form>
|
||||
</CardContent>
|
||||
<CardFooter
|
||||
className="w-full flex flex-col items-center justify-between gap-[5px]"
|
||||
>
|
||||
<Button
|
||||
className="w-full"
|
||||
type="submit"
|
||||
form="form-login">
|
||||
로그인
|
||||
</Button>
|
||||
<Button
|
||||
className="w-full bg-white border-2 text-black hover:bg-gray-100"
|
||||
onClick={moveToSignUpPage}
|
||||
>
|
||||
회원가입
|
||||
</Button>
|
||||
</CardFooter>
|
||||
</Card>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -2,7 +2,6 @@ import { defineConfig } from 'vite'
|
||||
import react from '@vitejs/plugin-react'
|
||||
import tailwindcss from '@tailwindcss/vite'
|
||||
import path from 'path'
|
||||
|
||||
// https://vite.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
|
||||
Reference in New Issue
Block a user