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

feat(my-recruit): QA반영 및 내 공고 일정 수정 페이지 구현 #51

Merged
merged 3 commits into from
Aug 29, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
36 changes: 36 additions & 0 deletions src/app/(sidebar)/(my-info)/containers/Onboarding/LogoLeaf.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
export function LogoLeaf() {
return (
<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 18 18" fill="none">
<path
d="M6.95076 4.69616L6.71222 2.62413L9.28129 0.449716L13.1015 2.08511L14.6898 3.99471L13.5597 6.1562L15.6505 6.15803L17.1724 9.00283L16.3246 12.9441L13.8177 13.886L11.5652 13.0542L10.7603 15.5643L8.61926 16.8785L4.56281 15.0992L4.73665 12.0278L1.91604 11.2028L0.936531 8.86515L1.91181 4.33131L4.72747 3.538L6.95076 4.69616Z"
fill="#20E79D"
/>
<path
opacity="0.9"
fill-rule="evenodd"
clip-rule="evenodd"
d="M12.5042 11.0016L10.3408 9.86802L10.6884 9.0166L12.9852 9.72614L12.5042 11.0016Z"
fill="#007D4F"
/>
<path
opacity="0.9"
d="M9.25348 7.4811L9.80066 5.25152L11.0796 5.67017L10.1247 7.7757L9.25348 7.4811Z"
fill="#007D4F"
/>
<path
opacity="0.9"
fill-rule="evenodd"
clip-rule="evenodd"
d="M7.47524 8.91236L5.06404 8.3716L5.38759 7.28785L7.67825 8.09248L7.47524 8.91236Z"
fill="#007D4F"
/>
<path
opacity="0.9"
fill-rule="evenodd"
clip-rule="evenodd"
d="M9.32851 10.2445L8.89211 12.4077L7.67188 12.2134L8.4429 9.99665L9.32851 10.2445Z"
fill="#007D4F"
/>
</svg>
);
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import { Dialog, DialogContent, DialogTrigger } from '@/system/components/Dialog/Dialog';
import { Dialog, DialogContent } from '@/system/components/Dialog/Dialog';
import { Logo } from './Logo';
import { Spacing } from '@/system/utils/Spacing';
import { AnimateSlide } from '@/system/utils/AnimateSlide/AnimateSlide';
import { TouchButton } from '@/components/TouchButton';
import { useState } from 'react';
import { useEffect, useState } from 'react';
import { If } from '@/system/utils/If';
import { cn } from '@/utils';
import { motion } from 'framer-motion';
import { motion, useAnimationControls } from 'framer-motion';
import { color } from '@/system/token/color';
import { LogoLeaf } from './LogoLeaf';
import { Icon } from '@/system/components';

const MAX_INDEX = 3;

Expand All @@ -18,6 +20,7 @@ interface OnboardingDialogProps {
export function OnboardingDialog({}: OnboardingDialogProps) {
const [step, setStep] = useState<'text' | 'card' | 'finish'>('text');
const [currentIndex, setCurrentIndex] = useState(0);
const controls = useAnimationControls();

const onNextClick = () => {
if (currentIndex < MAX_INDEX) {
Expand All @@ -27,11 +30,29 @@ export function OnboardingDialog({}: OnboardingDialogProps) {
setStep('card');
};

useEffect(() => {
if (step === 'card') {
const interval = setInterval(() => controls.start('wiggle'), 100);

const timeout = setTimeout(() => {
clearInterval(interval);
setStep('finish');
}, 3000);
return () => clearTimeout(timeout);
}
}, [step]);

return (
<Dialog open>
<DialogContent className="p-0 w-auto max-w-[auto]">
<motion.div
animate={step !== 'text' ? { width: 260, height: 440, transition: { delay: 0.2 } } : {}}
variants={{
text: {},
// wiggle: { x: [-2, 2, -2, 2] },
card: { width: 260, height: 440, transition: { delay: 0.2 } },
finish: { width: 260, height: 440, transition: { delay: 0.2 } },
}}
animate={step}
className="relative flex flex-col items-center p-[24px]">
<Spacing size={8} />
<button
Expand Down Expand Up @@ -112,16 +133,23 @@ export function OnboardingDialog({}: OnboardingDialogProps) {
className="absolute bg-white top-0 left-0 w-full h-full rounded-[24px]"
/>
</If>
<If condition={step !== 'text'}>
<TouchButton className="absolute bottom-[-84px] p-0">
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ delay: 1 }}
className="px-[20px] py-[13px] flex gap-[6px] text-white bg-neutral-95 rounded-[6px] text-bold2 font-semibold">
행운의 취뽀 부적 다운받기
</motion.div>
</TouchButton>
<If condition={step === 'finish'}>
<div className="absolute left-[50%] translate-x-[-50%] bottom-[-84px]">
<TouchButton>
<motion.div
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
transition={{ delay: 1 }}
className="px-[20px] py-[13px] flex items-center gap-[6px] text-white bg-neutral-95 rounded-[6px] text-bold2 font-semibold whitespace-pre">
<LogoLeaf />
<span className="flex items-center">
행운의 취뽀 부적 다운받기
<Icon name="download" />
</span>
<LogoLeaf />
</motion.div>
</TouchButton>
</div>
</If>
</DialogContent>
</Dialog>
Expand Down
4 changes: 4 additions & 0 deletions src/app/(sidebar)/(my-info)/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { If } from '@/system/utils/If';
import { motion } from 'framer-motion';
import { InfoCardSkeleton } from './components/InfoCardSkeleton';
import { AsyncBoundaryWithQuery } from '@/lib';
import { Onboarding } from './containers/Onboarding/Onboarding';

export default function MyInfo() {
const [showHeader, setShowHeader] = useState(false);
Expand Down Expand Up @@ -112,6 +113,9 @@ export default function MyInfo() {
pendingFallback={<InfoCardSkeleton count={4} />}>
<InfoCardList cardType={currentCardType} />
</AsyncBoundaryWithQuery>
<If condition={false}>
<Onboarding />
</If>
</div>
</div>
);
Expand Down
4 changes: 2 additions & 2 deletions src/app/(sidebar)/my-recruit/[id]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { useState } from 'react';
import { useDeleteRecruit } from '../api/useDeleteRecruit';
import { usePostCardToRecruit } from '../api/usePostCardToRecruit';
import { RightSidebar } from '../containers/RightSidebar/RightSidebar';
import { DueDateDialog } from '../containers/components/DueDateDialog';
import { DueDateDialog } from '../containers/components/DueDateDialog/DueDateDialog';
import { DetailContent } from './components/DetailContent';
import DetailHeader from './components/DetailHeader';

Expand Down Expand Up @@ -59,7 +59,7 @@ export default function CompanyDetail({ params: { id: recruitId } }: { params: {
</Dropdown.Trigger>
<Dropdown.Content align="end">
<Dropdown.CheckedItem className="gap-[8px]">
<DueDateDialog onDuedateAppend={() => {}} />
<DueDateDialog id={Number(recruitId)} onDuedateAppend={() => {}} />
</Dropdown.CheckedItem>
</Dropdown.Content>
</Dropdown>
Expand Down
25 changes: 25 additions & 0 deletions src/app/(sidebar)/my-recruit/api/useDeleteRecruitSchedule.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { http } from '@/apis/http';
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { GET_RECRUIT_SCHEDULE } from '@/app/(sidebar)/my-recruit/api/useGetRecruitSchedule';

interface Request {
id: number;
recruitScheduleId: number;
}

function deleteRecruitSchedule({ id, recruitScheduleId }: Request) {
return http.delete({ url: `/recruits/${id}/recruit-schedule/${recruitScheduleId}` });
}

export function useDeleteRecruitSchedule() {
const queryClient = useQueryClient();

const mutate = useMutation({
mutationFn: (data: Request) => deleteRecruitSchedule(data),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: [GET_RECRUIT_SCHEDULE] });
},
});

return mutate;
}
30 changes: 30 additions & 0 deletions src/app/(sidebar)/my-recruit/api/useGetRecruitSchedule.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { http } from '@/apis/http';
import { useSuspenseQuery } from '@tanstack/react-query';

interface Request {
id: number;
}

interface ScheduleType {
id: number;
recruitScheduleStage: string;
deadLine: string;
}

export const GET_RECRUIT_SCHEDULE = 'get-recruit-schedule';

function getRecruitSchedule({ id }: Request) {
return http.get<ScheduleType[]>({ url: `/recruits/${id}/recruit-schedule` });
}

export function useGetRecruitSchedule({ id }: Request) {
const result = useSuspenseQuery({
queryKey: [GET_RECRUIT_SCHEDULE, id],
queryFn: async () => {
const res = await getRecruitSchedule({ id });
return res.data;
},
});

return result;
}
26 changes: 26 additions & 0 deletions src/app/(sidebar)/my-recruit/api/usePostRecruitSchedule.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { http } from '@/apis/http';
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { GET_RECRUIT_SCHEDULE } from '@/app/(sidebar)/my-recruit/api/useGetRecruitSchedule';

interface Request {
id: number;
recruitScheduleStage: string;
deadLine: string;
}

function postRecruitSchedule({ id, ...data }: Request) {
return http.post({ url: `/recruits/${id}/recruit-schedule`, data });
}

export function usePostRecruitSchedule() {
const queryClient = useQueryClient();

const mutate = useMutation({
mutationFn: (data: Request) => postRecruitSchedule(data),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: [GET_RECRUIT_SCHEDULE] });
},
});

return mutate;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { http } from '@/apis/http';
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { GET_RECRUIT_SCHEDULE } from '@/app/(sidebar)/my-recruit/api/useGetRecruitSchedule';

interface Request {
id: number;
recruitScheduleId: number;
deadLine: string;
}

export function putRecruitScheduleDeadline({ id, recruitScheduleId, deadLine }: Request) {
return http.put({ url: `/recruits/${id}/recruit-schedule/${recruitScheduleId}/deadLine`, data: { deadLine } });
}

export function usePutRecruitScheduleDeadline() {
const queryClient = useQueryClient();

const mutate = useMutation({
mutationFn: (data: Request) => putRecruitScheduleDeadline(data),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: [GET_RECRUIT_SCHEDULE] });
},
});

return mutate;
}
29 changes: 29 additions & 0 deletions src/app/(sidebar)/my-recruit/api/usePutRecruitScheduleStage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { http } from '@/apis/http';
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { GET_RECRUIT_SCHEDULE } from '@/app/(sidebar)/my-recruit/api/useGetRecruitSchedule';

interface Request {
id: number;
recruitScheduleId: number;
recruitScheduleStage: string;
}

export function putRecruitScheduleStage({ id, recruitScheduleId, recruitScheduleStage }: Request) {
return http.put({
url: `/recruits/${id}/recruit-schedule/${recruitScheduleId}/stage`,
data: { recruitScheduleStage },
});
}

export function usePutRecruitScheduleStage() {
const queryClient = useQueryClient();

const mutate = useMutation({
mutationFn: (data: Request) => putRecruitScheduleStage(data),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: [GET_RECRUIT_SCHEDULE] });
},
});

