Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
a2edcd5
feat : add asset to implement error UI
yummjin Jun 24, 2025
8115a8d
feat : implement data fetching logic for notice content
yummjin Jun 24, 2025
579c163
feat : separate content from `ContentContainer` component and display…
yummjin Jun 24, 2025
0226634
feat : add types to notice and add colors for each types
yummjin Jun 24, 2025
f358906
feat : implement fetching notices by campus
yummjin Jun 24, 2025
23932d9
remove : remove mock data
yummjin Jun 24, 2025
248ee98
feat : implement notice item reflects its color by type properly
yummjin Jun 24, 2025
7f640fb
feat : implement `NoticeList` with UI for each status
yummjin Jun 24, 2025
030e83e
remove : remove unused types
yummjin Jun 24, 2025
4b72037
refactor : add default value to property of `Card` component
yummjin Jun 24, 2025
9e2d79f
feat : send different request by campus
yummjin Jun 24, 2025
f1473b2
feat : implement logic fetching elections
yummjin Jun 24, 2025
29ead09
refactor : change parameter type of `NoticeContentScreen`
yummjin Jun 24, 2025
8765f81
feat : add request endpoints
yummjin Jun 24, 2025
1cfa903
feat : implement type of `campus`, `election`
yummjin Jun 24, 2025
1609aa9
fix : change base url of user axios instances
yummjin Jun 24, 2025
3f11f6b
feat : implement `HomeScreen` with conditional rendering
yummjin Jun 24, 2025
3ff03f6
chore : add `dayjs` to use date strings
yummjin Jun 24, 2025
b245c12
feat : make indicator reflects real data
yummjin Jun 24, 2025
2f7f359
feat : change type of `VoteScreen` to display real data
yummjin Jun 24, 2025
34feade
feat : add constants for campus name into korean
yummjin Jun 24, 2025
d58cb27
feat : implement `getDate` function to stringfy dates
yummjin Jun 24, 2025
8840915
feat : add type of `Candidate` and `Nominee`
yummjin Jun 24, 2025
a2b9ecc
feat : implement query hooks for fetch candidate and nominee
yummjin Jun 24, 2025
da33333
feat : add endpoints for candidate and nominee
yummjin Jun 24, 2025
3f7ef11
feat : add functionalities to `Card` component to render real data
yummjin Jun 24, 2025
db47eb5
feat : add logics to `VoteContainer` and `VoteItem` render real data
yummjin Jun 24, 2025
869d4d1
feat : implement logic fetches user information
yummjin Jun 24, 2025
7c61889
feat : fetch user info at entry point
yummjin Jun 24, 2025
41fdc6c
feat : make conditional rendering logic refer to user role
yummjin Jun 24, 2025
2a59983
feat : add request and types
yummjin Jun 24, 2025
b8d15be
fix : remove user logic from home, handle user logic at `AuthScreen`
yummjin Jun 24, 2025
0660f45
feat : make `VotePromiseScreen` receive candidate and nominee data fr…
yummjin Jun 24, 2025
a65e517
feat : make `VoteContainer` to send data to promise(pledge) screen
yummjin Jun 24, 2025
8428872
feat : send data to UI pieces
yummjin Jun 24, 2025
4278842
refactor : make query stricter with adding `enabled` property into it
yummjin Jun 24, 2025
ddc9fef
feat : define `Pledge` type
yummjin Jun 24, 2025
8068e10
feat : add endpoint of fetching pledge
yummjin Jun 24, 2025
3913396
feat : implement logic fetches pledge and never fetch again(caching)
yummjin Jun 24, 2025
5c57879
feat : connect data with UI
yummjin Jun 24, 2025
3b891e2
fix : fix type missmatch at admin page due to the change of `Card` co…
yummjin Jun 25, 2025
4de5096
fix : change import of type and define type on page
yummjin Jun 25, 2025
6b759f5
fix : fix lint error by annotating unused component
yummjin Jun 25, 2025
bbb185d
fix : fix type missmatch as changing value
yummjin Jun 25, 2025
905dd75
fix : add import of used data
yummjin Jun 25, 2025
e866641
fix : add id temporarily to fix CI error
yummjin Jun 25, 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 package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
"axios": "^1.10.0",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"dayjs": "^1.11.13",
"framer-motion": "^12.12.1",
"jotai": "^2.12.4",
"lodash": "^4.17.21",
Expand Down
8 changes: 8 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

