Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
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
11 changes: 8 additions & 3 deletions src/Router.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ import { lazy } from "react";

import { createBrowserRouter, RouteObject } from "react-router-dom";

import ShopInfoPostCardSkeleton from "./pages/NoticeEmployeePage/components/ShopInfoPostCardSkeleton";
import noticeEmployeeLoader from "./pages/NoticeEmployeePage/loader/noticeEmployeeLoader";
import noticeEmployerLoader from "./pages/NoticeEmployerPage/loader/noticeEmployerLoader";
import profileLoader from "./pages/ProfilePage/loader/profileLoader";

import NoticeDetailSkeleton from "./components/NoticeDetailSkeleton";
import { ROUTES } from "./constants/router";
import AuthLayout from "./layouts/AuthLayout";
import MainLayout from "./layouts/MainLayout";
Expand All @@ -24,7 +25,9 @@ const ShopEditPage = lazy(() => import("@/pages/ShopEditPage"));
const NoticeListPage = lazy(() => import("@/pages/NoticeListPage"));
const NoticeRegisterPage = lazy(() => import("@/pages/NoticeRegisterPage"));
const NoticeEditPage = lazy(() => import("@/pages/NoticeEditPage"));
const NoticeEmployerPage = lazy(() => import("@/pages/NoticeEmployerPage"));
const NoticeEmployerPage = lazy(
() => import("@/pages/NoticeEmployerPage/NoticeEmployerPage"),
);
const NoticeEmployeePage = lazy(
() => import("@/pages/NoticeEmployeePage/NoticeEmployeePage"),
);
Expand Down Expand Up @@ -88,12 +91,14 @@ const noticeRoutes: RouteObject[] = [
{
path: ROUTES.NOTICE.NOTICE_ID.EMPLOYER,
Component: NoticeEmployerPage,
loader: noticeEmployerLoader,
hydrateFallbackElement: <NoticeDetailSkeleton />,
},
{
path: ROUTES.NOTICE.NOTICE_ID.EMPLOYEE,
Component: NoticeEmployeePage,
loader: noticeEmployeeLoader,
hydrateFallbackElement: <ShopInfoPostCardSkeleton />,
hydrateFallbackElement: <NoticeDetailSkeleton />,
},
];

Expand Down
56 changes: 56 additions & 0 deletions src/apis/loaders/notice.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { getShopApplications } from "../services/applicationService";
import { getNotice } from "../services/noticeService";

import { PostData } from "@/components/Post/PostList";
import { getLocalStorageValue } from "@/utils/localStorage";

interface LoadNoticeParams {
shopId: string;
noticeId: string;
}

export const loadNotice = async ({ shopId, noticeId }: LoadNoticeParams) => {
const noticeResult = await getNotice(shopId, noticeId);

if (noticeResult.status === 200) {
return noticeResult.data.item;
}
};

const MAX_VISIBLE_RECENT_NOTICES = 6;

export const loadRecentNotices = async (noticeId: string) => {
const allRecentNotices =
getLocalStorageValue<PostData[]>("recentNotices") ?? [];

const recentNotices = allRecentNotices
.filter(({ id }) => id !== noticeId)
.slice(0, MAX_VISIBLE_RECENT_NOTICES);

return recentNotices;
};

interface LoadNoticeApplicationsParams {
shopId: string;
noticeId: string;
offset?: number;
limit?: number;
}

export const loadNoticeApplications = async ({
shopId,
noticeId,
offset,
limit,
}: LoadNoticeApplicationsParams) => {
const noticeApplicationsResult = await getShopApplications(
shopId,
noticeId,
offset,
limit,
);

if (noticeApplicationsResult.status === 200) {
return noticeApplicationsResult.data;
}
};
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import { useNavigate, useRevalidator } from "react-router-dom";

import {
postApplication,
putApplication,
} from "@/apis/services/applicationService";
import Button from "@/components/Button";
import PostCard from "@/components/Post/PostCard";
import { useToast } from "@/hooks/useToast";
import { User } from "@/hooks/useUserStore";
import { useModalStore } from "@/store/useModalStore";
import { NoticeItem } from "@/types/notice";
import { cn } from "@/utils/cn";
Expand All @@ -14,13 +17,19 @@ interface NoticeDetailInfoProps {
noticeInfo: NoticeItem;
shopId: string;
noticeId: string;
user?: User | null;
isEmployerPage?: boolean;
}

