diff --git a/src/apis/bookmarks/type.ts b/src/apis/bookmarks/type.ts index 3841856..899891e 100644 --- a/src/apis/bookmarks/type.ts +++ b/src/apis/bookmarks/type.ts @@ -3,7 +3,8 @@ export interface BookmarkResponseType { } export interface BookmarkItemsType { - company_name: string; - recruitment_id: number; - created_at: string; -} + "company_logo_url": string; + "company_name": string; + "recruitment_id": number; + "created_at": string; +} \ No newline at end of file diff --git a/src/apis/companies/index.ts b/src/apis/companies/index.ts index 293c0d1..d5c1140 100644 --- a/src/apis/companies/index.ts +++ b/src/apis/companies/index.ts @@ -2,7 +2,7 @@ import { useQuery } from "@tanstack/react-query"; import { useToastStore } from "@team-return/design-system"; import { instance } from "../axios"; import { GetNumberOfPagesType } from "../recruitments/type"; -import { CompaniesDetailsType, CompaniesListResponseType } from "./type"; +import { CompaniesDetailsType, CompaniesListResponseType, GetCompaniesForReviewingResponse } from "./type"; const router = "/companies"; @@ -56,3 +56,13 @@ export const useGetNumberOfCompaniesListPages = (queryString: string) => { ); return data; }; + +export const useGetCompaniesForReviewing = () => { + return useQuery( + ["getCompaniesForReviewing"], + async () => { + const {data} = await instance.get(`${router}/review`); + return data; + } + ) +} \ No newline at end of file diff --git a/src/apis/companies/type.ts b/src/apis/companies/type.ts index c51775b..e704c65 100644 --- a/src/apis/companies/type.ts +++ b/src/apis/companies/type.ts @@ -39,4 +39,13 @@ export interface CompaniesDetailsTable { attachments: any[]; service_name: string; business_area: string; -} \ No newline at end of file +} + +export interface CompaniesForReviewType { + id: number, + name: string, +} + +export interface GetCompaniesForReviewingResponse { + companies : CompaniesForReviewType[] +} diff --git a/src/apis/recruitments/type.ts b/src/apis/recruitments/type.ts index 20d8cfa..78858e6 100644 --- a/src/apis/recruitments/type.ts +++ b/src/apis/recruitments/type.ts @@ -18,6 +18,8 @@ export interface RecruitmentsDetailType extends RecruitmentsDetailTable { company_id: number; company_profile_url: string; company_name: string; + bookmarked: boolean; + recruitment_id: number; } export interface RecruitmentsDetailTable { diff --git a/src/apis/reviews/index.ts b/src/apis/reviews/index.ts index 6635fdd..60ea24d 100644 --- a/src/apis/reviews/index.ts +++ b/src/apis/reviews/index.ts @@ -1,8 +1,11 @@ -import { useQuery } from "@tanstack/react-query"; +import { MutationOptions, useMutation, UseMutationOptions, useQuery } from "@tanstack/react-query"; +import { useToastStore } from "@team-return/design-system"; +import { AxiosError, AxiosResponse } from "axios"; import { instance } from "../axios"; import { getReviewDetailResponseProps, - getReviewListResponseProps + getReviewListResponseProps, + createReviewRequestType, } from "./type"; const router = "/reviews"; @@ -24,3 +27,47 @@ export const useGetReviewDetails = (reviewId: string) => { return data; }); }; + +export const useCreateReviews = (options: Omit, unknown, createReviewRequestType, unknown>, "mutationFn">) => { + const { append } = useToastStore(); + return useMutation( + async (body: createReviewRequestType) => + await instance.post(`${router}`, body), + { + ...options, + onError: (err: AxiosError) => { + const { response } = err; + switch (response?.status) { + case 400: { + append({ + title: "", + message: "입력값은 비어있으면 안됩니다.", + type: "RED", + }); + break; + } + case 404: { + switch ((response as AxiosResponse<{ message: string }>).data.message) { + case "Code Not Found": { + append({ + title: "", + message: "질문 분야가 누락되었습니다.", + type: "RED", + }); + break; + } + case 'ApplicationEntity Not Found': { + append({ + title: '', + message: '해당 기업에는 후기를 작성할 수 없습니다.', + type: 'RED', + }) + } + } + + } + } + }, + } + ); +}; diff --git a/src/apis/reviews/type.ts b/src/apis/reviews/type.ts index dcfe322..8e80a40 100644 --- a/src/apis/reviews/type.ts +++ b/src/apis/reviews/type.ts @@ -18,3 +18,14 @@ export interface getReviewDetailProps { answer: string; area: string; } + +export interface qnaElementsType { + question: string; + answer: string; + code_id: number; +} + +export interface createReviewRequestType { + company_id: number; + qna_elements: qnaElementsType[] +} \ No newline at end of file diff --git a/src/app/companies/reviews/create/page.tsx b/src/app/companies/reviews/create/page.tsx new file mode 100644 index 0000000..fc36510 --- /dev/null +++ b/src/app/companies/reviews/create/page.tsx @@ -0,0 +1,90 @@ +"use client"; + +import { useState } from "react"; +import ReviewForm from "@/components/company/ReviewForm"; +import FillBtn from "@/components/common/Button/FillBtn"; +import { useRouter, useSearchParams } from "next/navigation"; +import { useCreateReviews } from "@/apis/reviews"; +import { qnaElementsType } from "@/apis/reviews/type"; + +export default function CreateReviews() { + const router = useRouter(); + const params = useSearchParams(); + const companyId = Number(params.get('id')) + const [qnaElements, setQnaElements] = useState([ + { question: "", answer: "", code_id: 0 }, + ]); + const mutateOption = { + onSuccess: () => { + router.push(`/companies/reviews/?id=${companyId}`) + } + } + const { mutate: createReviews } = useCreateReviews(mutateOption); + + const handleClickCreateRevies = () => { + createReviews({ + company_id:companyId, + qna_elements: qnaElements + }) + } + + const handleChange = ( + index: number, + name: keyof qnaElementsType, + value: string | number + ) => { + setQnaElements(prev => { + const newReviews = [...prev]; + if (typeof value === "number") { + newReviews[index].code_id = value; + } else if (name !== "code_id") { + newReviews[index][name] = value; + } + return newReviews; + }); + }; + + const removeReviewList = (index: number) => { + setQnaElements(prev=>{ + let newReviews = [...prev]; + newReviews = newReviews.filter((_,idx)=>idx !== index); + return newReviews; + }) + } + + return ( +
+

+ 후기작성 +

+ {qnaElements + .map((qnaElement, idx) => ( + + ))} +
+ { + setQnaElements(prev => ([ + ...prev, + { question: "", answer: "", code_id: 0 } + ])); + }} + > + 면접질문 추가 + +
+ { + router.back(); + }} + > + 이전으로 + + 완료 +
+
+
+ ); +} diff --git a/src/app/companies/reviews/page.tsx b/src/app/companies/reviews/page.tsx index fe30be7..fec1e50 100644 --- a/src/app/companies/reviews/page.tsx +++ b/src/app/companies/reviews/page.tsx @@ -4,7 +4,7 @@ export default function Reviews() { return (

- 면접 후기 + 면접후기


diff --git a/src/app/globals.css b/src/app/globals.css index ac69618..a4780a8 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -53,6 +53,10 @@ input[type="number"]::-webkit-inner-spin-button { -webkit-appearance: none; } +textarea { + resize: none; +} + table { width: 100%; border: none; diff --git a/src/app/mypage/page.tsx b/src/app/mypage/page.tsx index 5e633aa..34b309e 100644 --- a/src/app/mypage/page.tsx +++ b/src/app/mypage/page.tsx @@ -1,10 +1,12 @@ import AppliedCompaniesList from "@/components/mypage/AppliedCompaniesList"; import DetailProfile from "@/components/mypage/DetailProfile"; +import CompaniesForReviewing from "@/components/mypage/CompaniesForReviewing"; export default function MyPage() { return (
+
); diff --git a/src/app/recruitments/detail/page.tsx b/src/app/recruitments/detail/page.tsx index 89d4233..79235a8 100644 --- a/src/app/recruitments/detail/page.tsx +++ b/src/app/recruitments/detail/page.tsx @@ -11,7 +11,7 @@ export default function RecruitmentsDetailPage() { const navigator = useRouter(); const { data: RecruitmentsDetial } = useGetRecruitmentsDetail(param.get("id")!); if (RecruitmentsDetial) { - const { company_id, company_name, company_profile_url, ...rest } = + const { company_id, company_name, company_profile_url, bookmarked, recruitment_id, ...rest } = RecruitmentsDetial; return ( @@ -20,6 +20,8 @@ export default function RecruitmentsDetailPage() { company_name={company_name} company_profile_url={company_profile_url} company_id={company_id} + bookmarked={bookmarked} + recruitmentId={recruitment_id} > { diff --git a/src/components/BookmarkCard.tsx b/src/components/BookmarkCard.tsx index db262a0..8a3f06a 100644 --- a/src/components/BookmarkCard.tsx +++ b/src/components/BookmarkCard.tsx @@ -1,10 +1,20 @@ -import { useGetBookmarks } from "@/apis/bookmarks"; +import { useGetBookmarks, useSetBookmarks } from "@/apis/bookmarks"; import { Icon } from "@team-return/design-system"; import Link from "next/link"; import HoverPrefetchLink from "./common/HoverPrefetchLink"; +import Image from "next/image"; +import { useEffect, useState } from "react"; +import { BookmarkItemsType } from "@/apis/bookmarks/type"; export default function BookmarkCard() { const { data: bookmarks } = useGetBookmarks(); + const { mutate: SetBookmarksMutate } = useSetBookmarks(); + + const [localBookmarks, setLocalBookmarks] = useState(bookmarks?.bookmarks || []) + + useEffect(()=>{ + if(bookmarks) setLocalBookmarks(bookmarks.bookmarks); + },[bookmarks?.bookmarks]) return (
@@ -17,17 +27,26 @@ export default function BookmarkCard() {
)} - {bookmarks?.bookmarks.map(({ company_name, recruitment_id }) => ( + {localBookmarks.map(({ company_name, recruitment_id, company_logo_url }) => (
+
+ 기업로고 +

{company_name}

-
diff --git a/src/components/common/Button/FillBtn.tsx b/src/components/common/Button/FillBtn.tsx new file mode 100644 index 0000000..76f7fb6 --- /dev/null +++ b/src/components/common/Button/FillBtn.tsx @@ -0,0 +1,20 @@ +import React from "react"; + +interface Propstype extends React.ButtonHTMLAttributes { + backgroundColor?: string +} + +export default function FillBtn({ children, backgroundColor, style, ...rest }: Propstype) { + return ( + + ); +} diff --git a/src/components/common/Button/SendBtn.tsx b/src/components/common/Button/SendBtn.tsx deleted file mode 100644 index 5773760..0000000 --- a/src/components/common/Button/SendBtn.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import React from "react"; - -function SendBtn() { - return ( - <> - <> - - ); -} - -export default React.memo(SendBtn); diff --git a/src/components/common/DropDown.tsx b/src/components/common/DropDown.tsx index 54ffa72..c80088b 100644 --- a/src/components/common/DropDown.tsx +++ b/src/components/common/DropDown.tsx @@ -19,7 +19,7 @@ interface PropsType { function DropDown({ title, items, onClickItem, selected }: PropsType) { const { toggleDropdown, DropDownComponent, closeDropDown } = useDropDown(); - const selectedItemLabel = items.find((item) => item.code === selected); + const selectedItemLabel = items.find((item) => item.code === selected.toString()); return (
{label}

)}
void; children?: React.ReactNode; company_id?: number; + bookmarked?: boolean; + recruitmentId?: number; } export default function CompanyTitle({ @@ -24,11 +29,17 @@ export default function CompanyTitle({ onClickInterview, children, company_id, + bookmarked, + recruitmentId }: PropsType) { const kebabItems: KebabItemType[] = getCompanyKebabItems( onClickRecruitments, onClickInterview ); + const { mutate: SetBookmarksMutate } = useSetBookmarks(); + const [localBookmarked, setLocalBookmarked] = useState( + bookmarked || false + ); const navigator = useRouter(); @@ -45,7 +56,26 @@ export default function CompanyTitle({ />
-

{company_name}

+
+

{company_name}

+ { + recruitmentId && ( + + ) + } + +
{business_number && (

사업자 번호 : {business_number} diff --git a/src/components/company/ReviewForm.tsx b/src/components/company/ReviewForm.tsx new file mode 100644 index 0000000..9aa5b1c --- /dev/null +++ b/src/components/company/ReviewForm.tsx @@ -0,0 +1,101 @@ +import React from "react"; +import { useGetCode } from "@/apis/code"; +import DropDown from "@/components/common/DropDown"; +import TextFiled from "@/components/common/TextFiled"; +import useForm from "@/hook/useForm"; +import { useRef, useState } from "react"; +import { theme } from "@team-return/design-system"; +import { qnaElementsType } from "@/apis/reviews/type"; + +interface PropsType { + question: string; + answer: string; + code_id: number; + index: number; + onChange: (idx: number, name: keyof qnaElementsType, value: string) => void; + setState: React.Dispatch>; + removeReviews: (index:number) => void; +} + +export default function ReviewForm({ + question, + answer, + code_id, + index, + onChange, + setState: setQnaElements, + removeReviews +}: PropsType) { + const textarea = useRef(null); + + const { data: codes } = useGetCode("JOB"); + + const [isFocus, setIsFocus] = useState(false); + + const jobCodeDropdownItems = codes?.codes.map(item => ({ + code: item.code.toString(), + label: item.keyword, + })); + + const handleResizeHeight = () => { + if (textarea?.current) { + textarea.current.style.height = "auto"; + textarea.current.style.height = textarea.current.scrollHeight + "px"; + } + }; + return ( +

+
+ { + onChange(index, "question", e.target.value); + }} + className="flex-1" + /> +
+

+ 질문 분야 +

+ { + setQnaElements(prev => { + const updatedElements = [...prev]; + updatedElements[index].code_id = Number(itemCode); + return updatedElements; + }); + }} + selected={code_id} + /> +
+
+

답변

+