21 changes: 15 additions & 6 deletions src/app/stackflow/Stack.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,21 @@ export const { Stack, useFlow } = stackflow({
],
initialActivity: () => {
if (fetchLoginStatus()) {
if (sessionStorage.getItem('userMode')) {
const mode = JSON.parse(sessionStorage.getItem('userMode')!).mode;
const userModeRaw = sessionStorage.getItem('userMode');
if (userModeRaw) {
const mode = JSON.parse(userModeRaw).mode;
if (mode === 'STUDENT') return 'HomeScreen';
else if (mode === 'ADMIN') return 'AdminHomeScreen';
else return 'LoginScreen';
} else return 'LoginScreen';
} else return 'LoginScreen';
if (mode === 'ADMIN') return 'AdminHomeScreen';
}

const userInfoRaw = sessionStorage.getItem('userInfo');
if (userInfoRaw) {
const role = JSON.parse(userInfoRaw).role;
if (role === 'ROLE_USER') return 'HomeScreen';
if (role === 'ROLE_ADMIN') return 'AdminHomeScreen';
}
}

return 'LoginScreen';
},
});
Binary file added src/assets/icon/icon-alert.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions src/assets/icon/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import Alert from './icon-alert.png';
import Character from './character.svg';
import CharacterComplete from './character-complete.png';
import CharacterFlat from './character-flat.png';
Expand All @@ -20,6 +21,7 @@ import VoteIcon from './icon-vote.svg';
import WriteIcon from './icon-write.svg';