function NoticeDetailInfo({
shopId,
noticeId,
noticeInfo,
user,
isEmployerPage,
}: NoticeDetailInfoProps) {
const navigate = useNavigate();
const { revalidate } = useRevalidator();
const { openModal } = useModalStore();
const { showToast } = useToast();

Expand All @@ -41,15 +50,20 @@ function NoticeDetailInfo({
description: shopDescription,
} = noticeInfo.shop!.item;

const applicationId = currentUserApplication?.item.id;
const applicationId = currentUserApplication?.item.id ?? "";
const applicationStatus = currentUserApplication?.item.status;
const isPast = isPastDate(startsAt, workhour);
const isDisabledNotice = isPast || closed || applicationStatus === "canceled";
const isDisabledNotice =
isPast ||
closed ||
applicationStatus === "canceled" ||
applicationStatus === "rejected";

const applyNotice = async () => {
const result = await postApplication(shopId, noticeId);

if (result.status === 201) {
revalidate();
showToast("신청 완료!");
}
};
Expand All @@ -65,17 +79,24 @@ function NoticeDetailInfo({
const result = await putApplication(
shopId,
noticeId,
applicationId ?? "",
applicationId,
"canceled",
);

if (result.status === 200) {
showToast("취소가 완료 되었습니다.");
revalidate();
}
},
});
};

const moveToEditNoticePage = () => {
if (user) {
navigate(`/notice/edit/${user.shopId}`);
}
};

return (
<>
<div className="flex flex-col gap-2 mb-1 md:mb-0">
Expand All @@ -96,27 +117,39 @@ function NoticeDetailInfo({
workhour={workhour}
closed={closed}
buttons={
<Button
disabled={isDisabledNotice}
className={cn("py-[14px]", {
"cursor-pointer": !isDisabledNotice,
})}
fullWidth
variant={applicationStatus === "pending" ? "white" : "primary"}
onClick={
applicationStatus === "pending"
? cancelApplication
: applyNotice
}
>
{applicationStatus === "pending" && "취소하기"}
{applicationStatus === "accepted" && "승낙"}
{applicationStatus === "rejected" && "지원 거절"}
{applicationStatus === "canceled" &&
"이미 취소한 지원 공고 입니다."}
{!applicationStatus && isDisabledNotice && "신청 불가"}
{!applicationStatus && !closed && !isPast && "지원하기"}
</Button>
isEmployerPage ? (
<Button
fullWidth
variant="white"
className={"py-[14px]"}
onClick={moveToEditNoticePage}
disabled={user?.shopId !== shopId}
>
공고 편집하기
</Button>
) : (
<Button
disabled={isDisabledNotice}
className={cn("py-[14px]", {
"cursor-pointer": !isDisabledNotice,
})}
fullWidth
variant={applicationStatus === "pending" ? "white" : "primary"}
onClick={
applicationStatus === "pending"
? cancelApplication
: applyNotice
}
>
{applicationStatus === "pending" && "취소하기"}
{applicationStatus === "accepted" && "승낙"}
{applicationStatus === "rejected" && "거절된 공고입니다."}
{applicationStatus === "canceled" &&
"이미 취소하신 공고 입니다."}
{!applicationStatus && isDisabledNotice && "신청 불가"}
{!applicationStatus && !closed && !isPast && "지원하기"}
</Button>
)
}
/>
)}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Location, Time } from "@/assets/icon";

