diff --git a/apps/admin/app/studies/[studyId]/layout.tsx b/apps/admin/app/studies/[studyId]/layout.tsx index ba37ffbb..05e232d2 100644 --- a/apps/admin/app/studies/[studyId]/layout.tsx +++ b/apps/admin/app/studies/[studyId]/layout.tsx @@ -14,7 +14,7 @@ const StudyLayout = ({ }; const MainLayoutStyle = { - height: "100vh", + height: "100%", overflow: "auto", }; diff --git a/apps/admin/app/studies/_components/StudyListItem.tsx b/apps/admin/app/studies/_components/StudyListItem.tsx index 5f954efd..8672bb2b 100644 --- a/apps/admin/app/studies/_components/StudyListItem.tsx +++ b/apps/admin/app/studies/_components/StudyListItem.tsx @@ -30,7 +30,9 @@ const StudyListItem = async ({ study }: { study: StudyListApiResponseDto }) => { {academicYear}-{semesterType === "FIRST" ? "1" : "2"} - {title} + + {title} + {studyType} @@ -70,8 +72,8 @@ const studyTypeColorMap: Record< ComponentProps["color"] > = { "과제 스터디": "green", - "온라인 커리큘럼": "blue", - "오프라인 커리큘럼": "yellow", + "온라인 스터디": "blue", + "오프라인 스터디": "yellow", }; const LinkStyle = { @@ -83,12 +85,21 @@ const LinkStyle = { const TableLeftStyle = { display: "flex", + flex: 2, alignItems: "center", gap: "31px", }; const TableRightStyle = { display: "flex", + flex: 3, alignItems: "center", gap: "64px", }; + +const StudyNameStyle = { + whiteSpace: "nowrap", + overflow: "hidden", + textOverflow: "ellipsis", + maxWidth: "150px", +}; diff --git a/apps/admin/app/studies/create-study/_components/StudyBasicInformation/StudyFormatSelect.tsx b/apps/admin/app/studies/create-study/_components/StudyBasicInformation/StudyFormatSelect.tsx index 35026ed6..50327ff9 100644 --- a/apps/admin/app/studies/create-study/_components/StudyBasicInformation/StudyFormatSelect.tsx +++ b/apps/admin/app/studies/create-study/_components/StudyBasicInformation/StudyFormatSelect.tsx @@ -23,7 +23,7 @@ const StudyFormatSelect = () => { value="OFFLINE" text={ - 오프라인 커리큘럼 + 오프라인 스터디 오프라인으로 진행해요. @@ -34,7 +34,7 @@ const StudyFormatSelect = () => { value="ONLINE" text={ - 온라인 커리큘럼 + 온라인 스터디 온라인으로 진행해요. diff --git a/apps/admin/constants/cookieKey.ts b/apps/admin/constants/cookieKey.ts new file mode 100644 index 00000000..6bd79aff --- /dev/null +++ b/apps/admin/constants/cookieKey.ts @@ -0,0 +1,4 @@ +export const enum cookieKey { + accessToken = "accessToken", + "admin-middleware-executed" = "admin-middleware-executed", +} diff --git a/apps/admin/constants/url.ts b/apps/admin/constants/url.ts new file mode 100644 index 00000000..6fc5cdb9 --- /dev/null +++ b/apps/admin/constants/url.ts @@ -0,0 +1,4 @@ +export const clientUrl = + process.env.NEXT_PUBLIC_VERCEL_ENV === "production" + ? process.env.NEXT_PUBLIC_CLIENT_PROD_URL + : process.env.NEXT_PUBLIC_CLIENT_DEV_URL; diff --git a/apps/admin/hooks/useForm.tsx b/apps/admin/hooks/useForm.tsx new file mode 100644 index 00000000..bba8d1ee --- /dev/null +++ b/apps/admin/hooks/useForm.tsx @@ -0,0 +1,3 @@ +const useForm = () => {}; + +export default useForm; diff --git a/apps/admin/middleware.ts b/apps/admin/middleware.ts index 5c3ac64c..f32cfe32 100644 --- a/apps/admin/middleware.ts +++ b/apps/admin/middleware.ts @@ -1,29 +1,40 @@ import { dashboardApi } from "apis/auth/dashboardApi"; +import { cookieKey } from "constants/cookieKey"; +import { clientUrl } from "constants/url"; import { cookies } from "next/headers"; -import type { NextRequest } from "next/server"; import { NextResponse } from "next/server"; - +import setExpireTime from "utils/setExpireTime"; export const config = { matcher: ["/studies/:path*", "/participants/:path*"], }; -const middleware = async (req: NextRequest) => { +const middleware = async () => { const cookieStore = cookies(); - const accessToken = cookieStore.get("accessToken")?.value; + const accessToken = cookieStore.get(cookieKey.accessToken)?.value; + const middlewareExecuted = cookieStore.get( + cookieKey["admin-middleware-executed"] + )?.value; if (!accessToken) { - return NextResponse.redirect(new URL("/not-found", req.url)); + return NextResponse.redirect(new URL("/auth", clientUrl)); } - const { studyRole, manageRole } = await dashboardApi.getDashboardInfo(); - - if (studyRole === "STUDENT" && manageRole === "NONE") { - const url = - process.env.NEXT_PUBLIC_VERCEL_ENV === "production" - ? process.env.NEXT_PUBLIC_CLIENT_PROD_URL - : process.env.NEXT_PUBLIC_CLIENT_DEV_URL; - - return NextResponse.redirect(new URL("/auth", url)); + if (!middlewareExecuted) { + try { + const { manageRole, studyRole } = await dashboardApi.getDashboardInfo(); + if (studyRole === "STUDENT" && manageRole === "NONE") { + return NextResponse.redirect(new URL("/auth", clientUrl)); + } + const response = NextResponse.next(); + response.cookies.set(cookieKey["admin-middleware-executed"], "true", { + httpOnly: true, + secure: true, + sameSite: "lax", + }); + return response; + } catch (error) { + return NextResponse.next(); + } } return NextResponse.next(); diff --git a/apps/admin/types/entities/study.ts b/apps/admin/types/entities/study.ts index 647fa996..894c2580 100644 --- a/apps/admin/types/entities/study.ts +++ b/apps/admin/types/entities/study.ts @@ -11,8 +11,8 @@ export type StudyType = "ASSIGNMENT" | "ONLINE" | "OFFLINE"; export type StudyKoreanType = | "과제 스터디" - | "온라인 커리큘럼" - | "오프라인 커리큘럼"; + | "온라인 스터디" + | "오프라인 스터디"; export type StudyCurriculumType = { studyDetailId: number; diff --git a/apps/admin/utils/setExpireTime.ts b/apps/admin/utils/setExpireTime.ts new file mode 100644 index 00000000..b48dd9c4 --- /dev/null +++ b/apps/admin/utils/setExpireTime.ts @@ -0,0 +1,8 @@ +const setExpireTime = (hour: number) => { + const expires = new Date(); + expires.setTime(expires.getTime() + hour * 60 * 60 * 1000); + + return expires; +}; + +export default setExpireTime; diff --git a/packages/ui/src/components/NavItem/index.tsx b/packages/ui/src/components/NavItem/index.tsx index fea1e725..a417b4b7 100644 --- a/packages/ui/src/components/NavItem/index.tsx +++ b/packages/ui/src/components/NavItem/index.tsx @@ -55,8 +55,7 @@ const NavItem = ({ href, imageUrl, alt, name, items }: NavItemProps) => { href={`${href}`} tabIndex={0} className={navItemStyle({ - type: - !segment[1] && `/${segment[0]}` === href ? "active" : "inactive", + type: !segment[1] && `${segment[0]}` === href ? "active" : "inactive", })} onClick={handleClickNavItem} >