Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 38 additions & 5 deletions src/apis/dashboards/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,16 @@
import axiosHelper from '@/utils/network/axiosHelper';
import { BasePaginationParams, Dashboard, DashboardFormType, DashboardInvitation, DashboardInvitationResponse, DashboardsResponse, GetDashboardsParams, InviteDashboardType } from './types';
import {
BasePaginationParams,
Dashboard,
DashboardFormType,
DashboardInvitation,
DashboardInvitationResponse,
dashboardSchema,
DashboardsResponse,
dashboardsResponseSchema,
GetDashboardsParams,
InviteDashboardType,
} from './types';

// dashboard 목록 조회
export const getDashboards = async ({ cursorId, page, size, navigationMethod }: GetDashboardsParams) => {
Expand All @@ -11,25 +22,45 @@ export const getDashboards = async ({ cursorId, page, size, navigationMethod }:
navigationMethod,
},
});
return response.data;

const result = dashboardsResponseSchema.safeParse(response.data);
if (!result.success) {
throw new Error('서버에서 받은 데이터가 예상과 다릅니다.');
}
return result.data;
};

// dashboard 생성
export const createDashboard = async (data: DashboardFormType) => {
const response = await axiosHelper.post<Dashboard>('/dashboards', data);
return response.data;

const result = dashboardSchema.safeParse(response.data);
if (!result.success) {
throw new Error('서버에서 받은 데이터가 예상과 다릅니다.');
}
return result.data;
};

// dashboard 상세 조회
export const getDashboardDetails = async (id: number) => {
const response = await axiosHelper.get<Dashboard>(`/dashboards/${id}`);
return response.data;

const result = dashboardSchema.safeParse(response.data);
if (!result.success) {
throw new Error('서버에서 받은 데이터가 예상과 다릅니다.');
}
return result.data;
};

// dashboard 수정
export const updateDashboard = async (id: number, data: DashboardFormType) => {
const response = await axiosHelper.put<Dashboard>(`/dashboards/${id}`, data);
return response.data;

const result = dashboardSchema.safeParse(response.data);
if (!result.success) {
throw new Error('서버에서 받은 데이터가 예상과 다릅니다.');
}
return result.data;
};

// dashboard 삭제
Expand All @@ -38,6 +69,7 @@ export const deleteDashboard = async (id: number) => {
return response.data;
};

