Skip to content
Closed
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
6 changes: 4 additions & 2 deletions src/api/articles/queries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import {
getSingleLostItemArticle,
} from './index';

type ChatroomParamId = number | string | null;

type LostItemInfiniteListParams = Omit<LostItemArticlesRequest, 'page'>;

type LostItemSearchParams = Required<Pick<SearchLostItemArticleRequest, 'query'>> & {
Expand All @@ -37,9 +39,9 @@ export const articleQueryKeys = {
lostItemStat: ['lostItem', 'stat'] as const,
lostItemChatroomAll: ['chatroom', 'lost-item'] as const,
lostItemChatroomList: ['chatroom', 'lost-item', 'list'] as const,
lostItemChatroomDetail: (articleId: number | string | null, chatroomId: number | string | null) =>
lostItemChatroomDetail: (articleId: ChatroomParamId, chatroomId: ChatroomParamId) =>
['chatroom', 'lost-item', 'detail', articleId, chatroomId] as const,
lostItemChatroomMessages: (articleId: number | string | null, chatroomId: number | string | null) =>
lostItemChatroomMessages: (articleId: ChatroomParamId, chatroomId: ChatroomParamId) =>
['chatroom', 'lost-item', 'messages', articleId, chatroomId] as const,
};

Expand Down
4 changes: 2 additions & 2 deletions src/api/bus/queries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -83,8 +83,8 @@ export const busQueries = {
? () =>
getBusRouteInfo({
...rest,
depart: depart as Depart,
arrival: arrival as Arrival,
depart,
arrival,
})
: skipToken,
});
Expand Down
9 changes: 7 additions & 2 deletions src/components/Callvan/components/AddPostForm/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,13 @@ interface FormState {
maxParticipants: number;
}

function convertTo24Hour(hour: number, isPM: boolean): number {
if (isPM) return hour === 12 ? 12 : hour + 12;
return hour === 12 ? 0 : hour;
}

function formatTime(hour: number, minute: number, isPM: boolean): string {
const hour24 = isPM ? (hour === 12 ? 12 : hour + 12) : hour === 12 ? 0 : hour;
const hour24 = convertTo24Hour(hour, isPM);
return `${String(hour24).padStart(2, '0')}:${String(minute).padStart(2, '0')}`;
}

