Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
7650de5
refactor: 사물함 신청 현황 테이블의 header와 body 동기화
smb0123 Jun 22, 2025
958478a
chore: getCellValue 함수명 변경
smb0123 Jun 22, 2025
5e399d2
refactor: 엑셀 파일과 신청 목록 테이블 동기화
smb0123 Jun 22, 2025
bbe6041
refactor: 사용자 정보를 하나의 객체로 합치는 로직을 reduce로 변경
smb0123 Jun 22, 2025
0e7d3d6
refactor: 엑셀 파일과 테이블의 데이터를 추출하는 로직을 하나의 함수로
smb0123 Jun 27, 2025
0780f33
refactor: FIELD_MAPPERS 함수 제거 후 getCellValue 함수에 switch 문 적용
smb0123 Jun 28, 2025
b9b816a
feat: switch 문에 default 구문 추가
smb0123 Jun 28, 2025
3acfa36
fix: Button에 disabled 속성이 작동안하는 문제 해결
smb0123 Jun 28, 2025
c1a2325
refactor: 임시 저장 api 응답이 올 때까지 임시저장이 불가능하게 변경
smb0123 Jun 28, 2025
727b2c7
feat: 임시 저장 api 응답이 올 때까지 로딩 화면 보여주기
smb0123 Jun 28, 2025
c075c3c
refactor/283-apply-list 브랜치 충돌 해결
smb0123 Jul 5, 2025
4c3dc1f
refactor: 신청 목록 페이지의 테이블과 엑셀 파일 리팩토링 (#284)
smb0123 Jul 5, 2025
154f71d
feat: Loader 컴포넌트에 size props 추가
smb0123 Jul 5, 2025
5d04ef3
refactor: 임시 저장 시 로딩 화면을 모달에서 Loader로 변경
smb0123 Jul 5, 2025
8b6c6f1
fix: 스피너가 중복으로 표시되는 문제 해결
smb0123 Jul 5, 2025
300a241
refactor: 임시 저장 동작 개선 (#289)
smb0123 Jul 7, 2025
ba2de02
fix: spinner가 중복으로 표시되는 문제 해결 (#292)
smb0123 Jul 7, 2025
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
1 change: 1 addition & 0 deletions design-system/src/lib/button/button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ export function Button({
return (
<button
{...props}
disabled={disabled}
className={clsx(
'rounded-lg transition-all duration-150 ease-in-out',
disabled
Expand Down
7 changes: 3 additions & 4 deletions service-apply/src/assets/spinner.css
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,11 @@
box-sizing: border-box;
display: block;
position: absolute;
left: calc(50% - 40px);
top: calc(50% - 40px);
transform: translate(-50%, -50%);
left: 50%;
top: 50%;
width: 80px;
height: 80px;
margin: 8px;
margin: -40px 0 0 -40px;
border: 8px solid black;
border-radius: 50%;
animation: lds-ring 1.2s 1s cubic-bezier(0.5, 0, 0.5, 1) infinite;
Expand Down
14 changes: 12 additions & 2 deletions service-apply/src/components/apply/ApplyForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { AFFILIATION_LIST } from '../../constants/affiliation';
import { DEPARTMENT_LIST } from '../../constants/department';
import { PrivacyCheckModal } from './PrivacyCheckModal';
import { Spinner } from '../../assets/Spinner';
import Loader from '../common/Loader';

const DEFAULT_PARKING_SECTION_OPTIONS = [
{
Expand Down Expand Up @@ -72,6 +73,7 @@ export const ApplyForm = () => {
setIsAgreed,
isError,
errorMessage,
temporarySaveStatus,
} = useApplyForm();

const parkingSectionOptions = DEFAULT_PARKING_SECTION_OPTIONS.concat(
Expand Down Expand Up @@ -221,8 +223,16 @@ export const ApplyForm = () => {
</Txt>
)}
<div className="flex flex-row justify-between mt-2 mb-12">
<Button color="secondary" onClick={onTemporarySave}>
임시 저장
<Button
color="secondary"
onClick={onTemporarySave}
disabled={temporarySaveStatus}
>
{temporarySaveStatus ? (
<Loader color="#000" size="sm" />
) : (
'임시 저장'
)}
</Button>
<Button
color="primary"
Expand Down
80 changes: 57 additions & 23 deletions service-apply/src/components/common/Loader.css
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
.lds-roller {
/* color: #1c4c5b; */
--loader-size: 80px;
--loader-center: 40px;
--loader-dot-size: 7.2px;
--loader-dot-margin: -3.6px;
}
.lds-roller,
.lds-roller div,
Expand All @@ -9,78 +13,108 @@
.lds-roller {
display: inline-block;
position: relative;
width: 80px;
height: 80px;
}
width: var(--loader-size);
height: var(--loader-size);
}

/* Size variants */
.lds-roller--sm {
--loader-size: 40px;
--loader-center: 20px;
--loader-dot-size: 3.6px;
--loader-dot-margin: -1.8px;
}

.lds-roller--md {
--loader-size: 80px;
--loader-center: 40px;
--loader-dot-size: 7.2px;
--loader-dot-margin: -3.6px;
}

.lds-roller--lg {
--loader-size: 120px;
--loader-center: 60px;
--loader-dot-size: 10.8px;
--loader-dot-margin: -5.4px;
}

.lds-roller--xl {
--loader-size: 160px;
--loader-center: 80px;
--loader-dot-size: 14.4px;
--loader-dot-margin: -7.2px;
}

.lds-roller div {
animation: lds-roller 1.2s cubic-bezier(0.5, 0, 0.5, 1) infinite;
transform-origin: 40px 40px;
transform-origin: var(--loader-center) var(--loader-center);
}
.lds-roller div:after {
content: ' ';
display: block;
position: absolute;
width: 7.2px;
height: 7.2px;
width: var(--loader-dot-size);
height: var(--loader-dot-size);
border-radius: 50%;
background: currentColor;
margin: -3.6px 0 0 -3.6px;
margin: var(--loader-dot-margin) 0 0 var(--loader-dot-margin);
}
.lds-roller div:nth-child(1) {
animation-delay: -0.036s;
}
.lds-roller div:nth-child(1):after {
top: 62.62742px;
left: 62.62742px;
top: calc(var(--loader-center) + var(--loader-center) * 0.5656);
left: calc(var(--loader-center) + var(--loader-center) * 0.5656);
}
.lds-roller div:nth-child(2) {
animation-delay: -0.072s;
}
.lds-roller div:nth-child(2):after {
top: 67.71281px;
left: 56px;
top: calc(var(--loader-center) + var(--loader-center) * 0.6928);
left: calc(var(--loader-center) + var(--loader-center) * 0.4);
}
.lds-roller div:nth-child(3) {
animation-delay: -0.108s;
}
.lds-roller div:nth-child(3):after {
top: 70.90963px;
left: 48.28221px;
top: calc(var(--loader-center) + var(--loader-center) * 0.7726);
left: calc(var(--loader-center) + var(--loader-center) * 0.2071);
}
.lds-roller div:nth-child(4) {
animation-delay: -0.144s;
}
.lds-roller div:nth-child(4):after {
top: 72px;
left: 40px;
top: calc(var(--loader-center) + var(--loader-center) * 0.8);
left: var(--loader-center);
}
.lds-roller div:nth-child(5) {
animation-delay: -0.18s;
}
.lds-roller div:nth-child(5):after {
top: 70.90963px;
left: 31.71779px;
top: calc(var(--loader-center) + var(--loader-center) * 0.7726);
left: calc(var(--loader-center) - var(--loader-center) * 0.2071);
}
.lds-roller div:nth-child(6) {
animation-delay: -0.216s;
}
.lds-roller div:nth-child(6):after {
top: 67.71281px;
left: 24px;
top: calc(var(--loader-center) + var(--loader-center) * 0.6928);
left: calc(var(--loader-center) - var(--loader-center) * 0.4);
}
.lds-roller div:nth-child(7) {
animation-delay: -0.252s;
}
.lds-roller div:nth-child(7):after {
top: 62.62742px;
left: 17.37258px;
top: calc(var(--loader-center) + var(--loader-center) * 0.5656);
left: calc(var(--loader-center) - var(--loader-center) * 0.5656);
}
.lds-roller div:nth-child(8) {
animation-delay: -0.288s;
}
.lds-roller div:nth-child(8):after {
top: 56px;
left: 12.28719px;
top: calc(var(--loader-center) + var(--loader-center) * 0.4);
left: calc(var(--loader-center) - var(--loader-center) * 0.6928);
}
@keyframes lds-roller {
0% {
Expand Down
8 changes: 6 additions & 2 deletions service-apply/src/components/common/Loader.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,15 @@ import './Loader.css';

interface LoaderProps {
color?: string;
size?: 'sm' | 'md' | 'lg' | 'xl';
}

const Loader = ({ color = '#FFF' }: LoaderProps) => {
const Loader = ({ color = '#FFF', size = 'md' }: LoaderProps) => {
return (
<div className={`lds-roller text-[${color}]`}>
<div
className={`lds-roller lds-roller--${size}`}
style={{ color } as React.CSSProperties}
>
<div />
<div />
<div />
Expand Down
3 changes: 2 additions & 1 deletion service-apply/src/hooks/apply/useApplyForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export const useApplyForm = () => {
});
}, [registrationData]);

const { postTemporarySave } = useTemporarySaveMutate();
const { postTemporarySave, temporarySaveStatus } = useTemporarySaveMutate();
const [isCaptchaModalOpen, setIsCaptchaModalOpen] = useState(false);
const [isPrivacyModalOpen, setIsPrivacyModalOpen] = useState(false);
const [isAgreed, setIsAgreed] = useState(false);
Expand Down Expand Up @@ -114,5 +114,6 @@ export const useApplyForm = () => {
setIsAgreed,
isError,
errorMessage,
temporarySaveStatus,
};
};
3 changes: 2 additions & 1 deletion service-apply/src/hooks/react-query/useApply.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export const useApplyMutate = () => {
};

export const useTemporarySaveMutate = () => {
const { mutate } = useMutation({
const { mutate, isPending } = useMutation({
mutationKey: ['applyTemporarySave'],
mutationFn: postTemporarySave,
});
Expand All @@ -72,6 +72,7 @@ export const useTemporarySaveMutate = () => {
},
});
},
temporarySaveStatus: isPending,
};
};

Expand Down
56 changes: 23 additions & 33 deletions service-manager/src/components/apply-list/ApplyList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import {
} from '../../hooks/react-query/useRegistration';
import { useSectorQueryById } from '../../hooks/react-query/useSetting';
import { ApplyCount } from './ApplyCount';
import { EXCEL_HEADERS, TABLE_HEADERS } from '../../constants/apply';
import { getExcelCellValue, getTableCellValue } from '../../functions/apply';
import ErrorBoundary from '../common/ErrorBoundary';

interface ApplyListProps {
Expand All @@ -30,17 +32,15 @@ export const ApplyList = ({ eventId }: ApplyListProps) => {
.filter(
(registration) => registration.sectorNum === sector.sectorNumber,
)
.map((registration, index) => ({
구간: registration.sectorNum,
순서: index + 1,
이름: registration.name,
학과: registration.department,
차량번호: registration.carNumber,
학생번호: registration.studentNumber,
경차여부: registration.isCompact ? '경차' : '경차 아님',
휴대폰번호: registration.phoneNumber,
이메일: registration.email,
})),
.map((registration) =>
EXCEL_HEADERS.reduce(
(acc, header) => ({
...acc,
...getExcelCellValue(header, registration, registrations),
}),
{},
),
),
)
.flat();

Expand Down Expand Up @@ -91,15 +91,9 @@ export const ApplyList = ({ eventId }: ApplyListProps) => {
<table className="w-full min-w-[50rem]">
<thead>
<tr>
<th>순서</th>
<th>이름</th>
<th>단과 대학</th>
<th>학과</th>
<th>차량 번호</th>
<th>학생 번호</th>
<th>경차 여부</th>
<th>휴대폰 번호</th>
<th>이메일</th>
{TABLE_HEADERS.map((header) => (
<th key={header.key}>{header.label}</th>
))}
</tr>
</thead>
<tbody className="text-center">
Expand All @@ -110,19 +104,15 @@ export const ApplyList = ({ eventId }: ApplyListProps) => {
.map((registration) => {
return (
<tr key={registration.id}>
<td>
{registrations.findIndex(
(data) => data.id === registration.id,
) + 1}
</td>
<td>{registration.name}</td>
<td>{registration.affiliation}</td>
<td> {registration.department} </td>
<td>{registration.carNumber}</td>
<td>{registration.studentNumber}</td>
<td>{registration.isCompact ? '경차' : '경차 아님'}</td>
<td>{registration.phoneNumber}</td>
<td>{registration.email}</td>
{TABLE_HEADERS.map((header) => (
<td key={header.key}>
{getTableCellValue(
header.key,
registration,
registrations,
)}
</td>
))}
</tr>
);
})}
Expand Down
18 changes: 18 additions & 0 deletions service-manager/src/constants/apply.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { RegistrationResponse } from '../apis/dtos/registration.dto';

export const TABLE_HEADERS = [
{ key: 'order', label: '순서' },
{ key: 'name', label: '이름' },
{ key: 'affiliation', label: '단과 대학' },
{ key: 'department', label: '학과' },
{ key: 'carNumber', label: '차량 번호' },
{ key: 'studentNumber', label: '학생 번호' },
{ key: 'isCompact', label: '경차 여부' },
{ key: 'phoneNumber', label: '휴대폰 번호' },
{ key: 'email', label: '이메일' },
] as const;

export const EXCEL_HEADERS = [
{ key: 'sector', label: '구간' },
...TABLE_HEADERS,
] as const;
Loading