Skip to content
This repository has been archived by the owner on Dec 16, 2024. It is now read-only.

Commit

Permalink
feat: implement update roster page
Browse files Browse the repository at this point in the history
  • Loading branch information
jspark2000 committed Mar 22, 2024
1 parent 4565d6b commit 9689200
Show file tree
Hide file tree
Showing 7 changed files with 338 additions and 16 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,280 @@
'use client'

import { Button } from '@/components/ui/button'
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage
} from '@/components/ui/form'
import { Input } from '@/components/ui/input'
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue
} from '@/components/ui/select'
import { RosterStatus, RosterType } from '@/lib/enums'
import fetcher from '@/lib/fetcher'
import { RosterFormSchema } from '@/lib/forms'
import type { Roster } from '@/lib/types/roster'
import { zodResolver } from '@hookform/resolvers/zod'
import { useRouter } from 'next/navigation'
import { useState } from 'react'
import { useForm } from 'react-hook-form'
import { toast } from 'sonner'
import type { z } from 'zod'

export default function UpdateRosterForm({ roster }: { roster: Roster }) {
const [isFetching, setIsFetching] = useState(false)

const router = useRouter()

const UpdateRosterFormSchema = RosterFormSchema.omit({ id: true })

const form = useForm<z.infer<typeof UpdateRosterFormSchema>>({
resolver: zodResolver(UpdateRosterFormSchema),
defaultValues: {
...roster,
offPosition: roster.offPosition ?? undefined,
defPosition: roster.defPosition ?? undefined,
splPosition: roster.splPosition ?? undefined
}
})

const onSubmit = async (data: z.infer<typeof UpdateRosterFormSchema>) => {
try {
setIsFetching(true)
await fetcher.put(
`/rosters/${roster.id}`,
{
...data,
class: data.class === '' ? '없음' : data.class,
offPosition:
data.type !== RosterType.Athlete ? data.type : data.offPosition
},
false
)
router.push('/console/roster?revalidate=true')
router.refresh()
toast.success('부원정보가 업데이트 되었습니다')
} catch (error) {
toast.error('부원을 업데이트하지 못했습니다')
} finally {
setIsFetching(false)
}
}

return (
<Form {...form}>
<form
onSubmit={form.handleSubmit(onSubmit)}
className="grid grid-cols-12 gap-5"
>
<div className="col-span-12 sm:col-span-6 lg:col-span-4">
<FormField
control={form.control}
name="name"
render={({ field }) => (
<FormItem>
<FormLabel>이름</FormLabel>
<FormControl>
<Input {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</div>
<div className="col-span-12 sm:col-span-6 lg:col-span-4">
<FormField
control={form.control}
name="studentId"
render={({ field }) => (
<FormItem>
<FormLabel>학번 (코치진의 경우 전화번호 뒤 4자리)</FormLabel>
<FormControl>
<Input {...field} placeholder="10자리 (ex 2024310000)" />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</div>
<div className="col-span-12 sm:col-span-6 lg:col-span-4">
<FormField
control={form.control}
name="type"
render={({ field }) => (
<FormItem>
<FormLabel>구분</FormLabel>
<Select
onValueChange={field.onChange}
defaultValue={field.value}
>
<FormControl>
<SelectTrigger>
<SelectValue placeholder="로스터 종류를 선택해주세요" />
</SelectTrigger>
</FormControl>
<SelectContent>
<SelectItem value={RosterType.Athlete}>선수</SelectItem>
<SelectItem value={RosterType.Staff}>스태프</SelectItem>
<SelectItem value={RosterType.HeadCoach}>감독</SelectItem>
<SelectItem value={RosterType.Coach}>코치</SelectItem>
</SelectContent>
</Select>
<FormMessage />
</FormItem>
)}
/>
</div>
<div className="col-span-12 sm:col-span-6 lg:col-span-4">
<FormField
control={form.control}
name="status"
render={({ field }) => (
<FormItem>
<FormLabel>상태</FormLabel>
<Select
onValueChange={field.onChange}
defaultValue={field.value}
>
<FormControl>
<SelectTrigger>
<SelectValue placeholder="부원의 상태를 선택해주세요" />
</SelectTrigger>
</FormControl>
<SelectContent>
<SelectItem value={RosterStatus.Enable}>활성</SelectItem>
<SelectItem value={RosterStatus.Absence}>휴학</SelectItem>
<SelectItem value={RosterStatus.Military}>군대</SelectItem>
<SelectItem value={RosterStatus.Alumni}>졸업</SelectItem>
</SelectContent>
</Select>
<FormMessage />
</FormItem>
)}
/>
</div>
<div className="col-span-12 sm:col-span-6 lg:col-span-4">
<FormField
control={form.control}
name="registerYear"
render={({ field }) => (
<FormItem>
<FormLabel>입부년도 (미식축구부에 입부한 년도)</FormLabel>
<FormControl>
<Input
{...field}
type="number"
placeholder="4자리 (ex 2024)"
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</div>
<div className="col-span-12 sm:col-span-6 lg:col-span-4">
<FormField
control={form.control}
name="admissionYear"
render={({ field }) => (
<FormItem>
<FormLabel>입학년도 (학교에 입학한 년도)</FormLabel>
<FormControl>
<Input
{...field}
type="number"
placeholder="4자리 (ex 2024)"
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</div>
<div className="col-span-12 sm:col-span-6 lg:col-span-4">
<FormField
control={form.control}
name="class"
render={({ field }) => (
<FormItem>
<FormLabel>직책 (선택)</FormLabel>
<FormControl>
<Input {...field} placeholder="(ex 주장, 주무)" />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</div>
{form.getValues('type') === RosterType.Athlete && (
<>
<div className="col-span-12 sm:col-span-6 lg:col-span-4">
<FormField
control={form.control}
name="offPosition"
render={({ field }) => (
<FormItem>
<FormLabel>오펜스포지션</FormLabel>
<FormControl>
<Input {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</div>
<div className="col-span-12 sm:col-span-6 lg:col-span-4">
<FormField
control={form.control}
name="defPosition"
render={({ field }) => (
<FormItem>
<FormLabel>디펜스포지션</FormLabel>
<FormControl>
<Input {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</div>
<div className="col-span-12 sm:col-span-6 lg:col-span-4">
<FormField
control={form.control}
name="splPosition"
render={({ field }) => (
<FormItem>
<FormLabel>스페셜포지션</FormLabel>
<FormControl>
<Input {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</div>
</>
)}
<div className="col-span-12 flex space-x-1">
<Button type="submit" disabled={isFetching}>
수정하기
</Button>
<Button
type="button"
variant={'outline'}
onClick={() => router.back()}
>
목록으로
</Button>
</div>
</form>
</Form>
)
}
23 changes: 23 additions & 0 deletions frontend/src/app/console/roster/[id]/update/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { getRoster } from '@/lib/actions'
import UpdateRosterForm from './_components/UpdateRosterForm'

export default async function RosterPage({
params
}: {
params: {
id: number
}
}) {
const roster = await getRoster(params.id)

return (
<main className="mx-auto flex w-full flex-grow flex-col items-center justify-start">
<div className="mt-4 flex w-full items-center justify-between px-4 text-left sm:px-6">
<div className="flex w-full flex-col space-y-3">
<h1 className="text-base font-bold sm:text-xl">부원정보수정</h1>
<UpdateRosterForm roster={roster} />
</div>
</div>
</main>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ import { UserIcon } from '@heroicons/react/24/outline'
import type { ColumnDef } from '@tanstack/react-table'
import { MoreHorizontal } from 'lucide-react'
import Image from 'next/image'
import { useRouter } from 'next/navigation'
import { useState } from 'react'
import { toast } from 'sonner'
import DeleteRosterForm from './DeleteRosterForm'

export default function RosterListTable({
Expand All @@ -29,6 +29,8 @@ export default function RosterListTable({
const [open, setOpen] = useState(false)
const [targetRoster, setTargetRoster] = useState<RosterListItem>()

const router = useRouter()

const handleClick = (roster: RosterListItem) => {
setTargetRoster(roster)
setOpen(true)
Expand Down Expand Up @@ -132,7 +134,9 @@ export default function RosterListTable({
<DropdownMenuLabel>메뉴</DropdownMenuLabel>
<DropdownMenuSeparator />
<DropdownMenuItem
onClick={() => toast.warning('준비중인 기능입니다')}
onClick={() =>
router.push(`/console/roster/${roster.id}/update`)
}
>
수정
</DropdownMenuItem>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,13 +50,18 @@ export default function CreateRosterForm() {
setIsFetching(true)

try {
await fetcher.post('/rosters', {
...data,
class: data.class === '' ? '없음' : data.class,
offPosition:
data.type !== RosterType.Athlete ? data.type : data.offPosition
})
await fetcher.post(
'/rosters',
{
...data,
class: data.class === '' ? '없음' : data.class,
offPosition:
data.type !== RosterType.Athlete ? data.type : data.offPosition
},
false
)
router.push('/console/roster?revalidate=true')
router.refresh()
toast.success('부원을 등록했습니다')
} catch (error) {
toast.error('부원을 등록하지 못했습니다')
Expand Down
6 changes: 0 additions & 6 deletions frontend/src/app/console/roster/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import { Button } from '@/components/ui/button'
import { getRosters } from '@/lib/actions'
import { calculateTotalPages } from '@/lib/utils'
import { PAGINATION_LIMIT_DEFAULT } from '@/lib/vars'
import { revalidatePath } from 'next/cache'
import Link from 'next/link'
import RosterListTable from './_components/RosterListTable'

Expand All @@ -12,16 +11,11 @@ export default async function RosterPage({
}: {
searchParams?: {
page?: string
revalidate?: string
}
}) {
const currentPage = Number(searchParams?.page) || 1
const rosterList = await getRosters(currentPage)

if (searchParams?.revalidate) {
revalidatePath('/console/roster')
}

return (
<main className="mx-auto flex w-full flex-grow flex-col items-center justify-start">
<div className="mt-4 flex w-full items-center justify-between px-4 text-left sm:px-6">
Expand Down
Loading

0 comments on commit 9689200

Please sign in to comment.