export {
Alert,
BackButton,
Character,
Character2,
Expand Down
10 changes: 6 additions & 4 deletions src/features/admin-dashboard/ui/CardList.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
import type { VoteStatus } from '@/shared/types';
import { Card } from '@/shared/ui';
import { VOTE_MOCK } from '../mock';
// import { Card } from '@/shared/ui';
import { VOTE_MOCK } from '@/features/admin-dashboard/mock';

export default function CardList({ status }: { status: VoteStatus }) {
const data = VOTE_MOCK.filter(
({ status: voteStatus }) => voteStatus === status,
);
console.log(data);

return (
<div className="scrollbar-hide px-normal flex size-full flex-1 flex-col gap-y-[26px] overflow-scroll">
{data.map(({ id, campus, status, title, date }, index) => (
{/* {data.map(({ id, campus, status, title, date }, index) => (
<Card
className={index === 0 ? 'mt-8' : ''}
key={id}
Expand All @@ -17,7 +19,7 @@ export default function CardList({ status }: { status: VoteStatus }) {
title={title}
date={date}
/>
))}
))} */}
</div>
);
}
29 changes: 29 additions & 0 deletions src/features/home/api/election.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { REQUEST, userGet } from '@/shared/api';
import type { Election } from '@/shared/types';
import { useQuery } from '@tanstack/react-query';

interface ElectionResponse {
status: {
code: number;
message: string;
};
metadata: {
resultCount: number;
};
results: Election[];
}

const fetchAllElections = async () => {
const response = await userGet<ElectionResponse>({
request: REQUEST.ELECTION_ALL,
});
if (response.data.metadata.resultCount === 0) return [];
return response.data.results;
};

export const useFetchAllElections = () => {
return useQuery({
queryKey: ['elections'],
queryFn: fetchAllElections,
});
};
1 change: 1 addition & 0 deletions src/features/home/api/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './election';
8 changes: 7 additions & 1 deletion src/features/home/mock/card.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import type { CardProps } from '@/features/home/types';
type CardProps = {
id: number;
title: string;
campus: string;
status: string;
date: string;
}

export const CARD_MOCK: CardProps[] = [
{
Expand Down
7 changes: 0 additions & 7 deletions src/features/home/types/card.ts

This file was deleted.

1 change: 0 additions & 1 deletion src/features/home/types/index.ts

This file was deleted.

26 changes: 26 additions & 0 deletions src/features/home/ui/CardSection.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { useFetchAllElections } from '../api';
import CardStack from './CardStack';

export default function CardSection() {
const { data, isFetching, isError } = useFetchAllElections();
if (isFetching) return <></>;
if (isError) return <></>;
if (data && data.length === 0)
return (
<>
<p className="mb-8 text-2xl font-semibold">진행중인 선거가 없어요</p>
<div className="shadow-card flex h-fit w-full cursor-pointer flex-col rounded-lg bg-white p-6">
<div className="flex w-full justify-between">투표가 없어여</div>
</div>
</>
);
if (data)
return (
<>
<p className="mb-8 text-2xl font-semibold">진행중인 선거가 있어요!</p>
<CardStack data={data} />
</>
);

return <></>;
}
20 changes: 10 additions & 10 deletions src/features/home/ui/CardStack.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@ import { useRef, useState } from 'react';
import { animate, motion } from 'framer-motion';

import { Card } from '@/shared/ui';
import type { CardProps } from '@/features/home/types';
import { CARD_MOCK } from '@/features/home/mock';
import type { Election } from '@/shared/types';
import { getDate } from '@/shared/utils';

export default function CardStack() {
const [stack, setStack] = useState<CardProps[]>(CARD_MOCK);
const [passedCards, setPassedCards] = useState<CardProps[]>([]);
export default function CardStack({ data }: { data: Election[] }) {
const [stack, setStack] = useState<Election[]>(data);
const [passedCards, setPassedCards] = useState<Election[]>([]);
const cardRefs = useRef<{ [key: number]: HTMLDivElement | null }>({});
const currentIndex = CARD_MOCK.length - stack.length;
const currentIndex = data.length - stack.length;

return (
<>
Expand Down Expand Up @@ -47,7 +47,7 @@ export default function CardStack() {
{ x: 0 },
{ type: 'spring', stiffness: 300 },
);
setStack(CARD_MOCK);
setStack(data);
setPassedCards([]);
} else if (stack.length !== 0) {
setPassedCards(prev => [...prev, stack[0]]);
Expand Down Expand Up @@ -94,17 +94,17 @@ export default function CardStack() {
>
<Card
isStackCard
id={card.id}
campus={card.campus}
status={card.status}
title={card.title}
date={card.date}
date={`${getDate(card.startAt, 'YYYY.MM.DD')} - ${getDate(card.endAt, 'YYYY.MM.DD')}`}
/>
</motion.div>
);
})}
</div>
<div className="mt-[14px] flex w-full justify-center gap-[6px]">
{CARD_MOCK.map((_, idx) => (
{data.map((_, idx) => (
<div
key={idx}
className={`h-[6px] rounded-full transition-colors ${
Expand Down
1 change: 1 addition & 0 deletions src/features/home/ui/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export { default as CardStack } from './CardStack';
export { default as CardSection } from './CardSection';
23 changes: 23 additions & 0 deletions src/features/notice-content/api/content.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { REQUEST, userGet } from '@/shared/api';
import { useQuery } from '@tanstack/react-query';

interface NoticeResponse {
id: number;
title: string;
content: string;
createdAt: string;
}

const fetchNoticeContent = async (id: number) => {
const response = await userGet<NoticeResponse>({
request: REQUEST.NOTICE + `${id}`,
});
return response.data;
};

export const useFetchNoticeContent = (id: number) => {
return useQuery({
queryKey: [`${id}`, 'notice-content'],
queryFn: () => fetchNoticeContent(id),
});
};
1 change: 1 addition & 0 deletions src/features/notice-content/api/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './content';
17 changes: 17 additions & 0 deletions src/features/notice-content/ui/NoticeContentSection.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { useFetchNoticeContent } from '../api';

export default function NoticeContentSection({ id }: { id: number }) {
const { data } = useFetchNoticeContent(id);
if (data)
return (
<div className="text-s flex flex-col gap-1 font-normal">
{data.content.split('\n').map((line, index) => (
<div key={index} className="mb-1">
{line}
</div>
))}
</div>
);

return <></>;
}
1 change: 1 addition & 0 deletions src/features/notice-content/ui/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default as NoticeContentSection } from './NoticeContentSection';
File renamed without changes.
21 changes: 21 additions & 0 deletions src/features/notice/api/notice.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { useQuery } from '@tanstack/react-query';

import { REQUEST, userGet } from '@/shared/api';
import type { Campus } from '@/shared/types';
import type { NoticeList } from '../types';

const fetchNoticeByCampus = async (campus: Campus) => {
const response = await userGet<NoticeList[]>({
request: REQUEST.NOTICE_CAMPUS + campus,
});
return response.data;
};

export const useFetchNoticeByCampus = (campus: Campus) => {
return useQuery({
queryKey: ['notices', campus],
queryFn: () => fetchNoticeByCampus(campus),
staleTime: 1000 * 60 * 5,
retry: 1,
});
};
1 change: 1 addition & 0 deletions src/features/notice/constants/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './status';
22 changes: 22 additions & 0 deletions src/features/notice/constants/status.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import type { NoticeListType } from '../types';

type StatusStyle = {
label: string;
bgColor: string;
color: string;
};

export const NOTICE_STATUS: Record<NoticeListType, StatusStyle> = {
COMPLETED: { label: '종료', bgColor: '#F5F5F5', color: '#999' },
NOTIFY: {
label: '알림',
bgColor: 'rgba(255, 95, 95, 0.10)',
color: '#FA4545',
},
ONGOING: {
label: '진행',
bgColor: 'rgba(50, 205, 50, 0.10)',
color: '#32CD32',
},
UPCOMING: { label: '예정', bgColor: '#E9F6FF', color: '#377FF8' },
};
40 changes: 0 additions & 40 deletions src/features/notice/mock/notice.ts

This file was deleted.

13 changes: 10 additions & 3 deletions src/features/notice/types/notice.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
export type NoticeType = 'MESSAGE' | 'NOTIFICATION';
export type NoticeType = 'UPCOMING' | 'NOTIFY';

export type NoticeListType = NoticeType | 'ONGOING' | 'COMPLETED';

export type Notice = {
id: number;
title: string;
type: NoticeType;
date: string;
startAt: string;
endAt: string;
noticeType: NoticeType;
};

export type NoticeList = Omit<Notice, 'noticeType'> & {
noticeStatus: NoticeListType;
};
Loading