Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

2 feature 로그인 페이지 폼 구현 및 멀티-root layout 구조 변경 #25

Merged
merged 9 commits into from
Jan 13, 2025
123 changes: 123 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,126 @@
# FlowSync

### 세계 최강의 PMS 서비스

```
KDEV3_flowSync_FE
├─ app
│ ├─ (home)
│ │ ├─ favicon.ico
│ │ ├─ layout.tsx
│ │ ├─ page.tsx
│ │ └─ projects
│ │ └─ [projectId]
│ │ ├─ page.tsx
│ │ └─ tasks
│ │ ├─ new
│ │ │ └─ page.tsx
│ │ └─ [taskId]
│ │ ├─ edit
│ │ │ └─ page.tsx
│ │ └─ page.tsx
│ └─ login
│ ├─ favicon.ico
│ ├─ find-password
│ │ └─ page.tsx
│ ├─ layout.tsx
│ └─ page.tsx
├─ package.json
├─ src
│ ├─ api
│ │ ├─ auth.ts
│ │ ├─ axiosInstance.ts
│ │ └─ projects.ts
│ ├─ components
│ │ ├─ common
│ │ │ ├─ backButton.tsx
│ │ │ ├─ BasicTable.tsx
│ │ │ ├─ CommentBox.tsx
│ │ │ ├─ CustomBox.tsx
│ │ │ ├─ Loading.tsx
│ │ │ ├─ LoginInputForm.tsx
│ │ │ ├─ MSWComponent.tsx
│ │ │ ├─ Pagination.tsx
│ │ │ ├─ Profile.tsx
│ │ │ ├─ ProjectsStatusCard.tsx
│ │ │ ├─ ProjectsStatusCards.tsx
│ │ │ ├─ SearchSection.tsx
│ │ │ ├─ SelectBox.tsx
│ │ │ ├─ SidebarTab.tsx
│ │ │ ├─ TaskComments.tsx
│ │ │ ├─ TaskContents.tsx
│ │ │ └─ TaskForm.tsx
│ │ ├─ layouts
│ │ │ ├─ Drawer.tsx
│ │ │ ├─ Header.tsx
│ │ │ └─ Sidebar.tsx
│ │ ├─ pages
│ │ │ ├─ FindPasswordPage
│ │ │ │ └─ index.tsx
│ │ │ ├─ LoginPage
│ │ │ │ └─ index.tsx
│ │ │ ├─ LogoutPage
│ │ │ │ └─ index.tsx
│ │ │ ├─ ProjectApprovalPage
│ │ │ │ └─ index.tsx
│ │ │ ├─ ProjectApprovalsPage
│ │ │ │ └─ index.tsx
│ │ │ ├─ ProjectPage
│ │ │ │ └─ index.tsx
│ │ │ ├─ ProjectsPage
│ │ │ │ └─ index.tsx
│ │ │ ├─ ProjectTaskPage
│ │ │ │ └─ index.tsx
│ │ │ └─ ProjectTasksPage
│ │ │ └─ index.tsx
│ │ └─ ui
│ │ ├─ avatar.tsx
│ │ ├─ button.tsx
│ │ ├─ checkbox.tsx
│ │ ├─ close-button.tsx
│ │ ├─ color-mode.tsx
│ │ ├─ dialog.tsx
│ │ ├─ drawer.tsx
│ │ ├─ field.tsx
│ │ ├─ input-group.tsx
│ │ ├─ link-button.tsx
│ │ ├─ native-select.tsx
│ │ ├─ pagination.tsx
│ │ ├─ popover.tsx
│ │ ├─ provider.tsx
│ │ ├─ radio.tsx
│ │ ├─ segmented-control.tsx
│ │ ├─ select.tsx
│ │ ├─ slider.tsx
│ │ └─ tooltip.tsx
│ ├─ context
│ │ ├─ PermissionsContext.tsx
│ │ ├─ ProjectsFilterContext.tsx
│ │ └─ SidebarContext.tsx
│ ├─ data
│ │ ├─ members_mock_data.json
│ │ ├─ new_task_data.ts
│ │ ├─ projects_mock_data.json
│ │ ├─ task_comments_data.ts
│ │ ├─ task_data.ts
│ │ ├─ users_mock_data.json
│ │ ├─ 목데이터이미지1.jpeg
│ │ └─ 목데이터이미지2.jpeg
│ ├─ mocks
│ │ ├─ browser.ts
│ │ ├─ handlers.ts
│ │ ├─ index.ts
│ │ └─ server.ts
│ ├─ types
│ │ ├─ api.ts
│ │ ├─ index.ts
│ │ ├─ loginActions.ts
│ │ ├─ loginForm.ts
│ │ ├─ pagination.ts
│ │ ├─ profile.ts
│ │ ├─ project.ts
│ │ └─ search.ts
│ └─ utils
│ ├─ getTranslatedStatus.ts
│ └─ styledReset.ts
```
File renamed without changes.
34 changes: 34 additions & 0 deletions app/(home)/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import type { Metadata } from "next";

