Skip to content

Commit

Permalink
Merge pull request #75 from seunghyeon77/feat/운동-인증-페이지
Browse files Browse the repository at this point in the history
2차 QA 수정 사항
  • Loading branch information
cho-in-sik authored Dec 29, 2024
2 parents be12cdb + 0184d56 commit 6459010
Show file tree
Hide file tree
Showing 14 changed files with 588 additions and 377 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { IWorkoutConfirmationPageProps } from '@/types/workoutConfirmation';
import ConfirmationProfile from './ConfirmationProfile';
import ConfirmationCompoObjection from './ConfirmationCompoObjection';
import ConfirmationCompoConfirm from './ConfirmationCompoConfirm';
import ConfirmationCompoTime from './ConfirmationCompoTime';

interface IConfirmationCompoProps {
workoutConfirmationPage: IWorkoutConfirmationPageProps;
Expand Down Expand Up @@ -30,15 +31,9 @@ export default function ConfirmationCompo({
workspaceId={workspaceId}
/>
)}
<div
className={`${
workoutConfirmationPage.isMine ? 'mr-2' : 'ml-2'
} flex items-end `}
>
<span className='text-[10px] text-[#9CA3AF]'>
{workoutConfirmationPage.createdAt.substring(11, 16)}
</span>
</div>
<ConfirmationCompoTime
workoutConfirmationPage={workoutConfirmationPage}
/>
</div>
</div>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { IWorkoutConfirmationPageProps } from '@/types/workoutConfirmation';

interface IConfirmationPageProps {
workoutConfirmationPage: IWorkoutConfirmationPageProps;
}

export default function ConfirmationCompoTime({
workoutConfirmationPage,
}: IConfirmationPageProps) {
return (
<div
className={`${
workoutConfirmationPage.isMine ? 'mr-2' : 'ml-2'
} flex items-end `}
>
<span className='text-[10px] text-[#9CA3AF]'>
{workoutConfirmationPage.createdAt.substring(11, 16)}
</span>
</div>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
'use client';

import { usePathname } from 'next/navigation';
import { useEffect } from 'react';

export default function ScrollTop() {
const pathName = usePathname();

useEffect(() => {
window.scrollTo(0, 0);
}, [pathName]);

return null;
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,64 +10,91 @@ import ObjectionBell from './_components/ObjectionBell';
import IsSameDateAsPrevious from './_components/IsSameDataAsPrevious';
import ConfirmationCompo from './_components/ConfirmationCompo';
import useInfiniteQuerys from '@/hooks/workoutConfirmation/ useInfiniteQuerys';
import { useEffect, useRef } from 'react';
import { useEffect, useRef, useState } from 'react';
import NoDataUI from '../../_components/NoDataUI';

export default function Page() {
const workspaceId = useWorkoutIdFromParams();
const [ref, inView] = useInView({ threshold: 0, delay: 0 });
const [initialObserve, setInitialObserve] = useState(false);

const [ref, inView] = useInView({
threshold: 0.1,
delay: 500,
});

const scrollBottomRef = useRef<HTMLDivElement | null>(null);
const isInitialRender = useRef(true);
const isInitialRender = useRef<boolean>(true);

const workoutConfirmation = useInfiniteQuerys({
queryKey: ['workoutConfirmations', workspaceId],
dataReqFn: workoutConfirmations,
params: { workspaceId },
inView,
inView: inView && initialObserve,
});

const workoutConfirmationPages = workoutConfirmation?.pages.flatMap(
(pages) => pages.data.data
);
const workoutConfirmationPages = workoutConfirmation?.pages
.slice()
.reverse()
.flatMap((pages) => pages.data.data);

const workoutConfirmationVoteInCompletionCount =
workoutConfirmation?.pages.flatMap(
(pages) => pages.data.voteIncompletionCount
);

useEffect(() => {
if (scrollBottomRef.current && isInitialRender) {
scrollBottomRef.current?.scrollIntoView({ behavior: 'auto' });
isInitialRender.current = false;
}
if (
isInitialRender.current &&
workoutConfirmationPages &&
workoutConfirmationPages.length
)
setTimeout(() => {
scrollBottomRef.current?.scrollIntoView({
behavior: 'auto',
});
isInitialRender.current = false;
setTimeout(() => {
setInitialObserve(true);
}, 100);
}, 100);
}, [workoutConfirmationPages]);

return (
<div className='h-full'>
<div ref={ref} />
<div className='-mx-4 px-4 bg-[#F1F7FF] -mt-3 pb-3'>
{workoutConfirmationPages?.map(
(
workoutConfirmationPage: IWorkoutConfirmationPageProps,
index: number
) => {
return (
<div
key={`${workoutConfirmationPage.workoutConfirmationId}-${
workoutConfirmationPage.objectionId || 'noObjection'
}-${workoutConfirmationPage.createdAt}`}
>
<IsSameDateAsPrevious
workoutConfirmationPages={workoutConfirmationPages}
workoutConfirmationPage={workoutConfirmationPage}
index={index}
/>
<ConfirmationCompo
workoutConfirmationPage={workoutConfirmationPage}
workspaceId={workspaceId}
/>
</div>
);
}
{workoutConfirmationPages?.length === 0 ? (
<div>
<NoDataUI content='아직 운동 인증이 없어요.' />
</div>
) : (
<div>
<div ref={ref} />
{workoutConfirmationPages?.map(
(
workoutConfirmationPage: IWorkoutConfirmationPageProps,
index: number
) => {
return (
<div
key={`${workoutConfirmationPage.workoutConfirmationId}-${
workoutConfirmationPage.objectionId || 'noObjection'
}-${workoutConfirmationPage.createdAt}`}
>
<IsSameDateAsPrevious
workoutConfirmationPages={workoutConfirmationPages}
workoutConfirmationPage={workoutConfirmationPage}
index={index}
/>
<ConfirmationCompo
workoutConfirmationPage={workoutConfirmationPage}
workspaceId={workspaceId}
/>
</div>
);
}
)}
<div ref={scrollBottomRef} />
</div>
)}
<ObjectionBell
workspaceId={workspaceId}
Expand All @@ -76,7 +103,6 @@ export default function Page() {
}
/>
</div>
<div ref={scrollBottomRef} />
</div>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import Image from 'next/image';

import confirmDetailNoImage from '@/../public/svgs/workspace/workspaceConfirmaion/confirmDetailNoImage.svg';
import { IWorkspaceConfirmationDetailProps } from '@/types/workoutConfirmation';

interface IConfirmationDetailImage {
workspaceConfirmationDetail: IWorkspaceConfirmationDetailProps | undefined;
}

export default function ConfirmationDetailImage({
workspaceConfirmationDetail,
}: IConfirmationDetailImage) {
return (
<div className='w-full h-[380px] bg-[#E5E7EB] mt-5 flex justify-center relative'>
{workspaceConfirmationDetail?.workoutConfirmationImageUrl === '' ? (
<Image src={confirmDetailNoImage} alt='confirmDetailNoImage' />
) : (
<Image
src={workspaceConfirmationDetail?.workoutConfirmationImageUrl}
alt='Image'
loader={({ src }) => src}
loading='lazy'
sizes='360px'
fill
/>
)}
</div>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
'use client';

import { useQuery } from '@tanstack/react-query';

import ConfirmationDetailImage from './ConfimationDetailImage';
import ConfirmationDetailProfile from './ConfirmationDetailProfile';
import { IWorkspaceConfirmationDetailProps } from '@/types/workoutConfirmation';
import useWorkoutIdFromParams from '@/hooks/workoutHistory/useWorkoutIdFromParams';
import { workoutConfirmaionsDetail } from '@/api/workspaceConfirmaion';

interface IConfirmationDetailCompoProps {
workoutConfirmationId: number;
}

export default function ConfirmationDetailCompo({
workoutConfirmationId,
}: IConfirmationDetailCompoProps) {
const workspaceId = useWorkoutIdFromParams();

const { data: workspaceConfirmationDetail } = useQuery<{
data: IWorkspaceConfirmationDetailProps;
}>({
queryKey: [
'workspaceConfimationDetail',
workspaceId,
workoutConfirmationId,
],
queryFn: () =>
workoutConfirmaionsDetail({
workspaceId,
workoutConfirmationId,
}),
});
return (
<div>
<ConfirmationDetailProfile
workspaceConfirmationDetail={workspaceConfirmationDetail?.data}
/>
<div className='my-5'>
<span className='text-base text-[#1F2937]'>
{workspaceConfirmationDetail?.data.comment}
</span>
<ConfirmationDetailImage
workspaceConfirmationDetail={workspaceConfirmationDetail?.data}
/>
</div>
</div>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
'use client';

import { workoutObjectionReason } from '@/api/workspaceConfirmaion';
import {
DialogClose,
DialogContent,
DialogFooter,
DialogHeader,
DialogTitle,
} from '@/components/ui/dialog';
import useWorkoutIdFromParams from '@/hooks/workoutHistory/useWorkoutIdFromParams';
import { DialogDescription } from '@radix-ui/react-dialog';
import { useMutation } from '@tanstack/react-query';
import { useRouter } from 'next/navigation';
import { useState } from 'react';

interface IConfirmationDetailObjectionInputProps {
workoutConfirmationId: number;
}

export default function ConfirmationDetailObjectionInput({
workoutConfirmationId,
}: IConfirmationDetailObjectionInputProps) {
const router = useRouter();
const [reasonInput, setReasonInput] = useState('');
const workspaceId = useWorkoutIdFromParams();

const objectionReason = useMutation({
mutationFn: workoutObjectionReason,
onSuccess: (data) => {
console.log('Success:', data);
alert('이의 신청이 성공적으로 제출되었습니다.');
router.push(`/workspace/${workspaceId}/workspaceConfirmation`);
},
onError: (error) => {
console.error('Error:', error);
alert('이의 신청 제출 중 오류가 발생했습니다.');
},
});

const handleReasonPost = (event: React.MouseEvent<HTMLButtonElement>) => {
event.preventDefault();

if (reasonInput.length < 10) {
alert('10자 이상 작성하셔야 합니다.');
}

if (reasonInput.length >= 10) {
objectionReason.mutate({
workspaceId,
workoutConfirmationId,
objectionReason: reasonInput,
});
}
};

return (
<DialogContent
className={`w-72 ${
reasonInput.length <= 10 ? 'h-[190px]' : 'h-[165px]'
} rounded-lg`}
>
<DialogHeader>
<DialogTitle className='-mb-2' />
<DialogDescription className='text-base text-[#1F2937]'>
이의 신청 이유를 입력해주세요.
</DialogDescription>
</DialogHeader>
<div>
<input
className='w-60 h-10 rounded-lg bg-[#F9FAFB] text-sm text-[#B7C4D5] pl-3'
value={reasonInput}
onChange={(e) => setReasonInput(e.target.value)}
/>
{reasonInput.length <= 10 ? (
<span className='text-[8px] text-[#F87171] ml-2'>
이의 신청 이유는 10자 이상 작성해주세요.
</span>
) : (
<></>
)}
</div>
<DialogFooter>
<div>
<hr className='absolute left-1/2 -translate-x-1/2 w-72 border-l border-[#E5E7EB]' />
<div className='flex justify-between mx-7 items-center'>
<DialogClose asChild>
<span className='text-sm text-[#B7C4D5]'>cancel</span>
</DialogClose>
<hr className='h-[45px] border-l border-[#E5E7EB]' />
<button
className='text-sm text-[#3B82F6] mr-3'
onClick={handleReasonPost}
>
OK
</button>
</div>
</div>
</DialogFooter>
</DialogContent>
);
}
Loading

0 comments on commit 6459010

Please sign in to comment.