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
3 changes: 3 additions & 0 deletions prisma/seed/regions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ export async function seedRegions(prisma: PrismaClient): Promise<void> {
sort_order: group.sortOrder,
},
update: {
level: 1,
parent_id: null,
name: group.name,
sort_order: group.sortOrder,
is_active: true,
Expand All @@ -65,6 +67,7 @@ export async function seedRegions(prisma: PrismaClient): Promise<void> {
parent_id: parentId,
},
update: {
level: 2,
name: child.name,
sort_order: child.sortOrder,
parent_id: parentId,
Expand Down
1 change: 1 addition & 0 deletions src/features/pickup/constants/pickup-error-messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ export const PICKUP_ERRORS = {
export const PICKUP_DAY_REASON = {
PAST: 'PAST',
OUT_OF_RANGE: 'OUT_OF_RANGE',
CLOSED: 'CLOSED', // 당일이지만 현재시각+리드타임으로 가용 슬롯이 없음
} as const;
10 changes: 10 additions & 0 deletions src/features/pickup/services/pickup-slot.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,16 @@ describe('PickupSlotService', () => {
reason: 'OUT_OF_RANGE',
});
});

it('오늘이지만 가용 슬롯이 없으면 CLOSED로 막는다', () => {
// KST 19:00 + 리드 60분 = cutoff 20:00 > 마지막 슬롯 19:30 → 당일 슬롯 없음
const now = new Date('2026-06-18T10:00:00.000Z'); // KST 06-18 19:00
const calendar = service.pickupCalendar('2026-06', now);
expect(calendar.days.find((d) => d.date === '2026-06-18')).toMatchObject({
selectable: false,
reason: 'CLOSED',
});
});
});

describe('pickupTimeSlots', () => {
Expand Down
13 changes: 13 additions & 0 deletions src/features/pickup/services/pickup-slot.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,12 @@ export class PickupSlotService {

const daysInMonth = new Date(Date.UTC(ym.year, ym.month, 0)).getUTCDate();

// 오늘은 현재시각+리드타임 이후 가용 슬롯이 하나라도 있어야 선택 가능
// (없으면 pickupTimeSlots가 전부 마감 → 캘린더도 선택 불가로 일치시킨다)
const cutoffMinutes = kstMinutesOfDay(now) + PICKUP_MIN_LEAD_MINUTES;
const lastSlotMinutes = PICKUP_CLOSE_MINUTES - PICKUP_SLOT_INTERVAL_MINUTES;
const todayHasSlot = cutoffMinutes <= lastSlotMinutes;

const days = Array.from({ length: daysInMonth }, (_, index) => {
const day = index + 1;
const date = kstMidnightUtc(ym.year, ym.month, day);
Expand All @@ -60,6 +66,13 @@ export class PickupSlotService {
reason: PICKUP_DAY_REASON.OUT_OF_RANGE,
};
}
if (diff === 0 && !todayHasSlot) {
return {
date: formatKstDate(date),
selectable: false,
reason: PICKUP_DAY_REASON.CLOSED,
};
}
return { date: formatKstDate(date), selectable: true, reason: null };
});

Expand Down
6 changes: 6 additions & 0 deletions src/features/region/dto/inputs/search-regions.input.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,10 @@ describe('SearchRegionsInput', () => {
const errors = await validate(build({ keyword: 'a', limit: 1.5 }));
expect(errors[0].property).toBe('limit');
});

it('keyword 최대 길이 경계(80 통과, 81 거절)', async () => {
expect(await validate(build({ keyword: 'a'.repeat(80) }))).toHaveLength(0);
const errors = await validate(build({ keyword: 'a'.repeat(81) }));
expect(errors[0].property).toBe('keyword');
});
});
Loading