Skip to content
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
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { ActivityDetailEdit, Schedule } from '@/types/activityDetailType';
import { AxiosError } from 'axios';
import { toast } from 'sonner';
import { notFound } from 'next/navigation';
import { validateSchedules } from '../../utils/dateValidatoin';

interface SubImageType {
id?: number;
Expand Down Expand Up @@ -208,6 +209,11 @@ export const useEditActivityForm = () => {

const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
const validationMessage = validateSchedules(dates);
if (validationMessage) {
toast.error(validationMessage);
return;
}
try {
await mutation.mutateAsync();
} catch (error) {
Expand Down
17 changes: 8 additions & 9 deletions src/app/(with-header)/myactivity/components/ScheduleSelect.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ interface ScheduleSelectProps {
export function ScheduleSelect({
index,
isRemovable,

onRemove,
onDateChange,
onStartTimeChange,
Expand All @@ -29,9 +28,9 @@ export function ScheduleSelect({
endTime,
}: ScheduleSelectProps) {
return (
<div className='pt-5 w-full'>
<div className='flex flex-wrap items-end gap-4 w-full'>
<div className='flex flex-col gap-10 flex-1 min-w-0'>
<div className='w-full pt-5'>
<div className='flex w-full flex-wrap items-end gap-4'>
<div className='flex min-w-0 flex-1 flex-col gap-10'>
<Input
className='w-full'
type='date'
Expand All @@ -40,7 +39,7 @@ export function ScheduleSelect({
variant='compact'
/>
</div>
<div className='flex flex-col gap-10 flex-1 min-w-0'>
<div className='flex min-w-0 flex-1 flex-col gap-10'>
<Input
className='w-full'
type='time'
Expand All @@ -49,7 +48,7 @@ export function ScheduleSelect({
variant='compact'
/>
</div>
<div className='flex flex-col gap-10 flex-1 min-w-0'>
<div className='flex min-w-0 flex-1 flex-col gap-10'>
<Input
className='w-full'
type='time'
Expand All @@ -61,11 +60,11 @@ export function ScheduleSelect({

<div className='flex-shrink-0'>
{isRemovable && (
<div className="flex-shrink-0 w-44">
<div className='w-44 flex-shrink-0'>
<button
type="button"
type='button'
onClick={() => onRemove(index)}
className="w-full h-46 border border-gray-300 rounded-[10px] flex items-center justify-center hover:bg-gray-100"
className='flex h-46 w-full items-center justify-center rounded-[10px] border border-gray-300 hover:bg-gray-100'
>
<IconClose />
</button>
Expand Down
48 changes: 38 additions & 10 deletions src/app/(with-header)/myactivity/components/ScheduleSelectForm.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
'use client';

import { toast } from 'sonner';
import { ScheduleSelect } from './ScheduleSelect';
import { Schedule } from '@/types/activityDetailType';

Expand All @@ -14,6 +15,17 @@ interface ScheduleSelectFormProps {
) => void;
}

function isPastDate(dateStr: string) {
const selected = new Date(dateStr);
const today = new Date();
today.setHours(0, 0, 0, 0);
return selected < today;
}

function isInvalidTimeRange(start: string, end: string) {
return start >= end;
}

export function ScheduleSelectForm({
dates,
onAddDate,
Expand All @@ -23,38 +35,54 @@ export function ScheduleSelectForm({
return (
<div className='space-y-4 pt-24'>
<div className='flex flex-row items-center justify-between gap-5'>
<p className='text-xl text-black font-bold'>예약 가능한 시간대</p>
<p className='text-xl font-bold text-black'>예약 가능한 시간대</p>
<div>
<button
type='button'
onClick={onAddDate}
className='w-44 h-44 rounded-[10px] bg-green-300 px-10 py-5 hover:bg-green-600'
className='h-44 w-44 rounded-[10px] bg-green-300 px-10 py-5 hover:bg-green-600'
>
<p className='text-2xl font-bold text-white'>+</p>
</button>
</div>
</div>

{dates.map((dateSlot, idx) => (
<div key={dateSlot.id ?? idx} className='flex'>
<ScheduleSelect
index={idx}
isRemovable={dates.length > 1}
onAddDate={onAddDate}
onRemove={onRemoveDate}
onDateChange={(index, value) => onDateChange(index, 'date', value)}
onStartTimeChange={(index, value) =>
onDateChange(index, 'startTime', value)
}
onEndTimeChange={(index, value) =>
onDateChange(index, 'endTime', value)
}
onDateChange={(index, value) => {
if (isPastDate(value)) {
toast.error('오늘 이전 날짜는 선택할 수 없습니다.');
return;
}
onDateChange(index, 'date', value);
}}
onStartTimeChange={(index, value) => {
const end = dates[index].endTime;
if (end && isInvalidTimeRange(value, end)) {
toast.error('시작 시간은 종료 시간보다 빨라야 합니다.');
return;
}
onDateChange(index, 'startTime', value);
}}
onEndTimeChange={(index, value) => {
const start = dates[index].startTime;
if (start && isInvalidTimeRange(start, value)) {
toast.error('종료 시간은 시작 시간보다 늦어야 합니다.');
return;
}
onDateChange(index, 'endTime', value);
}}
Comment on lines +57 to +79
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick (assertive)

실시간 검증 로직이 사용자 경험을 향상시킵니다.

각 입력 필드에서 즉시 검증을 수행하여 사용자에게 빠른 피드백을 제공하는 것이 좋습니다. 다만 validateSchedules 함수와 검증 로직의 일관성을 유지하는 것을 권장합니다.

중복을 줄이기 위해 isPastDate 로직을 validateSchedules와 통일하는 것을 고려해보세요:

function isPastDate(dateStr: string) {
-  const selected = new Date(dateStr);
-  const today = new Date();
-  today.setHours(0, 0, 0, 0);
-  return selected < today;
+  const selectedDate = new Date(dateStr);
+  const today = new Date();
+  today.setHours(0, 0, 0, 0);
+  return selectedDate < today;
}

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In src/app/(with-header)/myactivity/components/ScheduleSelectForm.tsx between
lines 57 and 79, the onDateChange, onStartTimeChange, and onEndTimeChange
handlers perform immediate validation but use logic that may differ from the
validateSchedules function. To fix this, refactor the validation checks in these
handlers to reuse or align with the validation logic defined in
validateSchedules, especially for the isPastDate check, to ensure consistency
and reduce duplication.

date={dateSlot.date}
startTime={dateSlot.startTime}
endTime={dateSlot.endTime}
/>
</div>
))}

</div>
);
}
31 changes: 31 additions & 0 deletions src/app/(with-header)/myactivity/utils/dateValidatoin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { Schedule } from '@/types/activityDetailType';

export function validateSchedules(schedules: Schedule[]) {
const today = new Date();
today.setHours(0, 0, 0, 0);

for (let i = 0; i < schedules.length; i++) {
const { date, startTime, endTime } = schedules[i];

if (!date || !startTime || !endTime) {
return `날짜와 시간이 모두 입력되어야 합니다!`;
}

const selectedDate = new Date(date);
if (selectedDate < today) {
return `오늘보다 이전 날짜는 선택할 수 없습니다!`;
}

const [startHour, startMinute] = startTime.split(':').map(Number);
const [endHour, endMinute] = endTime.split(':').map(Number);

const startMinutes = startHour * 60 + startMinute;
const endMinutes = endHour * 60 + endMinute;

if (startMinutes >= endMinutes) {
return `종료 시간은 시작 시간보다 늦어야 합니다!`;
}
}

return null;
}