Skip to content

Commit

Permalink
feat: 식사 분석 조회 페이지
Browse files Browse the repository at this point in the history
  • Loading branch information
ArpaAP committed Aug 5, 2024
1 parent eb7e5a7 commit 2a303c6
Show file tree
Hide file tree
Showing 7 changed files with 233 additions and 45 deletions.
6 changes: 6 additions & 0 deletions apps/web/src/app/analyze/page.layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ import MainLayout from '@/components/MainLayout'
import { Meal, MealItem } from '@repo/database'
import { IconFileAnalytics, IconUpload } from '@tabler/icons-react'
import axios from 'axios'
import { useRouter } from 'next/navigation'
import { useEffect, useState } from 'react'
import toast from 'react-hot-toast'

interface AnalyzePageLayoutProps {
mealId: string
Expand All @@ -17,6 +19,7 @@ export default function AnalyzePageLayout({
meal,
mealItems,
}: AnalyzePageLayoutProps) {
const router = useRouter()
const [successCount, setSuccessCount] = useState(0)

const totalCount = mealItems.length
Expand All @@ -31,6 +34,9 @@ export default function AnalyzePageLayout({
console.error(error)
}
}

toast.success('분석이 완료되었습니다!')
setTimeout(() => router.push(`/meal/${mealId}`), 2000)
})()
}, [mealItems])