// TODO : UserSchema 추가이후, Invitation schema가 작성되면 응답 검증 로직 추가 필요
// dashboard 초대 불러오기
export const getDashboardInvitations = async (id: number, { page, size }: BasePaginationParams) => {
const response = await axiosHelper.get<DashboardInvitation>(`/dashboards/${id}/invitations`, {
Expand All @@ -49,6 +81,7 @@ export const getDashboardInvitations = async (id: number, { page, size }: BasePa
return response.data;
};

// TODO : UserSchema 추가이후, Invitation schema가 작성되면 응답 검증 로직 추가 필요
// dashboard 초대
export const inviteDashboard = async (id: number, data: InviteDashboardType) => {
const response = await axiosHelper.post<DashboardInvitationResponse>(`/dashboards/${id}/invitations`, data);
Expand Down
42 changes: 42 additions & 0 deletions src/apis/dashboards/queries.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
'use client';

import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import { createDashboard, getDashboards } from '.';
import { DashboardFormType } from './types';

export const useDashboardsQuery = (page: number, size: number) => {
return useQuery({
queryKey: ['dashboards', page, size],
queryFn: () =>
getDashboards({
page,
size,
navigationMethod: 'pagination',
}),
});
};

export const useDashboardMutation = () => {
const queryClient = useQueryClient();

const create = useMutation({
mutationFn: (data: DashboardFormType) => {
return createDashboard(data);
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['dashboards'] });
},
});

// TODO : update query 작성
const update = () => {};

// TODO : remove query 작성
const remove = () => {};

return {
create: create.mutateAsync,
update,
remove,
};
};
59 changes: 34 additions & 25 deletions src/apis/dashboards/types.ts
Original file line number Diff line number Diff line change
@@ -1,44 +1,53 @@
import { z } from 'zod';
import { User } from '../users/types';
import { DASHBOARD_FORM_ERROR_MESSAGE, DASHBOARD_FORM_VALID_LENGTH } from '@/constants/dashboard';
import { DEFAULT_COLORS } from '@/constants/colors';

// base pagination params 타입 (필요시 공용으로 추출)
export type BasePaginationParams = {
page?: number;
size?: number;
};
export const basePaginationParamsSchema = z.object({
page: z.number().optional(),
size: z.number().optional(),
});
export type BasePaginationParams = z.infer<typeof basePaginationParamsSchema>;

// dashboard 항목 타입
export type Dashboard = {
id: number;
title: string;
color: string;
createdAt: string;
updatedAt: string;
createdByMe: boolean;
userId: number;
};
export const dashboardSchema = z.object({
id: z.number(),
title: z.string(),
color: z.enum(DEFAULT_COLORS).catch(DEFAULT_COLORS[0]),
createdAt: z.string(),
updatedAt: z.string(),
createdByMe: z.boolean(),
userId: z.number(),
});
export type Dashboard = z.infer<typeof dashboardSchema>;

// dashboard 리스트 응답 타입
export type DashboardsResponse = {
cursorId: number;
totalCount: number;
dashboards: Dashboard[];
};
export const dashboardsResponseSchema = z.object({
cursorId: z.number().nullable(),
totalCount: z.number(),
dashboards: z.array(dashboardSchema),
});
export type DashboardsResponse = z.infer<typeof dashboardsResponseSchema>;

// dashboard get params 타입
export type NavigationMethod = 'infiniteScroll' | 'pagination';
export type GetDashboardsParams = BasePaginationParams & {
cursorId?: number;
navigationMethod: NavigationMethod;
};
export const getDashboardsParamsSchema = basePaginationParamsSchema.extend({
cursorId: z.number().optional(),
navigationMethod: z.enum(['infiniteScroll', 'pagination']),
});
export type GetDashboardsParams = z.infer<typeof getDashboardsParamsSchema>;

// dashboard 작성 스키마
export const dashboardFormSchema = z.object({
title: z.string(),
title: z
.string()
.min(DASHBOARD_FORM_VALID_LENGTH.TITLE.MIN, { message: DASHBOARD_FORM_ERROR_MESSAGE.TITLE.MIN })
.max(DASHBOARD_FORM_VALID_LENGTH.TITLE.MAX, { message: DASHBOARD_FORM_ERROR_MESSAGE.TITLE.MAX }),
color: z.string(),
});
export type DashboardFormType = z.infer<typeof dashboardFormSchema>;

// TODO : UserSchema가 추가로 작성되면 zod schema로 변경
// invitation 타입
export type InvitationUser = Pick<User, 'id' | 'email' | 'nickname'>;
export type InvitationDashboard = Pick<Dashboard, 'id' | 'title'>;
Expand All @@ -61,6 +70,6 @@ export type DashboardInvitationResponse = {

// invitation 스키마
export const inviteDashboardFormSchema = z.object({
email: z.string(),
email: z.string().email({ message: DASHBOARD_FORM_ERROR_MESSAGE.EMAIL.INVALID }),
});
export type InviteDashboardType = z.infer<typeof inviteDashboardFormSchema>;
4 changes: 4 additions & 0 deletions src/app/(dashboard)/dashboard/[id]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
export default async function DashboardDetailPage({ params }: { params: Promise<{ id: string }> }) {
const id = (await params).id;
return <div className='p-10'>아이디 {id} : 대시보드 상세페이지</div>;
}
2 changes: 1 addition & 1 deletion src/app/(dashboard)/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { PropsWithChildren } from 'react';
import Header from '@/components/dashboard-header/Header';
import Sidebar from '@/components/Sidebar/Sidebar';
import Sidebar from '@/components/dashboard/Sidebar';

export default function layout({ children }: PropsWithChildren) {
return (
Expand Down
19 changes: 9 additions & 10 deletions src/app/(dashboard)/mydashboard/page.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
'use client';

import MyDashboard from '@/components/dashboard/MyDashboard';
import InvitedDashboardCard from '@/components/invited-dashboard/InvitedDashboardCard';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';

const queryClient = new QueryClient();

export default function page() {
export default function MydashboardPage() {
return (
<div>
<QueryClientProvider client={queryClient}>
<InvitedDashboardCard />
</QueryClientProvider>
<div className='p-10'>
<div className='w-full max-w-screen-lg'>
<div className='grid gap-10'>
<MyDashboard />
<InvitedDashboardCard />
</div>
</div>
</div>
);
}
7 changes: 5 additions & 2 deletions src/app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import localFont from 'next/font/local';
import { Montserrat } from 'next/font/google';
import { DialogContainer } from '@/components/ui/Modal/DialogContainer';
import QueryClientProvider from '@/components/provider/QueryProvider';
import './globals.css';

const pretendard = localFont({
Expand All @@ -24,8 +25,10 @@ export default function RootLayout({
return (
<html lang='ko'>
<body className={`${pretendard.className} ${montserrat.variable}`}>
{children}
<DialogContainer />
<QueryClientProvider>
{children}
<DialogContainer />
</QueryClientProvider>
</body>
</html>
);
Expand Down
32 changes: 0 additions & 32 deletions src/components/Sidebar/Dot.tsx

This file was deleted.

54 changes: 0 additions & 54 deletions src/components/Sidebar/Sidebar.tsx

This file was deleted.

26 changes: 0 additions & 26 deletions src/components/Sidebar/SidebarItemList.tsx

This file was deleted.

Loading