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
8 changes: 8 additions & 0 deletions src/app/global.css
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,14 @@ input[type='number'] {
box-shadow: 0px 4px 30px 0px rgba(55, 127, 248, 0.1);
}

.shadow-voteCreateItem {
box-shadow: 0px 8px 20px 0px rgba(55, 127, 248, 0.1);
}

.shadow-voteCreateDock {
box-shadow: 0px -4px 30px 0px rgba(55, 127, 248, 0.04);
}

.perspective {
perspective: 1000px;
}
Expand Down
6 changes: 6 additions & 0 deletions src/app/stackflow/Stack.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,19 @@ import { VoteResultContentScreen } from '@/screen/vote-result-content/ui';
import { VoteScreen } from '@/screen/vote/ui';
import { VotePromiseScreen } from '@/screen/vote-promise/ui';
import { VoteCompleteScreen } from '@/screen/vote-complete/ui';
import { AdminHomeScreen } from '@/screen/admin-home/ui';
import { VoteCreateScreen } from '@/screen/vote-create/ui';
import { VoteCreateCompleteScreen } from '@/screen/vote-create-complete/ui';

export const { Stack, useFlow } = stackflow({
transitionDuration: 350,
activities: {
AdminHomeScreen,
NoticeScreen,
NoticeContentScreen,
VoteScreen,
VoteCreateScreen,
VoteCreateCompleteScreen,
VoteCompleteScreen,
VotePromiseScreen,
VoteResultScreen,
Expand Down
Binary file added src/assets/icon/icon-create-complete.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/icon/icon-create-vote.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/icon/icon-dashboard.webp
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
14 changes: 14 additions & 0 deletions src/assets/icon/icon-default-back.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 changes: 8 additions & 0 deletions src/assets/icon/index.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,21 @@
import VerifiedCheckIcon from './icon-check-verified.svg';
import CreateCompleteIcon from './icon-create-complete.png';
import CreateVoteIcon from './icon-create-vote.webp';
import DashBoardIcon from './icon-dashboard.webp';
import MessageIcon from './icon-message.svg';
import NoticeIcon from './icon-notice.svg';
import NotificationIcon from './icon-notification.svg';
import ResultIcon from './icon-result.svg';
import UserIcon from './icon-user.png';
import VoteCompleteIcon from './icon-vote-complete.png';
import VoteIcon from './icon-vote.svg';
import BackButton from './icon-default-back.svg';

export {
BackButton,
CreateVoteIcon,
CreateCompleteIcon,
DashBoardIcon,
MessageIcon,
NoticeIcon,
NotificationIcon,
Expand Down
7 changes: 6 additions & 1 deletion src/features/login/ui/LoginForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,12 @@ export default function LoginForm() {
<Button type="button" onClick={() => replace(PATH.HOME, {})}>
학생으로 로그인
</Button>
<Button intent="textmain" size="fit">
<Button
type="button"
intent="textmain"
size="fit"
onClick={() => replace(PATH.ADMIN_HOME, {})}
>
관리자로 로그인
</Button>
</div>
Expand Down
2 changes: 1 addition & 1 deletion src/features/notice/ui/NoticeList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import NoticeItem from './NoticeItem';

export default function NoticeList() {
return (
<div className="flex w-full flex-1 flex-shrink-0 flex-col divide-y divide-[#ECECEC] overflow-scroll">
<div className="scrollbar-hide flex w-full flex-1 flex-shrink-0 flex-col divide-y divide-[#ECECEC] overflow-scroll">
{NOTICE_MOCK.map(({ id, title, type, date }) => (
<NoticeItem key={id} id={id} title={title} type={type} date={date} />
))}
Expand Down
8 changes: 8 additions & 0 deletions src/features/vote-create/model/context/create.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { createContext, type Dispatch, type SetStateAction } from 'react';

export const VoteCreateContext = createContext<{
mode: number;
setMode: Dispatch<SetStateAction<number>>;
isCandidateMode: boolean;
setIsCandidateMode: Dispatch<SetStateAction<boolean>>;
} | null>(null);
1 change: 1 addition & 0 deletions src/features/vote-create/model/context/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './create';
1 change: 1 addition & 0 deletions src/features/vote-create/model/hooks/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default as useVoteCreateContext } from './useVoteCreateContext';
12 changes: 12 additions & 0 deletions src/features/vote-create/model/hooks/useVoteCreateContext.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { useContext } from 'react';
import { VoteCreateContext } from '@/features/vote-create/model';

export default function useVoteCreateContext() {
const context = useContext(VoteCreateContext);
if (!context) {
throw new Error(
'useVoteCreateContext는 VoteCreateProvider 안에서만 사용할 수 있어요.',
);
}
return context;
}
3 changes: 3 additions & 0 deletions src/features/vote-create/model/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export * from './context';
export * from './hooks';
export * from './provider';
25 changes: 25 additions & 0 deletions src/features/vote-create/model/provider/create.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { useState, type ReactNode } from 'react';

import { VoteCreateContext } from '@/features/vote-create/model';

export default function VoteCreateProvider({
children,
}: {
children: ReactNode;
}) {
const [mode, setMode] = useState(0);
const [isCandidateMode, setIsCandidateMode] = useState<boolean>(false);

return (
<VoteCreateContext.Provider
value={{
mode,
setMode,
isCandidateMode,
setIsCandidateMode,
}}
>
{children}
</VoteCreateContext.Provider>
);
}
1 change: 1 addition & 0 deletions src/features/vote-create/model/provider/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default as VoteCreateProvider } from './create';
28 changes: 28 additions & 0 deletions src/features/vote-create/ui/VoteButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import type { ButtonHTMLAttributes } from 'react';
import { IoChevronDownSharp, IoChevronForwardSharp } from 'react-icons/io5';

interface VoteButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
label: string;
arrowDown?: boolean;
}

export default function VoteInput({
arrowDown = false,
label,
onClick,
}: VoteButtonProps) {
return (
<button
name="mainCandidate"
className="text-s border-sl flex h-17 w-full cursor-pointer items-center justify-between rounded-md border-[1px] px-5 focus:outline-none"
onClick={onClick}
>
{label}
{arrowDown ? (
<IoChevronDownSharp size={20} className="text-sl" />
) : (
<IoChevronForwardSharp size={20} className="text-sl" />
)}
</button>
);
}
23 changes: 23 additions & 0 deletions src/features/vote-create/ui/VoteCandidateForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import VoteInput from './VoteInput';

export default function VoteCandidateForm() {
return (
<>
<p className="mt-11 flex flex-col items-start text-lg font-semibold">
정후보자 등록하기
</p>
<div className="mt-[18px] flex w-full flex-col gap-[14px] text-lg font-semibold">
<VoteInput placeholder="정후보자 이름" />
<VoteInput placeholder="학번" />
<VoteInput placeholder="소속 단과대" />
<VoteInput placeholder="학과" />
<div className="border-sl flex h-45 w-full flex-col gap-y-2.5 rounded-md border-[1px] p-5">
<textarea
className="flex-1 resize-none font-medium text-black placeholder:font-semibold focus:outline-none"
placeholder="약력을 짧게 작성해주세요"
/>
</div>
</div>
</>
);
}
39 changes: 39 additions & 0 deletions src/features/vote-create/ui/VoteDetailForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { IoChevronDownSharp } from 'react-icons/io5';
import { useVoteCreateContext } from '../model';
import { VoteButton, VoteCandidateForm } from '.';

export default function VoteDetailForm() {
const { isCandidateMode, setIsCandidateMode } = useVoteCreateContext();

return (
<>
{isCandidateMode ? (
<VoteCandidateForm />
) : (
<>
<div className="mt-11 flex flex-col items-start text-lg font-semibold">
<p>'구름' 선거운동본부</p>
<p>후보자를 입력해주세요</p>
</div>
<div className="text-s mt-[18px] flex w-full flex-col gap-[14px] text-lg font-semibold">
<VoteButton
label="정후보자"
onClick={() => setIsCandidateMode(true)}
/>
<VoteButton
label="부후보자"
onClick={() => setIsCandidateMode(true)}
/>
<div className="border-sl flex h-45 w-full flex-col gap-y-2.5 rounded-md border-[1px] p-5">
<div className="flex items-center justify-between">
핵심공약을 입력해주세요
<IoChevronDownSharp size={20} className="text-sl" />
</div>
<textarea className="flex-1 resize-none font-medium text-black focus:outline-none"></textarea>
</div>
</div>
</>
)}
</>
);
}
18 changes: 18 additions & 0 deletions src/features/vote-create/ui/VoteInput.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import type { InputHTMLAttributes } from 'react';
import { IoClose } from 'react-icons/io5';

type VoteInputProps = InputHTMLAttributes<HTMLInputElement>;

export default function VoteInput({ placeholder }: VoteInputProps) {
return (
<div className="relative w-full">
<input
className="border-sl placeholder:text-s flex h-17 w-full items-center justify-between rounded-md border-[1px] px-5 focus:outline-none"
placeholder={placeholder}
/>
<button className="absolute top-1/2 right-5 size-fit -translate-y-1/2 transform cursor-pointer focus:outline-none">
<IoClose className="text-sl" size={20} />
</button>
</div>
);
}
27 changes: 27 additions & 0 deletions src/features/vote-create/ui/VoteTitleForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { HiPlus } from 'react-icons/hi';
import { Button } from '@/shared/ui';
import { VoteButton, VoteInput } from '@/features/vote-create/ui';

export default function VoteTitleForm() {
return (
<>
<p className="mt-11 text-lg font-semibold">선거 정보 등록하기</p>
<div className="mt-[18px] flex w-full flex-col gap-[14px] text-lg font-semibold">
<VoteInput placeholder="투표 제목을 입력하세요" />
<VoteButton label="투표 시작일을 선택하세요" arrowDown />
<VoteButton label="투표 종료일을 선택하세요" arrowDown />
</div>
<p className="mt-6 text-lg font-semibold">선거운동본부 등록하기</p>
<div className="text-s mt-[18px] flex w-full flex-col gap-[14px] text-lg font-semibold">
<VoteInput placeholder="후보팀명을 입력하세요" />
<Button
className="flex h-17 items-center justify-between px-5 text-lg font-semibold"
intent="disabled"
>
<span>후보팀 추가하기</span>
<HiPlus />
</Button>
</div>
</>
);
}
5 changes: 5 additions & 0 deletions src/features/vote-create/ui/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export { default as VoteButton } from './VoteButton';
export { default as VoteCandidateForm } from './VoteCandidateForm';
export { default as VoteDetailForm } from './VoteDetailForm';
export { default as VoteInput } from './VoteInput';
export { default as VoteTitleForm } from './VoteTitleForm';
16 changes: 16 additions & 0 deletions src/screen/admin-home/ui/AdminHomeScreen.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { VoteBg } from '@/assets/image';
import { AdminAppBar } from '@/shared/ui';
import { AdminHomeContainer } from '@/widgets/admin-home/ui';
import { AppScreen } from '@stackflow/plugin-basic-ui';

export default function AdminHomeScreen() {
return (
<AppScreen
preventSwipeBack
backgroundImage={`url(${VoteBg})`}
appBar={AdminAppBar}
>
<AdminHomeContainer />
</AppScreen>
);
}
1 change: 1 addition & 0 deletions src/screen/admin-home/ui/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default as AdminHomeScreen } from './AdminHomeScreen';
33 changes: 33 additions & 0 deletions src/screen/vote-create-complete/ui/VoteCreateCompleteScreen.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { AppScreen } from '@stackflow/plugin-basic-ui';

import { useFlow } from '@/app/stackflow';
import { Button, NoBackAppBar } from '@/shared/ui';
import { VoteBg } from '@/assets/image';
import { CreateCompleteIcon } from '@/assets/icon';

export default function VoteCreateCompleteScreen() {
const { pop } = useFlow();

return (
<>
<AppScreen
preventSwipeBack
backgroundImage={`url(${VoteBg})`}
appBar={NoBackAppBar('', VoteBg)}
>
<div className="p-normal flex size-full flex-col">
<p className="text-m mt-mt mb-2.5 text-center text-4xl font-bold">
투표가 생성되었어요!
</p>
<p className="text-s mb-12 text-center text-xl font-semibold">
투표는 대시보드에서 수정 가능해요
</p>
<img src={CreateCompleteIcon} alt="voteComplete" className="mb-20" />
<Button intent="gradient" onClick={() => pop({ animate: false })}>
홈으로 돌아가기
</Button>
</div>
</AppScreen>
</>
);
}
1 change: 1 addition & 0 deletions src/screen/vote-create-complete/ui/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default as VoteCreateCompleteScreen } from './VoteCreateCompleteScreen';
10 changes: 10 additions & 0 deletions src/screen/vote-create/ui/VoteCreateScreen.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { VoteCreateProvider } from '@/features/vote-create/model';
import { VoteCreateContainer } from '@/widgets/vote-create/ui';

export default function VoteCreateScreen() {
return (
<VoteCreateProvider>
<VoteCreateContainer />
</VoteCreateProvider>
);
}
1 change: 1 addition & 0 deletions src/screen/vote-create/ui/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default as VoteCreateScreen } from './VoteCreateScreen';
3 changes: 3 additions & 0 deletions src/shared/constants/path.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
export const PATH = {
ADMIN_HOME: 'AdminHomeScreen',
HOME: 'HomeScreen',
LOGIN: 'LoginScreen',
NOTICE: 'NoticeScreen',
NOTICE_CONTENT: 'NoticeContentScreen',
VOTE: 'VoteScreen',
VOTE_COMPLETE: 'VoteCompleteScreen',
VOTE_CREATE: 'VoteCreateScreen',
VOTE_CREATE_COMPLETE: 'VoteCreateCompleteScreen',
VOTE_PROMISE: 'VotePromiseScreen',
VOTE_RESULT: 'VoteResultScreen',
VOTE_RESULT_CONTENT: 'VoteResultContentScreen',
Expand Down
Loading