Add samples page
Signed-off-by: Felipe Cardoso <felipe.cardoso@hotmail.it>
This commit is contained in:
96
frontend/src/app/samples/page.tsx
Normal file
96
frontend/src/app/samples/page.tsx
Normal file
@@ -0,0 +1,96 @@
|
|||||||
|
// src/app/samples/page.tsx
|
||||||
|
"use client"
|
||||||
|
|
||||||
|
import {useSamples} from '@/contexts/SamplesContext'
|
||||||
|
import Image from 'next/image'
|
||||||
|
import {useMemo} from 'react'
|
||||||
|
|
||||||
|
interface ParsedSample {
|
||||||
|
filename: string
|
||||||
|
timestamp: number
|
||||||
|
batch: number
|
||||||
|
index: number
|
||||||
|
url: string
|
||||||
|
created_at: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function SamplesPage() {
|
||||||
|
const {samples, isLoading, error} = useSamples()
|
||||||
|
|
||||||
|
const groupedSamples = useMemo(() => {
|
||||||
|
if (!samples?.length) return new Map()
|
||||||
|
|
||||||
|
const parsed: ParsedSample[] = samples.map(sample => {
|
||||||
|
console.debug('sample', sample)
|
||||||
|
const [timestamp, info] = sample.filename.split('__')
|
||||||
|
const [batch, index] = info.split('_')
|
||||||
|
return {
|
||||||
|
...sample,
|
||||||
|
timestamp: parseInt(timestamp),
|
||||||
|
batch: parseInt(batch),
|
||||||
|
index: parseInt(index.replace('.jpg', '')),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Group by batch
|
||||||
|
const groups = parsed.reduce((acc, sample) => {
|
||||||
|
const group = acc.get(sample.batch) || []
|
||||||
|
group.push(sample)
|
||||||
|
acc.set(sample.batch, group)
|
||||||
|
return acc
|
||||||
|
}, new Map<number, ParsedSample[]>())
|
||||||
|
|
||||||
|
// Sort within each group
|
||||||
|
for (const [batch, items] of groups) {
|
||||||
|
groups.set(batch, items.sort((a, b) => b.index - a.index))
|
||||||
|
}
|
||||||
|
|
||||||
|
// return new Map([...groups].sort((a, b) => b[0] - a[0]))
|
||||||
|
return new Map([...groups])
|
||||||
|
}, [samples])
|
||||||
|
|
||||||
|
if (isLoading) return <div>Loading samples...</div>
|
||||||
|
if (error) return <div>Error: {error.message}</div>
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="p-6 space-y-8">
|
||||||
|
<h1 className="text-2xl font-bold">Samples Gallery</h1>
|
||||||
|
|
||||||
|
{Array.from(groupedSamples).map(([batch, items]) => (
|
||||||
|
<div key={batch} className="space-y-2">
|
||||||
|
<h2 className="text-xl font-semibold">
|
||||||
|
Step {batch}
|
||||||
|
</h2>
|
||||||
|
<div className="overflow-x-auto">
|
||||||
|
<div className="flex gap-4 min-w-full pb-4">
|
||||||
|
{items.sort((a: any, b: any) => a.index - b.index).map((sample: any) => (
|
||||||
|
<div key={sample.filename} className="flex-shrink-0 w-48">
|
||||||
|
<Image
|
||||||
|
src={`${process.env.NEXT_PUBLIC_API_URL}${sample.url}`}
|
||||||
|
alt={`Sample ${sample.index}`}
|
||||||
|
width={200}
|
||||||
|
height={200}
|
||||||
|
className="rounded-lg shadow-sm object-cover w-full h-48"
|
||||||
|
/>
|
||||||
|
<div className="mt-2 text-sm">
|
||||||
|
<div className="text-gray-600">
|
||||||
|
{new Date(sample.created_at).toLocaleString()}
|
||||||
|
</div>
|
||||||
|
<a
|
||||||
|
href={`${process.env.NEXT_PUBLIC_API_URL}${sample.url}`}
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="text-blue-600 hover:underline"
|
||||||
|
>
|
||||||
|
Sample {sample.index}
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user