From 188a0eb1af20a08f55bf446f5bff6ed7e47d9564 Mon Sep 17 00:00:00 2001 From: jspark2000 Date: Mon, 25 Mar 2024 15:42:46 +0000 Subject: [PATCH] feat: implements roster profile-image upload service --- .../app/src/attendance/attendance.service.ts | 18 ++++++++- backend/app/src/roster/roster.service.ts | 24 ++++++++++-- .../update/_components/UpdateRosterForm.tsx | 38 +++++++++++++++++-- .../roster/_components/RosterListTable.tsx | 4 +- .../[id]/_components/AttendanceListTable.tsx | 4 +- .../check/_components/AttendanceCheckCard.tsx | 8 ++-- 6 files changed, 81 insertions(+), 15 deletions(-) diff --git a/backend/app/src/attendance/attendance.service.ts b/backend/app/src/attendance/attendance.service.ts index 861c2a8..dfdc8dc 100644 --- a/backend/app/src/attendance/attendance.service.ts +++ b/backend/app/src/attendance/attendance.service.ts @@ -5,7 +5,7 @@ import { UnexpectedException } from '@libs/exception' import { PrismaService } from '@libs/prisma' -import { calculatePaginationOffset } from '@libs/utils' +import { calculatePaginationOffset, formatFileUrl } from '@libs/utils' import { Prisma, RosterType, @@ -87,6 +87,14 @@ export class AttendanceService { ] }) + attendances.forEach((attendance) => { + if (attendance.Roster.profileImageUrl) { + attendance.Roster.profileImageUrl = formatFileUrl( + attendance.Roster.profileImageUrl + ) + } + }) + const total = await this.prisma.attendance.count({ where: { scheduleId, @@ -157,6 +165,14 @@ export class AttendanceService { ] }) + attendances.forEach((attendance) => { + if (attendance.Roster.profileImageUrl) { + attendance.Roster.profileImageUrl = formatFileUrl( + attendance.Roster.profileImageUrl + ) + } + }) + const total = await this.prisma.attendance.count({ where: { scheduleId, diff --git a/backend/app/src/roster/roster.service.ts b/backend/app/src/roster/roster.service.ts index 5ea2d63..303a046 100644 --- a/backend/app/src/roster/roster.service.ts +++ b/backend/app/src/roster/roster.service.ts @@ -8,7 +8,7 @@ import { } from '@libs/exception' import { PrismaService } from '@libs/prisma' import { StorageService } from '@libs/storage' -import { calculatePaginationOffset } from '@libs/utils' +import { calculatePaginationOffset, formatFileUrl } from '@libs/utils' import { Prisma, RosterStatus, type Roster } from '@prisma/client' import type { CreateRosterDTO, UpdateRosterDTO } from './dto/roster.dto' @@ -22,11 +22,17 @@ export class RosterService { async getRoster(rosterId: number): Promise { try { - return await this.prisma.roster.findUniqueOrThrow({ + const roster = await this.prisma.roster.findUniqueOrThrow({ where: { id: rosterId } }) + + if (roster.profileImageUrl) { + roster.profileImageUrl = formatFileUrl(roster.profileImageUrl) + } + + return roster } catch (error) { if ( error instanceof Prisma.PrismaClientKnownRequestError && @@ -40,11 +46,17 @@ export class RosterService { async getRosterByStudentId(studentId: string): Promise { try { - return await this.prisma.roster.findUniqueOrThrow({ + const roster = await this.prisma.roster.findUniqueOrThrow({ where: { studentId } }) + + if (roster.profileImageUrl) { + roster.profileImageUrl = formatFileUrl(roster.profileImageUrl) + } + + return roster } catch (error) { if ( error instanceof Prisma.PrismaClientKnownRequestError && @@ -80,6 +92,12 @@ export class RosterService { ] }) + rosters.forEach((roster) => { + if (roster.profileImageUrl) { + roster.profileImageUrl = formatFileUrl(roster.profileImageUrl) + } + }) + const total = await this.prisma.roster.count({ where: { status diff --git a/frontend/src/app/console/roster/[id]/update/_components/UpdateRosterForm.tsx b/frontend/src/app/console/roster/[id]/update/_components/UpdateRosterForm.tsx index 819242b..5109255 100644 --- a/frontend/src/app/console/roster/[id]/update/_components/UpdateRosterForm.tsx +++ b/frontend/src/app/console/roster/[id]/update/_components/UpdateRosterForm.tsx @@ -1,5 +1,6 @@ 'use client' +import { ImageInput } from '@/components/ImageInput' import { Button } from '@/components/ui/button' import { Form, @@ -30,6 +31,11 @@ import type { z } from 'zod' export default function UpdateRosterForm({ roster }: { roster: Roster }) { const [isFetching, setIsFetching] = useState(false) + const [selectedFile, setSelectedFile] = useState(null) + + const handleFileSelect = (file: File) => { + setSelectedFile(file) + } const router = useRouter() @@ -39,9 +45,18 @@ export default function UpdateRosterForm({ roster }: { roster: Roster }) { resolver: zodResolver(UpdateRosterFormSchema), defaultValues: { ...roster, - offPosition: roster.offPosition ?? undefined, - defPosition: roster.defPosition ?? undefined, - splPosition: roster.splPosition ?? undefined + offPosition: + roster.type === RosterType.Athlete + ? roster.offPosition ?? undefined + : undefined, + defPosition: + roster.type === RosterType.Athlete + ? roster.defPosition ?? undefined + : undefined, + splPosition: + roster.type === RosterType.Athlete + ? roster.splPosition ?? undefined + : undefined } }) @@ -58,6 +73,17 @@ export default function UpdateRosterForm({ roster }: { roster: Roster }) { }, false ) + + if (selectedFile) { + const formData = new FormData() + formData.append('image', selectedFile) + await fetcher.put( + `/rosters/${roster.id}/profile-image`, + formData, + false + ) + } + router.push('/console/roster?revalidate=true') router.refresh() toast.success('부원정보가 업데이트 되었습니다') @@ -74,6 +100,12 @@ export default function UpdateRosterForm({ roster }: { roster: Roster }) { onSubmit={form.handleSubmit(onSubmit)} className="grid grid-cols-12 gap-5" > +
+

+ 프로필 이미지 변경 +

+ +
-
+
{roster.profileImageUrl ? ( ) : ( diff --git a/frontend/src/app/console/schedule/[id]/_components/AttendanceListTable.tsx b/frontend/src/app/console/schedule/[id]/_components/AttendanceListTable.tsx index 9f94963..b34c2c8 100644 --- a/frontend/src/app/console/schedule/[id]/_components/AttendanceListTable.tsx +++ b/frontend/src/app/console/schedule/[id]/_components/AttendanceListTable.tsx @@ -86,14 +86,14 @@ export default function AttendanceListTable({ const attendance = row.original return ( -
+
{attendance.Roster.profileImageUrl ? ( profile ) : ( diff --git a/frontend/src/app/console/schedule/[id]/check/_components/AttendanceCheckCard.tsx b/frontend/src/app/console/schedule/[id]/check/_components/AttendanceCheckCard.tsx index 2a36365..735fd70 100644 --- a/frontend/src/app/console/schedule/[id]/check/_components/AttendanceCheckCard.tsx +++ b/frontend/src/app/console/schedule/[id]/check/_components/AttendanceCheckCard.tsx @@ -58,20 +58,20 @@ export default function AttendanceCheckCard({ return (
-
+
{attendance.Roster.profileImageUrl ? ( image ) : ( - + )}
-
+