chore: seminarhof customer project
This commit is contained in:
157
apps/app/src/pages/kanban.tsx
Normal file
157
apps/app/src/pages/kanban.tsx
Normal file
@@ -0,0 +1,157 @@
|
||||
import { Link, createFileRoute } from '@tanstack/react-router'
|
||||
import { m } from '@/i18n/messages'
|
||||
import { bookingStatus } from '@/i18n/i18n-enum.gen'
|
||||
import { useLocale, getLocale } from '@/i18n/config'
|
||||
import { asI18n } from '@pikku/react'
|
||||
import { Box, Container, Group, Paper, Stack, Text } from '@pikku/mantine/core'
|
||||
import { usePikkuQuery } from '@project/functions-sdk/pikku/api.gen'
|
||||
import { usePageTitle } from '../components/AppLayout'
|
||||
import { PublicShell } from '../components/PublicShell'
|
||||
import { BOOKING_STATUS, type BookingStatus } from '../lib/status'
|
||||
import { fmtDateShort } from '../lib/dates'
|
||||
|
||||
const COLUMNS: BookingStatus[] = ['enquiry', 'reserved', 'confirmed', 'ended', 'cancelled']
|
||||
|
||||
type Booking = {
|
||||
bookingId: string
|
||||
eventName: string
|
||||
startDate: Date
|
||||
endDate: Date
|
||||
status: string
|
||||
clientName: string
|
||||
isStammgruppe: boolean
|
||||
depositPaid: boolean
|
||||
hasDeposit: boolean
|
||||
halfHouse: boolean
|
||||
}
|
||||
|
||||
function BookingCard({ booking, locale }: { booking: Booking; locale: string }) {
|
||||
useLocale()
|
||||
const palette = BOOKING_STATUS[booking.status as BookingStatus]
|
||||
|
||||
return (
|
||||
<Link
|
||||
to="/admin/bookings/$bookingId"
|
||||
params={{ bookingId: booking.bookingId }}
|
||||
style={{ textDecoration: 'none', display: 'block' }}
|
||||
>
|
||||
<Paper
|
||||
withBorder
|
||||
radius="md"
|
||||
p="sm"
|
||||
style={{
|
||||
borderLeft: `3px solid ${palette.dot}`,
|
||||
cursor: 'pointer',
|
||||
}}
|
||||
>
|
||||
<Stack gap={4}>
|
||||
<Text size="sm" fw={600} lineClamp={2} c="var(--mantine-color-text)">
|
||||
{asI18n(`${booking.eventName}`)}
|
||||
</Text>
|
||||
<Group gap={4}>
|
||||
{booking.isStammgruppe && (
|
||||
<Text size="xs" c="yellow.6" title={m.common__pages__admin_booking__client__regular_group()} style={{ cursor: 'default' }}>{asI18n('★')}</Text>
|
||||
)}
|
||||
<Text size="xs" c="dimmed" lineClamp={1}>{asI18n(`${booking.clientName}`)}</Text>
|
||||
</Group>
|
||||
<Text size="xs" c="dimmed">
|
||||
{asI18n(`${fmtDateShort(booking.startDate, locale)} – ${fmtDateShort(booking.endDate, locale)}`)}
|
||||
</Text>
|
||||
{booking.halfHouse && (
|
||||
<Text size="xs" c="dimmed">{asI18n('½')}</Text>
|
||||
)}
|
||||
{booking.hasDeposit && (
|
||||
<Text size="xs" c={booking.depositPaid ? 'green' : 'orange'} fw={500}>
|
||||
{(booking.depositPaid ? m.common__pages__admin__bookings__deposit__paid : m.common__pages__admin__bookings__deposit__outstanding)()}
|
||||
</Text>
|
||||
)}
|
||||
</Stack>
|
||||
</Paper>
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
|
||||
function KanbanInner() {
|
||||
useLocale()
|
||||
usePageTitle(m.common__pages__kanban__title())
|
||||
|
||||
const { data, isLoading, error } = usePikkuQuery('getAdminBookings', { venueSlug: 'drawehn' })
|
||||
|
||||
if (isLoading) return <Container py="xl"><Text>{m.common__states__loading()}</Text></Container>
|
||||
if (error) return <Container py="xl"><Text c="red">{m.common__states__error()}</Text></Container>
|
||||
if (!data) return null
|
||||
|
||||
const byStatus = new Map<BookingStatus, Booking[]>()
|
||||
for (const col of COLUMNS) byStatus.set(col, [])
|
||||
for (const b of data.bookings) {
|
||||
const col = b.status as BookingStatus
|
||||
if (byStatus.has(col)) byStatus.get(col)!.push({ ...b, halfHouse: !!b.halfHouse } as unknown as Booking)
|
||||
}
|
||||
|
||||
return (
|
||||
<Box
|
||||
style={{
|
||||
overflowX: 'auto',
|
||||
minHeight: 0,
|
||||
paddingBottom: 24,
|
||||
}}
|
||||
px="xl"
|
||||
pt="md"
|
||||
>
|
||||
<Group align="flex-start" wrap="nowrap" gap="md" style={{ minWidth: 'max-content' }}>
|
||||
{COLUMNS.map((col) => {
|
||||
const palette = BOOKING_STATUS[col]
|
||||
const cards = byStatus.get(col) ?? []
|
||||
return (
|
||||
<Stack
|
||||
key={col}
|
||||
gap="sm"
|
||||
style={{ width: 260, flexShrink: 0 }}
|
||||
>
|
||||
<Group gap="xs" align="center">
|
||||
<Box
|
||||
style={{
|
||||
width: 8,
|
||||
height: 8,
|
||||
borderRadius: 4,
|
||||
background: palette.dot,
|
||||
flexShrink: 0,
|
||||
}}
|
||||
/>
|
||||
<Text size="xs" fw={700} tt="uppercase" style={{ letterSpacing: '0.06em', color: palette.fg }}>
|
||||
{bookingStatus[col]()}
|
||||
</Text>
|
||||
<Text size="xs" c="dimmed" ml="auto">
|
||||
{cards.length}
|
||||
</Text>
|
||||
</Group>
|
||||
<Stack gap="xs">
|
||||
{cards.length === 0 ? (
|
||||
<Paper withBorder radius="md" p="sm" style={{ borderStyle: 'dashed' }}>
|
||||
<Text size="xs" c="dimmed" ta="center">{asI18n('—')}</Text>
|
||||
</Paper>
|
||||
) : (
|
||||
cards.map((b) => (
|
||||
<BookingCard key={b.bookingId} booking={b} locale={getLocale()} />
|
||||
))
|
||||
)}
|
||||
</Stack>
|
||||
</Stack>
|
||||
)
|
||||
})}
|
||||
</Group>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
function KanbanPage() {
|
||||
return (
|
||||
<PublicShell>
|
||||
<KanbanInner />
|
||||
</PublicShell>
|
||||
)
|
||||
}
|
||||
|
||||
export const Route = createFileRoute('/kanban')({
|
||||
component: KanbanPage,
|
||||
})
|
||||
Reference in New Issue
Block a user