function ShopInfoPostCardSkeleton() {
function NoticeDetailSkeleton() {
return (
<section>
<div className="flex flex-col gap-3 md:gap-6 xl:w-[60.25rem] mx-auto px-3 md:px-8 py-10 md:py-[3.75rem]">
Expand Down Expand Up @@ -49,4 +49,4 @@ function ShopInfoPostCardSkeleton() {
);
}

export default ShopInfoPostCardSkeleton;
export default NoticeDetailSkeleton;
2 changes: 1 addition & 1 deletion src/components/Post/PostCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ export default function PostCard({
className="w-full h-[180px] object-cover md:h-[360px] lg:h-[308px]"
/>
{isDimmed && (
<div className="absolute inset-0 flex items-center justify-center bg-black opcity-70">
<div className="absolute inset-0 flex items-center justify-center bg-black opacity-70">
<p className="text-[1.75rem] text-gray-30 font-bold">
{!closed && isPast && "지난 공고"}
{closed && !isPast && "마감 완료"}
Expand Down
2 changes: 1 addition & 1 deletion src/components/Post/PostList.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import Post from "./Post";

interface PostData {
export interface PostData {
id: string;
name: string;
imageUrl: string;
Expand Down
2 changes: 1 addition & 1 deletion src/constants/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const ROUTES = {
NOTICE: {
ROOT: "/",
REGISTER: "/notice/register",
EDIT: "/notice/edit",
EDIT: "/notice/edit/:noticeId",
NOTICE_ID: {
EMPLOYER: `/notice/:shopId/:noticeId/employer`,
EMPLOYEE: `/notice/:shopId/:noticeId/employee`,
Expand Down
59 changes: 59 additions & 0 deletions src/hooks/useUpdateRecentNotices.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { useEffect } from "react";

import { NoticeItem } from "../types";

import { PostData } from "@/components/Post/PostList";
import {
getLocalStorageValue,
setLocalStorageValue,
} from "@/utils/localStorage";

const RECENT_NOTICES = "recentNotices";

const updateLocalStorageRecentNotices = (candidateNotice: PostData) => {
const storageValue: PostData[] = getLocalStorageValue(RECENT_NOTICES) ?? [];

if (
storageValue.length > 0 &&
!storageValue.some(({ id }) => id === candidateNotice.id)
) {
if (storageValue.length === 7) {
storageValue.shift();
}
storageValue.push(candidateNotice);
}
setLocalStorageValue(RECENT_NOTICES, storageValue);
};

interface UseRecentNoticesParams {
noticeInfo: NoticeItem;
link: string;
}

const useUpdateRecentNotices = ({
noticeInfo,
link,
}: UseRecentNoticesParams) => {
useEffect(() => {
const { id, hourlyPay, startsAt, workhour, closed } = noticeInfo;
const { name, imageUrl, address1, originalHourlyPay } =
noticeInfo.shop!.item;

const visitedNotice = {
id,
name,
imageUrl,
address1,
originalHourlyPay,
link,
hourlyPay,
startsAt,
workhour,
closed,
};

updateLocalStorageRecentNotices(visitedNotice);
}, [noticeInfo, link]);
};

export default useUpdateRecentNotices;
2 changes: 1 addition & 1 deletion src/hooks/useUserStore.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { create } from "zustand";

type User = {
export type User = {
id: string;
email: string;
type: "employer" | "employee";
Expand Down
25 changes: 21 additions & 4 deletions src/pages/NoticeEmployeePage/NoticeEmployeePage.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,26 @@
import { useLoaderData, useParams } from "react-router-dom";

import NoticeDetailInfo from "./components/NoticeDetailInfo";
import NoticeDetailInfo from "../../components/NoticeDetailInfo";

import PostList from "@/components/Post/PostList";
import PostList, { PostData } from "@/components/Post/PostList";
import useUpdateRecentNotices from "@/hooks/useUpdateRecentNotices";
import { NoticeItem } from "@/types/notice";

export default function NoticeEmployeePage() {
const { noticeInfo, recentNotices } = useLoaderData();
const { noticeInfo, recentNotices } = useLoaderData<{
noticeInfo: NoticeItem;
recentNotices: PostData[];
}>();
const { shopId, noticeId } = useParams() as {
shopId: string;
noticeId: string;
};

useUpdateRecentNotices({
noticeInfo,
link: `/notice/${shopId}/${noticeId}/employee`,
});

return (
<>
<section>
Expand All @@ -26,7 +36,14 @@ export default function NoticeEmployeePage() {
<section>
<div className="flex flex-col gap-8 xl:w-[60.25rem] mx-auto mb-[3.75rem] px-3 md:px-8 py-10 md:py-[3.75rem]">
<h2 className="text-[1.625rem] font-bold">최근에 본 공고</h2>
<PostList posts={recentNotices} />

{recentNotices.length > 0 ? (
<PostList posts={recentNotices} />
) : (
<div className="flex items-center justify-center w-full h-[20rem] text-black">
최근에 본 공고가 없습니다.
</div>
)}
</div>
</section>
</>
Expand Down
Loading