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
10 changes: 5 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,10 @@
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"lucide-react": "^0.525.0",
"next": "15.1.8",
"react": "^19.0.0",
"next": "^16.0.0",
"react": "^19.2.0",
"react-chartjs-2": "^5.3.0",
"react-dom": "^19.0.0",
"react-dom": "^19.2.0",
"react-redux": "^9.2.0",
"sheet": "link:@/components/ui/sheet",
"tailwind-merge": "^3.3.1"
Expand All @@ -37,8 +37,8 @@
"@eslint/eslintrc": "^3",
"@eslint/js": "^9.27.0",
"@types/node": "^20",
"@types/react": "^19",
"@types/react-dom": "^19",
"@types/react": "^19.2.0",
"@types/react-dom": "^19.2.0",
"eslint": "^9.27.0",
"eslint-config-prettier": "^10.1.5",
"eslint-plugin-next": "^0.0.0",
Expand Down
780 changes: 400 additions & 380 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions src/api/votes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export interface VoteOption {
export interface BestVoteResponse {
voteId: number
title: string
content: string | null
category: VoteCategory
totalParticipants: number
createdBy: string
Expand Down
6 changes: 4 additions & 2 deletions src/app/poll/[id]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,14 @@ interface PollOption {
interface PollDetail {
voteId: number
title: string
content: string | null
category: string
creatorNickname: string
createdAt: string
totalVoteCount: number
options: PollOption[]
hasVoted: boolean
votedOptionLabel: string
votedOptionLabel: string | null
}

export default function PollDetailPage() {
Expand Down Expand Up @@ -170,14 +171,15 @@ export default function PollDetailPage() {
voteId={data.voteId}
createdBy={data.creatorNickname}
title={data.title}
content={data.content}
options={data.options.map((opt) => ({
optionId: opt.optionId,
content: opt.content,
vote_count: opt.voteCount,
}))}
totalParticipants={data.totalVoteCount}
hasVoted={data.hasVoted}
votedOptionLabel={data.votedOptionLabel}
votedOptionLabel={data.votedOptionLabel ?? undefined}
/>
)}
{bestComment && !open && (
Expand Down
5 changes: 5 additions & 0 deletions src/components/pages/balanse/balanseList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ export default function BalanceList({ data }: { data: Vote }) {
</CardHeader>
<CardContent className="pt-2 pb-4">
<p className="font-semibold mb-2">{data.title}</p>
{data.content && (
<p className="text-sm text-gray-600 mb-3 leading-relaxed whitespace-pre-wrap">
{data.content}
</p>
)}
<div className="text-sm text-gray-700 space-y-1">
{data.options.map((opt) => (
<div key={opt.id} className="bg-white">
Expand Down
8 changes: 7 additions & 1 deletion src/components/pages/balanse/mockPollCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
MostVotedVoteResponse,
} from '@/api/comment/mostVotedVoteApi'
import Link from 'next/link'
import InlineLoading from '@/components/_shared/inlineLoading'

const categoryMap: Record<string, string> = {
ETC: '기타',
Expand Down Expand Up @@ -33,7 +34,12 @@ function MockPollCard() {
getData()
}, [])

if (loading) return <div className="p-4">λ‘œλ”© 쀑...</div>
if (loading)
return (
<div className="p-4 flex justify-center items-center">
<InlineLoading />
</div>
)
if (error) return <div className="p-4 text-red-500">{error}</div>
if (!data) return null

Expand Down
49 changes: 42 additions & 7 deletions src/components/pages/create/createForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ const CreateForm = () => {
const [title, setTitle] = useState<string>('')
const [category, setCategory] = useState<string | null>(null)
const [options, setOptions] = useState<string[]>(['', '']) // A, B
const [content, setContent] = useState<string>('')

const isFormValid = () => {
if (title.trim() === '') return false
Expand All @@ -37,6 +38,9 @@ const CreateForm = () => {
title,
options,
category: category as VoteCategory,
...(content.trim()
? ({ content: content.trim() } as Pick<CreateVoteData, 'content'>)
: {}),
}
const voteId = await createVote(voteData)
return voteId
Expand All @@ -46,8 +50,9 @@ const CreateForm = () => {
<div className="flex flex-col items-center gap-10">
{/* 질문 */}
<div className="flex flex-col items-center gap-3 w-full">
<div className="w-full text-[18px] font-[700] leading-none">
μ§ˆλ¬Έμ„ μž‘μ„±ν•΄μ£Όμ„Έμš”
<div className="w-full text-[18px] font-[700] leading-none flex items-center gap-1">
<div>μ§ˆλ¬Έμ„ μž‘μ„±ν•΄μ£Όμ„Έμš”</div>
<div className="text-[#FF3B30]">*</div>
</div>
<div className="w-full border border-[#C6C6C6] rounded-lg px-5 py-3">
<input
Expand All @@ -60,10 +65,39 @@ const CreateForm = () => {
</div>
</div>

{/* 썰(κ²½ν—˜λ‹΄) */}
<div className="flex flex-col items-center gap-2 w-full">
<div className="w-full flex items-center gap-2">
<div className="text-[18px] font-[700] leading-none">
썰(κ²½ν—˜λ‹΄)을 λ“€λ €μ£Όμ„Έμš”
</div>
<div className="px-2 py-[2px] rounded-md border border-[#E4E4E4] text-[12px] text-[#8E8E8E] leading-none">
선택사항
</div>
</div>
<div className="w-full border border-[#C6C6C6] rounded-lg px-5 py-3">
<textarea
className="w-full h-[120px] resize-none text-[16px] text-[#1D1D1D] font-[400] outline-none"
placeholder="μ˜ˆμ‹œ: μ–΄μ œ μ μ‹¬μ‹œκ°„μ— λ™λ£Œλ“€κ³Ό 뭘 먹을지 μ •ν•˜λž΄λ‹€κ°€ μ‹œκ°„μ„ λ‹€ μ“°κ³  κ²°κ΅­ 편의점 λ„μ‹œλ½μ„ 먹게 λ˜μ—ˆλŠ”λ°,, μ—¬λŸ¬λΆ„μ΄λΌλ©΄ μ–΄λ–€ 선택을 ν•˜μ‹€κ±΄κ°€μš”?"
value={content}
onChange={(e) => setContent(e.target.value.slice(0, 500))}
/>
</div>
<div className="w-full flex items-center justify-between">
<div className="text-[12px] font-[400] leading-none text-[#8E8E8E]">
썰을 μΆ”κ°€ν•˜λ©΄ 더 λ§Žμ€ 곡감과 λŒ“κΈ€μ„ 받을 수 μžˆμ–΄μš”!
</div>
<div className="text-[12px] font-[400] leading-none text-[#8E8E8E]">
{content.length}/500
</div>
</div>
</div>

{/* 주제 */}
<div className="flex flex-col items-center gap-3 w-full">
<div className="w-full text-[18px] font-[700] leading-none">
주제λ₯Ό μ„ νƒν•΄μ£Όμ„Έμš”
<div className="w-full text-[18px] font-[700] leading-none flex items-center gap-1">
<div>주제λ₯Ό μ„ νƒν•΄μ£Όμ„Έμš”</div>
<div className="text-[#FF3B30]">*</div>
</div>
<div className="flex gap-2 w-full">
{categories.map((c) => (
Expand All @@ -90,8 +124,9 @@ const CreateForm = () => {

{/* 선택지 */}
<div className="flex flex-col items-center w-full">
<div className="w-full text-[18px] font-[700] leading-none">
선택지λ₯Ό μž‘μ„±ν•΄μ£Όμ„Έμš”
<div className="w-full text-[18px] font-[700] leading-none flex items-center gap-1">
<div>선택지λ₯Ό μž‘μ„±ν•΄μ£Όμ„Έμš”</div>
<div className="text-[#FF3B30]">*</div>
</div>

{/* 선택지 inputλ“€ */}
Expand Down Expand Up @@ -123,7 +158,7 @@ const CreateForm = () => {
<button
className="flex items-center pt-4 ml-auto gap-3 text-[#555555] text-[14px] font-[400] leading-none"
onClick={() => {
if (options.length < 5) {
if (options.length < 4) {
setOptions([...options, ''])
}
}}
Expand Down
5 changes: 5 additions & 0 deletions src/components/pages/my/_shared/balanseHistoryCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,11 @@ export default function BalanceHistoryCard({
</CardHeader>
<CardContent className="pt-2 pb-4">
<p className="font-semibold mb-2">{data.title}</p>
{data.content && (
<p className="text-sm text-gray-600 mb-3 leading-relaxed whitespace-pre-wrap">
{data.content}
</p>
)}
<div className="text-sm text-gray-700 space-y-1">
{data.options.map((opt, index) => (
<div key={index} className="text-[16px] font-[400] text-[#555555]">
Expand Down
9 changes: 8 additions & 1 deletion src/components/pages/poll/pollCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,20 +6,22 @@ interface PollCardProps {
voteId: number | string
createdBy: string
title: string
content?: string | null
options: {
optionId: number
content: string
vote_count: number
}[]
totalParticipants: number
hasVoted?: boolean
votedOptionLabel?: string
votedOptionLabel?: string | null
}

function PollCard({
voteId,
createdBy,
title,
content,
options = [],
totalParticipants,
hasVoted = false,
Expand Down Expand Up @@ -111,6 +113,11 @@ function PollCard({
<div className="mx-auto p-4 space-y-4 rounded-xl shadow bg-white mb-6">
<div className="text-sm font-medium text-gray-700">{createdBy}</div>
<div className="text-base font-semibold">{title}</div>
{content && (
<div className="text-sm text-gray-600 leading-relaxed whitespace-pre-wrap">
{content}
</div>
)}
<div className="space-y-2">
{localOptions.map((option, idx) => {
const isSelected = selectedId === option.optionId
Expand Down
2 changes: 2 additions & 0 deletions src/types/api/votes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@ export type CreateVoteData = {
title: string
options: string[]
category: VoteCategory
content?: string
}

export type MineVotesResponse = {
voteId: number
title: string
content: string | null
category: string
totalVoteCount: number
createdAt: string
Expand Down
1 change: 1 addition & 0 deletions src/types/balanse/vote.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export interface VoteOption {
export interface Vote {
id: number
title: string
content: string | null
category: string
member_id: number
nickname: string
Expand Down
1 change: 1 addition & 0 deletions src/types/my/history.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export type MyVoteHistoryItem = {
voteId: number
title: string
content: string | null
category: string
totalVoteCount: number
createdAt: string
Expand Down