Expand Down
8 changes: 5 additions & 3 deletions apps/web/src/app/dashboard.page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ export default function DashboardPage({ user }: DashboardPageProps) {
</div>
<div className="flex items-center gap-4">
<div className="font-bold text-4xl">
영양 분석 및 식단 추천 서비스
AI 기반 영양 분석 및 식단 추천 서비스
</div>
<Image src={logoWhite} alt="logo" width={120} />
</div>
Expand Down Expand Up @@ -110,7 +110,7 @@ export default function DashboardPage({ user }: DashboardPageProps) {
</div>

<div className="text-2xl font-bold py-3">
황부연님의 일평균 영양 현황
{user.name}님의 일평균 영양 현황
</div>

<div className="grid grid-cols-4 gap-4 py-3 mb-6">
Expand Down Expand Up @@ -171,7 +171,9 @@ export default function DashboardPage({ user }: DashboardPageProps) {
</div>
</div>

<div className="text-2xl font-bold py-3">황부연님의 영양섭취량</div>
<div className="text-2xl font-bold py-3">
{user.name}님의 영양섭취량
</div>

<div className="pb-12">
<Line
Expand Down
170 changes: 167 additions & 3 deletions apps/web/src/app/meal/[mealId]/page.layout.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,177 @@
import { Meal, MealItem } from '@repo/database'
'use client'

import MainLayout from '@/components/MainLayout'
import { storage } from '@/lib/firebase/firebaseClient'
import { Meal, MealItem, MealItemAnalysis } from '@repo/database'
import { getDownloadURL, ref } from 'firebase/storage'
import Image from 'next/image'
import { useEffect, useState } from 'react'

interface MealPageLayoutProps {
meal: Meal
mealItems: MealItem[]
mealItems: (MealItem & { mealItemAnalysis: MealItemAnalysis | null })[]
imageUrls: string[]
}

export default function MealPageLayout({
meal,
mealItems,
imageUrls,
}: MealPageLayoutProps) {
return null
let typeStr = ''

switch (meal.type) {
case 'breakfast':
typeStr = '아침'
break
case 'lunch':
typeStr = '점심'
break
case 'dinner':
typeStr = '저녁'
break
}

return (
<MainLayout>
<div className="container mx-auto px-36 py-24">
<div className="text-3xl text-primary-800 font-bold pb-4">
{meal.date.toLocaleDateString()} {typeStr}
</div>
<div className="flex gap-4">
<div className="grow text-lg text-black/60">
{mealItems.length}개의 음식
</div>
</div>

<hr className="my-4" />

<div className="text-2xl text-primary-800 font-semibold py-6">
식품별 분석 정보
</div>

<div className="flex flex-col gap-6">
{mealItems.map((mealItem, index) => {
let { mealItemAnalysis } = mealItem

return (
<div key={index} className="flex gap-12 items-center">
<Image
src={imageUrls[index]}
alt=""
width={200}
height={200}
className="object-cover w-48 aspect-square border rounded-xl"
/>
{mealItemAnalysis ? (
<div className="w-full">
<div className="flex gap-4 items-center pb-2 border-b">
<div className="text-xl font-semibold">
{mealItemAnalysis?.className}
</div>
<div className="text-sm text-gray-500">
정확도 {(mealItemAnalysis?.confidence * 100).toFixed(2)}
%
</div>
</div>

<div className="grid grid-cols-7 gap-4 w-full py-6">
<div className="col-span-1">
<div className="text-black/60 text-sm">칼로리</div>
<div className="font-medium">
{mealItemAnalysis?.kcal.toFixed(2)} kcal
</div>
</div>
<div className="col-span-1">
<div className="text-black/60 text-sm">탄수화물</div>
<div className="font-medium">
{mealItemAnalysis?.carbohydrate.toFixed(2)} g
</div>
</div>
<div className="col-span-1">
<div className="text-black/60 text-sm">당류</div>
<div className="font-medium">
{mealItemAnalysis?.sugar.toFixed(2)} g
</div>
</div>
<div className="col-span-1">
<div className="text-black/60 text-sm">지방</div>
<div className="font-medium">
{mealItemAnalysis?.fat.toFixed(2)} g
</div>
</div>
<div className="col-span-1">
<div className="text-black/60 text-sm">단백질</div>
<div className="font-medium">
{mealItemAnalysis?.protein.toFixed(2)} g
</div>
</div>
<div className="col-span-1">
<div className="text-black/60 text-sm">칼슘</div>
<div className="font-medium">
{mealItemAnalysis?.calcium.toFixed(2)} mg
</div>
</div>
<div className="col-span-1">
<div className="text-black/60 text-sm"></div>
<div className="font-medium">
{mealItemAnalysis?.phosphorus.toFixed(2)} mg
</div>
</div>
<div className="col-span-1">
<div className="text-black/60 text-sm">나트륨</div>
<div className="font-medium">
{mealItemAnalysis?.natrium.toFixed(2)} mg
</div>
</div>
<div className="col-span-1">
<div className="text-black/60 text-sm">칼륨</div>
<div className="font-medium">
{mealItemAnalysis?.kalium.toFixed(2)} mg
</div>
</div>
<div className="col-span-1">
<div className="text-black/60 text-sm">마그네슘</div>
<div className="font-medium">
{mealItemAnalysis?.magnesium.toFixed(2)} mg
</div>
</div>
<div className="col-span-1">
<div className="text-black/60 text-sm">철분</div>
<div className="font-medium">
{mealItemAnalysis?.iron.toFixed(2)} mg
</div>
</div>
<div className="col-span-1">
<div className="text-black/60 text-sm">아연</div>
<div className="font-medium">
{mealItemAnalysis?.zinc.toFixed(2)} mg
</div>
</div>
<div className="col-span-1">
<div className="text-black/60 text-sm">콜레스테롤</div>
<div className="font-medium">
{mealItemAnalysis?.cholesterol.toFixed(2)} mg
</div>
</div>
<div className="col-span-1">
<div className="text-black/60 text-sm">트랜스지방</div>
<div className="font-medium">
{mealItemAnalysis?.transfat.toFixed(2)} g
</div>
</div>
</div>
</div>
) : (
<div className="">
<div className="text-xl font-bold">인식 실패</div>
</div>
)}
</div>
)
})}
</div>
</div>
</MainLayout>
)
}
16 changes: 15 additions & 1 deletion apps/web/src/app/meal/[mealId]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { getServerSession } from 'next-auth'
import { notFound, redirect } from 'next/navigation'
import { PrismaClient } from '@repo/database'
import MealPageLayout from './page.layout'
import { storage } from '@/lib/firebase/firebaseClient'
import { ref, getDownloadURL } from 'firebase/storage'

const prisma = new PrismaClient()

Expand Down Expand Up @@ -35,7 +37,19 @@ export default async function MealPage({

const mealItems = await prisma.mealItem.findMany({
where: { mealId },
include: {
mealItemAnalysis: true,
},
})

return <MealPageLayout meal={meal} mealItems={mealItems} />
const imageUrls = await Promise.all(
mealItems.map(async (mealItem) => {
const refPath = ref(storage, `images/${mealItem.imageName}`)
return getDownloadURL(refPath)
})
)

return (
<MealPageLayout meal={meal} mealItems={mealItems} imageUrls={imageUrls} />
)
}
74 changes: 38 additions & 36 deletions apps/web/src/app/my/page.layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -102,45 +102,47 @@ export default function MyPageLayout({
<TabPanels>
<TabPanel>
<div className="flex flex-col gap-2">
{meals.map((meal) => {
let typeStr = ''
{meals
.sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime())
.map((meal) => {
let typeStr = ''

switch (meal.type) {
case 'breakfast':
typeStr = '아침'
break
case 'lunch':
typeStr = '점심'
break
case 'dinner':
typeStr = '저녁'
break
case 'snack':
typeStr = '간식'
break
case 'etc':
typeStr = '기타'
break
}
switch (meal.type) {
case 'breakfast':
typeStr = '아침'
break
case 'lunch':
typeStr = '점심'
break
case 'dinner':
typeStr = '저녁'
break
case 'snack':
typeStr = '간식'
break
case 'etc':
typeStr = '기타'
break
}

return (
<Link
href={`/meal/${meal.mealId}`}
key={meal.mealId}
className="bg-gray-100 rounded-xl p-4 flex justify-between items-center gap-4 py-4"
>
<div className="flex items-center gap-2">
<div className="text-lg font-medium">
{meal.date.toLocaleDateString()} {typeStr}
return (
<Link
href={`/meal/${meal.mealId}`}
key={meal.mealId}
className="bg-gray-100 rounded-xl p-4 flex justify-between items-center gap-4 py-4"
>
<div className="flex items-center gap-2">
<div className="text-lg font-medium">
{meal.date.toLocaleDateString()} {typeStr}
</div>
<div className="text-sm">
{meal.mealItems.length}개 음식
</div>
</div>
<div className="text-sm">
{meal.mealItems.length}개 음식
</div>
</div>
<IconChevronRight />
</Link>
)
})}
<IconChevronRight />
</Link>
)
})}
</div>
</TabPanel>
<TabPanel>Content 2</TabPanel>
Expand Down
2 changes: 1 addition & 1 deletion apps/web/src/app/upload/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ export default function UploadPage() {
})
.then((res) => {
if (res.status === 200) {
toast.success('사진 업르드를 완료했습니다.')
toast.success('사진 업르드를 완료했습니다. 분석을 시작합니다.')

let meal = res.data.data as Meal
router.push(`/analyze?mealId=${meal.mealId}`)
Expand Down
2 changes: 1 addition & 1 deletion apps/web/src/components/MainNavbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ export default function MainNavbar() {

<div className="flex h-full pt-5 gap-6 px-2 text-[15px]">
<NavLink href="/board" name="게시판" />
<NavLink href="/upload" name="식사 분석" />
<NavLink href="/market" name="마켓" />
<NavLink href="/analysis" name="내 식습관" />
</div>
</div>

Expand Down

0 comments on commit 2a303c6

Please sign in to comment.