import { Provider } from "@/src/components/ui/provider";
import { Box, Container, Flex } from "@chakra-ui/react";
import Header from "@/src/components/layouts/Header";
import Sidebar from "@/src/components/layouts/Sidebar";
import { MSWComponent } from "@/src/components/common/MSWComponent";
import { SidebarProvider } from "@/src/context/SidebarContext";

export const metadata: Metadata = {
title: "FlowSync",
description: "The World Best PMS Service",
};

const RootLayout = (props: { children: React.ReactNode }) => {
const { children } = props;
return (
<Provider>
<Header />
<Container maxWidth={"100%"} display="flex" flexDirection="row" margin={0} padding={0}>
<SidebarProvider>
<Sidebar />
</SidebarProvider>
<Flex width="100%" height="100%" align="center" justify="center">
<Box maxW="container.xl" width="100%" height="100%" p={10}>
{children}
</Box>
</Flex>
</Container>
</Provider>
);
};

export default RootLayout;
File renamed without changes.
3 changes: 3 additions & 0 deletions app/(home)/projects/[projectId]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import ProjectPage from "@/src/components/pages/ProjectPage";

export default ProjectPage;
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
"use client";

import { Box, Text } from "@chakra-ui/react";
import { mockData } from "../../../../../src/data/task_data";
import { mockComments } from "../../../../../src/data/task_comments_data";
import { mockData } from "@/src/data/task_data";
import { mockComments } from "@/src/data/task_comments_data";
import TaskContent from "@/src/components/common/TaskContents";
import TaskComments from "@/src/components/common/TaskComments";
import CommentBox from "@/src/components/common/CommentBox";
Expand All @@ -12,14 +12,12 @@ import BackButton from "@/src/components/common/backButton";
export default function ProjectTaskPage() {
const taskId = useParams().taskId as string;

const task = Object.values(mockData).find(
(task) => task.id === Number(taskId)
);
const task = Object.values(mockData).find(task => task.id === Number(taskId));

const comments =
mockComments.data
.find((item) => item.taskId === Number(taskId))
?.comments.map((comment) => ({
.find(item => item.taskId === Number(taskId))
?.comments.map(comment => ({
...comment,
id: comment.id.toString(),
})) || []; // 댓글이 없으면 빈 배열
Expand All @@ -29,16 +27,7 @@ export default function ProjectTaskPage() {
}

return (
<Box
maxW="1000px"
w={"100%"}
mx="auto"
mt={10}
p={6}
borderWidth={1}
borderRadius="lg"
boxShadow="md"
>
<Box maxW="1000px" w={"100%"} mx="auto" mt={10} p={6} borderWidth={1} borderRadius="lg" boxShadow="md">
<BackButton />
{/* 글 컴포넌트 */}
<TaskContent task={task} />
Expand Down
22 changes: 1 addition & 21 deletions app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,27 +17,7 @@ const RootLayout = (props: { children: React.ReactNode }) => {
const useMsw = process.env.USE_MSW === "true";
return (
<html suppressHydrationWarning>
<body>
<Provider>
<Header />
<Container
maxWidth={"100%"}
display="flex"
flexDirection="row"
margin={0}
padding={0}
>
<SidebarProvider>
<Sidebar />
</SidebarProvider>
<Flex width="100%" height="100%" align="center" justify="center">
<Box maxW="container.xl" width="100%" height="100%" p={10}>
{useMsw ? <MSWComponent>{children}</MSWComponent> : children}
</Box>
</Flex>
</Container>
</Provider>
</body>
<body>{useMsw ? <MSWComponent>{children}</MSWComponent> : children}</body>
</html>
);
};
Expand Down
Binary file added app/login/favicon.ico
Binary file not shown.
4 changes: 2 additions & 2 deletions app/login/find-password/page.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
import findPasswordPage from "@/src/components/pages/FindPasswordPage";
import FindPasswordPage from "@/src/components/pages/FindPasswordPage";

export default findPasswordPage;
export default FindPasswordPage;
31 changes: 31 additions & 0 deletions app/login/layout.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import type { Metadata } from "next";
import { Provider } from "@/src/components/ui/provider";
import { Box, Flex } from "@chakra-ui/react";

export const metadata: Metadata = {
title: "FlowSync",
description: "The World Best PMS Service",
};

const RootLayout = ({ children }: { children: React.ReactNode }) => {
return (
<Provider>
{/* 로그인 페이지 중앙 정렬 레이아웃 */}
<Flex
direction="column"
align="center"
justify="center"
minHeight="100vh"
backgroundColor="gray.50"
padding="3"
overflow="hidden" // 스크롤 제거
>
<Box width="100%" maxW="500px" borderRadius="md" bg="white" boxShadow="lg" padding="1">
{children}
</Box>
</Flex>
</Provider>
);
};

export default RootLayout;
3 changes: 0 additions & 3 deletions app/projects/[projectId]/tasks/page.tsx

This file was deleted.

1 change: 0 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 6 additions & 6 deletions src/api/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,14 @@ export const fetchUserPermissions = async (): Promise<PermissionsResponse> => {
return response.data;
};

// 로그인 API 호출
export const login = async (email: string, password: string): Promise<void> => {
const response = await axiosInstance.post("/auth/login", { email, password });
console.log(response.data.message); // 성공 메시지 출력
// 로그인 API 호출 ()
export const login = async (email: string, password: string): Promise<{ token: string; user: { id: string; name: string } }> => {
const response = await axiosInstance.post("/login", { email, password });
return response.data; // { token, user }
};

// 로그아웃 API 호출
export const logout = async (): Promise<void> => {
await axiosInstance.post("/auth/logout");
await axiosInstance.post("/logout");
console.log("로그아웃 성공!");
};
};
10 changes: 5 additions & 5 deletions src/api/axiosInstance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@ const axiosInstance = axios.create({
timeout: 10000, // 요청 타임아웃 설정 (10초)
headers: {
"Content-Type": "application/json",
}
},
// withCredentials: true,
});

// 응답 인터셉터 추가
axiosInstance.interceptors.response.use(
(response) => response,
(error) => {

response => response,
error => {
// 개발 단계에서 에러를 중앙관리하기위한 구문
// 추후 중앙화된 로깅 도구를 사용하거나 제거
// 에러 처리는 개별 컴포넌트에서 진행
Expand All @@ -27,4 +27,4 @@ axiosInstance.interceptors.response.use(
}
);

export default axiosInstance;
export default axiosInstance;
41 changes: 41 additions & 0 deletions src/components/common/LoginInputForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { LoginFormData } from "@/src/types/loginForm";

export default function LoginInputForm({ label, id, type, placeholder, onChange, className = "" }: LoginFormData & { className?: string; onChange?: (e: React.ChangeEvent<HTMLInputElement>) => void }) {
return (
<div className={`inputField ${className}`} style={{ width: "100%", marginBottom: "16px" }}>
<label
htmlFor={id}
className="label"
style={{
display: "block",
marginBottom: "8px",
fontSize: "14px",
fontWeight: "bold",
color: "#4A5568",
textAlign: "left",
}}
>
{label}
<span style={{ color: "red", marginLeft: "4px" }}>*</span>
</label>
<input
id={id}
type={type}
placeholder={placeholder}
className="input"
onChange={onChange}
style={{
width: "100%",
padding: "10px",
border: "1px solid #CBD5E0",
borderRadius: "4px",
fontSize: "14px",
outline: "none",
transition: "border-color 0.2s ease",
}}
onFocus={e => (e.target.style.borderColor = "#00a8ff")}
onBlur={e => (e.target.style.borderColor = "#CBD5E0")}
/>
</div>
);
}
4 changes: 2 additions & 2 deletions src/components/pages/FindPasswordPage/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
function findPasswordPage() {
function FindPasswordPage() {
return <h1>비밀번호 찾기</h1>;
}

export default findPasswordPage;
export default FindPasswordPage;
Loading