Expand Down Expand Up @@ -91,7 +96,7 @@ export default function AddPostForm() {
if (!form.departureType || !form.arrivalType || isPending) return;

const selectedDateTime = new Date(form.departureDate);
const hour24 = form.isPM ? (form.departureHour === 12 ? 12 : form.departureHour + 12) : form.departureHour === 12 ? 0 : form.departureHour;
const hour24 = convertTo24Hour(form.departureHour, form.isPM);
selectedDateTime.setHours(hour24, form.departureMinute, 0, 0);

if (selectedDateTime < new Date()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ export default function CallvanPageLayout({
departures: filter.departures.length > 0 ? filter.departures.join(',') : undefined,
arrivals: filter.arrivals.length > 0 ? filter.arrivals.join(',') : undefined,
sort: filter.sort !== 'LATEST_DESC' ? filter.sort : undefined,
author: filter.author !== 'ALL' ? filter.author : undefined,
author: filter.author === 'ALL' ? undefined : filter.author,
joined: filter.joined ? 'true' : undefined,
page: undefined,
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import { useBodyScrollLock } from 'utils/hooks/ui/useBodyScrollLock';
import styles from './CallvanRestrictionModal.module.scss';

interface CallvanRestrictionModalProps {
restriction: RestrictedCallvanResponse | null;
onClose: () => void;
readonly restriction: RestrictedCallvanResponse | null;
readonly onClose: () => void;
}

export default function CallvanRestrictionModal({ restriction, onClose }: CallvanRestrictionModalProps) {
Expand All @@ -29,7 +29,7 @@ export default function CallvanRestrictionModal({ restriction, onClose }: Callva
}, [onClose]);

return (
<div className={styles.modal__overlay} role="dialog" aria-modal="true" aria-labelledby="callvan-restriction-title">
<dialog className={styles.modal__overlay} open aria-modal="true" aria-labelledby="callvan-restriction-title">
<button type="button" className={styles.modal__dim} onClick={onClose} aria-label="닫기" />
<div className={styles.modal__sheet}>
<div className={styles.modal__content}>
Expand All @@ -49,6 +49,6 @@ export default function CallvanRestrictionModal({ restriction, onClose }: Callva
</button>
</div>
</div>
</div>
</dialog>
);
}
3 changes: 2 additions & 1 deletion src/components/Callvan/utils/callvanQuery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@ export function parseCallvanQuery(query: ParsedUrlQuery, fallback: CallvanParams
const author = isCallvanAuthor(rawAuthor) ? rawAuthor : fallback.author;

const rawJoined = parseStringParam(query, 'joined');
const joined = rawJoined === 'true' ? true : rawJoined === 'false' ? false : fallback.joined;
const joinedFromQuery = rawJoined === 'true' ? true : rawJoined === 'false' ? false : null;
const joined = joinedFromQuery !== null ? joinedFromQuery : fallback.joined;

const rawStatuses = parseArrayParam(query, 'statuses');
const validStatuses = rawStatuses.filter(isCallvanStatus);
Expand Down
4 changes: 2 additions & 2 deletions src/components/CampusInfo/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,10 @@ const formatDateRange = (fromDate: string, toDate: string) => {
return `기간 : ${fromFormatted} - ${toFormatted}`;
};

type ShopIconProps = {
type ShopIconProps = Readonly<{
iconUrl: string | null | undefined;
name: string;
};
}>;

function ShopIcon({ iconUrl, name }: ShopIconProps) {
return (
Expand Down
59 changes: 25 additions & 34 deletions src/components/TimetablePage/components/MainTimetable/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,28 @@ import DownloadTimetableModal from './DownloadTimetableModal';
import styles from './MyLectureTimetable.module.scss';

interface MainTimetableLayoutProps {
curriculum: React.ReactNode;
myLectures: Lecture[] | MyLectureInfo[] | undefined;
onClickDownloadImage: (e: React.MouseEvent<HTMLButtonElement>) => void;
onClickEdit: () => void;
onClickGraduation: () => void;
timetableContent: React.ReactNode;
footer?: React.ReactNode;
readonly curriculum: React.ReactNode;
readonly myLectures: Lecture[] | MyLectureInfo[] | undefined;
readonly onClickDownloadImage: (e: React.MouseEvent<HTMLButtonElement>) => void;
readonly onClickEdit: () => void;
readonly onClickGraduation: () => void;
readonly timetableContent: React.ReactNode;
readonly footer?: React.ReactNode;
}

function validateSemesterAndTimetable(
mySemester: { semesters: unknown[] } | undefined | null,
timeTableFrameList: Array<{ id: number | null }>,
): boolean {
if (mySemester?.semesters.length === 0) {
toast.error('학기가 존재하지 않습니다. 학기를 추가해주세요.');
return false;
}
if (!timeTableFrameList.some((frame) => isValidTimetableFrameId(frame.id))) {
toast.error('시간표가 존재하지 않습니다. 시간표를 추가해주세요.');
return false;
}
return true;
}

function MainTimetableLayout({
Expand Down Expand Up @@ -79,19 +94,7 @@ function InvalidMainTimetable() {
const { data: deptList } = useSuspenseQuery(deptQueries.list());
const { data: mySemester } = useSemesterCheck(token);

const isSemesterAndTimetableExist = () => {
if (mySemester?.semesters.length === 0) {
toast.error('학기가 존재하지 않습니다. 학기를 추가해주세요.');
return false;
}

if (!timeTableFrameList.some((frame) => isValidTimetableFrameId(frame.id))) {
toast.error('시간표가 존재하지 않습니다. 시간표를 추가해주세요.');
return false;
}

return true;
};
const isSemesterAndTimetableExist = () => validateSemesterAndTimetable(mySemester, timeTableFrameList);

const onClickDownloadImage = (e: React.MouseEvent<HTMLButtonElement>) => {
e.stopPropagation();
Expand Down Expand Up @@ -121,7 +124,7 @@ function InvalidMainTimetable() {
);
}

function ValidMainTimetable({ timetableFrameId }: { timetableFrameId: number }) {
function ValidMainTimetable({ timetableFrameId }: Readonly<{ timetableFrameId: number }>) {
const [isModalOpen, openModal, closeModal] = useBooleanState(false);
const token = useTokenState();
const semester = useSemester();
Expand All @@ -132,19 +135,7 @@ function ValidMainTimetable({ timetableFrameId }: { timetableFrameId: number })
const { data: deptList } = useSuspenseQuery(deptQueries.list());
const { data: mySemester } = useSemesterCheck(token);

const isSemesterAndTimetableExist = () => {
if (mySemester?.semesters.length === 0) {
toast.error('학기가 존재하지 않습니다. 학기를 추가해주세요.');
return false;
}

if (!timeTableFrameList.some((frame) => isValidTimetableFrameId(frame.id))) {
toast.error('시간표가 존재하지 않습니다. 시간표를 추가해주세요.');
return false;
}

return true;
};
const isSemesterAndTimetableExist = () => validateSemesterAndTimetable(mySemester, timeTableFrameList);

const onClickDownloadImage = (e: React.MouseEvent<HTMLButtonElement>) => {
e.stopPropagation();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@ const DEFAULT_TIME_STRING = ['9', '10', '11', '12', '13', '14', '15', '16', '17'
]);

interface TimetableGridPlaceholderProps {
firstColumnWidth: number;
columnWidth: number;
rowHeight: number;
totalHeight: number;
readonly firstColumnWidth: number;
readonly columnWidth: number;
readonly rowHeight: number;
readonly totalHeight: number;
}

export default function TimetableGridPlaceholder({
Expand Down
2 changes: 1 addition & 1 deletion src/pages/room/[id]/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import {
import styles from './RoomDetailPage.module.scss';

interface RoomDetailPageProps {
id: string;
readonly id: string;
}

export const getStaticPaths: GetStaticPaths = async () => {
Expand Down
65 changes: 37 additions & 28 deletions src/pages/timetable/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,41 @@ const MobilePage = dynamic(
{ ssr: true },
);

async function prefetchTimetableWithToken(
queryClient: QueryClient,
token: string,
year: number,
term: Term,
validatedFrameId: number | null,
context: GetServerSidePropsContext,
) {
try {
const mySemesterData = await queryClient.fetchQuery(timetableQueries.mySemester(token));
const userSemester = mySemesterData?.semesters?.[0];
const semester = year && term ? { year, term } : userSemester || getRecentSemester();

const timetableFrameList = await queryClient.fetchQuery(timetableQueries.frameList(token, semester));

const mainFrame = timetableFrameList.find((frame) => frame.is_main);
const currentFrameId = validatedFrameId ?? mainFrame?.id ?? null;

const prefetchPromises = [
queryClient.prefetchQuery(timetableQueries.semesterInfo()),
queryClient.prefetchQuery(deptQueries.list()),
];

if (currentFrameId !== null) {
prefetchPromises.push(queryClient.prefetchQuery(timetableQueries.lectureInfo(token, currentFrameId)));
}

await Promise.all(prefetchPromises);
} catch (error) {
if (!isServerAuthError(error) && !(isKoinError(error) && error.status === 403)) throw error;
if (isServerAuthError(error)) clearServerAuthCookies(context);
queryClient.setQueryData(timetableQueryKeys.frameList(getRecentSemester()), createDefaultTimetableFrameList());
}
}

export const getServerSideProps = withCacheControl(async (context: GetServerSidePropsContext, cacheControl) => {
const queryClient = new QueryClient();
const { token, query } = parseServerSideParams(context);
Expand All @@ -39,35 +74,9 @@ export const getServerSideProps = withCacheControl(async (context: GetServerSide
const validatedFrameId = isValidTimetableFrameId(frameId) ? frameId : null;

if (token) {
try {
const mySemesterData = await queryClient.fetchQuery(timetableQueries.mySemester(token));
const userSemester = mySemesterData?.semesters?.[0];
const semester = year && term ? { year, term } : userSemester || getRecentSemester();

const timetableFrameList = await queryClient.fetchQuery(timetableQueries.frameList(token, semester));

const mainFrame = timetableFrameList.find((frame) => frame.is_main);
const currentFrameId = validatedFrameId ?? mainFrame?.id ?? null;

const prefetchPromises = [
queryClient.prefetchQuery(timetableQueries.semesterInfo()),
queryClient.prefetchQuery(deptQueries.list()),
];

if (currentFrameId !== null) {
prefetchPromises.push(queryClient.prefetchQuery(timetableQueries.lectureInfo(token, currentFrameId)));
}

await Promise.all(prefetchPromises);
} catch (error) {
if (!isServerAuthError(error) && !(isKoinError(error) && error.status === 403)) throw error;
if (isServerAuthError(error)) clearServerAuthCookies(context);
const semester = getRecentSemester();
queryClient.setQueryData(timetableQueryKeys.frameList(semester), createDefaultTimetableFrameList());
}
await prefetchTimetableWithToken(queryClient, token, year, term, validatedFrameId, context);
} else {
const semester = getRecentSemester();
queryClient.setQueryData(timetableQueryKeys.frameList(semester), createDefaultTimetableFrameList());
queryClient.setQueryData(timetableQueryKeys.frameList(getRecentSemester()), createDefaultTimetableFrameList());
}

if (!token) {
Expand Down
30 changes: 13 additions & 17 deletions src/utils/ts/withCacheControl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import type {
GetServerSidePropsResult,
PreviewData,
} from 'next';
import type { ParsedUrlQuery } from 'querystring';
import type { ParsedUrlQuery } from 'node:querystring';

export const PUBLIC_SSR_CACHE_CONTROL = 'public, s-maxage=60, stale-while-revalidate=300';
export const STORE_PUBLIC_SSR_CACHE_CONTROL = 'public, s-maxage=300, stale-while-revalidate=1800';
Expand All @@ -16,26 +16,22 @@ export interface SSRCacheControl {
enablePublicCache: (cacheControl?: string) => void;
}

export interface GetServerSidePropsWithCacheControl<
export type GetServerSidePropsWithCacheControl<
Props extends SSRPageProps = SSRPageProps,
Params extends ParsedUrlQuery = ParsedUrlQuery,
Preview extends PreviewData = PreviewData,
> {
(
context: GetServerSidePropsContext<Params, Preview>,
cacheControl: SSRCacheControl,
): Promise<GetServerSidePropsResult<Props>>;
}
> = (
context: GetServerSidePropsContext<Params, Preview>,
cacheControl: SSRCacheControl,
) => Promise<GetServerSidePropsResult<Props>>;

export interface WithCacheControl {
<
Props extends SSRPageProps = SSRPageProps,
Params extends ParsedUrlQuery = ParsedUrlQuery,
Preview extends PreviewData = PreviewData,
>(
getServerSideProps: GetServerSidePropsWithCacheControl<Props, Params, Preview>,
): GetServerSideProps<Props, Params, Preview>;
}
export type WithCacheControl = <
Props extends SSRPageProps = SSRPageProps,
Params extends ParsedUrlQuery = ParsedUrlQuery,
Preview extends PreviewData = PreviewData,
>(
getServerSideProps: GetServerSidePropsWithCacheControl<Props, Params, Preview>,
) => GetServerSideProps<Props, Params, Preview>;

export const withCacheControl: WithCacheControl = (getServerSideProps) => async (context) => {
let shouldCachePublicResponse = false;
Expand Down
Loading