return mutate;
}
14 changes: 11 additions & 3 deletions src/app/(sidebar)/my-recruit/components/DraggableInfoCard.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,20 @@
import { InfoCard } from '@/components/InfoCard';
import { useDndContext } from '@/lib/dnd-kit/dnd-kit';
import { If } from '@/system/utils/If';
import { ComponentProps } from 'react';

export function DraggableInfoCard(props: ComponentProps<typeof InfoCard>) {
const { over } = useDndContext();

const overTitle = over?.data.current?.title;

return (
<div className="relative">
<div className="absolute top-0 left-[50%] translate-y-[calc(-100%-8px)] translate-x-[-50%] px-[10px] py-[4px] bg-[rgba(112,115,124,0.85)] text-[white] text-label1 font-semibold rounded-[6px]">
{props.title || '제목을 입력해주세요'}
</div>
<If condition={overTitle != null}>
<div className="absolute top-0 left-[50%] translate-y-[calc(-100%-8px)] translate-x-[-50%] px-[10px] py-[4px] bg-[rgba(112,115,124,0.85)] text-[white] text-label1 font-semibold rounded-[6px] whitespace-pre">
{`내 공고 '${overTitle}' 폴더로 복사 중`}
</div>
</If>
<InfoCard {...props} />
</div>
);
Expand Down
2 changes: 1 addition & 1 deletion src/app/(sidebar)/my-recruit/constant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ export const recruitStatusList = [
{ variant: 'text', text: '지원 준비' },
{ variant: 'text', text: '지원 완료' },
{ variant: 'border' },
{ variant: 'text', text: '서류 마감' },
{ variant: 'text', text: '서류 통과' },
{ variant: 'text', text: '서류 탈락' },
{ variant: 'border' },
{ variant: 'text', text: '면접 통과' },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,10 @@ export function AllRecruitList({ selectedSeason }: Props) {
present: (
<div className="flex flex-col gap-[12px]">
{allRecruits.map((cardInfo) => (
<Droppable key={`${cardInfo.season}-${cardInfo.title}`} id={cardInfo.id}>
<Droppable
key={`${cardInfo.id}-${cardInfo.season}-${cardInfo.title}`}
id={cardInfo.id}
dataForOverlay={{ title: cardInfo.title }}>
<RowCard
highlighted={cardInfo.id === over?.id}
{...cardInfo}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ export function ProgressingRecruitList() {
const { mutate: deleteRecruit } = useDeleteRecruit();

const [cardsPerRow, setCardsPerRow] = useState(최초_노출_카드_갯수);
console.log('🚀 ~ ProgressingRecruitList ~ cardsPerRow:', cardsPerRow, recruitCards);
const resizeRef = useResizeObserver(({ contentRect }) => {
setCardsPerRow(Math.max(1, Math.floor(contentRect.width / (MIN_CARD_WIDTH + CARD_GAP))));
});
Expand Down Expand Up @@ -55,7 +54,7 @@ export function ProgressingRecruitList() {
<div ref={resizeRef} className="grid flex-wrap" style={{ gridTemplateColumns, gap: CARD_GAP }}>
{recruitCardForShow.map((cardInfo) => (
<BoxCard
key={`${cardInfo.season}-${cardInfo.title}`}
key={`${cardInfo.id}-${cardInfo.season}-${cardInfo.title}`}
{...cardInfo}
onDuedateAppend={() => {}}
onRecruitDelete={deleteRecruit}
Expand Down
Loading
Loading