chore: seminarhof customer project
This commit is contained in:
276
apps/app/src/pages/index.tsx
Normal file
276
apps/app/src/pages/index.tsx
Normal file
@@ -0,0 +1,276 @@
|
||||
import { Navigate, createFileRoute } from '@tanstack/react-router'
|
||||
import { m } from '@/i18n/messages'
|
||||
import { useLocale, getLocale } from '@/i18n/config'
|
||||
import { asI18n } from '@pikku/react'
|
||||
import {
|
||||
Badge,
|
||||
Box,
|
||||
Button,
|
||||
Container,
|
||||
Paper,
|
||||
Progress,
|
||||
Stack,
|
||||
Table,
|
||||
Text,
|
||||
Title,
|
||||
} from '@pikku/mantine/core'
|
||||
import { BrandHeader } from '../components/Brand'
|
||||
import { RequireAuth, useAuth } from '../auth'
|
||||
import { usePageTitle } from '../components/AppLayout'
|
||||
import { fmtDate } from '../lib/dates'
|
||||
import { usePikkuQuery } from '@project/functions-sdk/pikku/api.gen'
|
||||
|
||||
function MarketingPage({ hero, sections }: { hero: React.ReactNode; sections: React.ReactNode[] }) {
|
||||
return (
|
||||
<Box>
|
||||
{hero}
|
||||
<Container size="md" py="xl">
|
||||
<Stack gap="lg">{sections}</Stack>
|
||||
</Container>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
function IndexPage() {
|
||||
useLocale()
|
||||
|
||||
return (
|
||||
<MarketingPage
|
||||
hero={(
|
||||
<Box bg="var(--brand-plum)" py="xl" c="white">
|
||||
<Container size="md">
|
||||
<BrandHeader variant="white" />
|
||||
</Container>
|
||||
</Box>
|
||||
)}
|
||||
sections={[
|
||||
<Paper key="launchpad" withBorder radius="md" p="lg">
|
||||
<Stack gap="md">
|
||||
<Text size="xs" c="dimmed" fw={600} tt="uppercase">
|
||||
{m.common__pages__launchpad__title()}
|
||||
</Text>
|
||||
<Paper withBorder radius="md" p="md">
|
||||
<Stack gap="xs">
|
||||
<Title order={4}>{m.common__pages__launchpad__client__title()}</Title>
|
||||
<Text size="sm" c="dimmed">
|
||||
{m.common__pages__launchpad__client__body()}
|
||||
</Text>
|
||||
<Box>
|
||||
<Button component="a" href="/admin/bookings" size="sm" variant="light">
|
||||
{m.common__pages__launchpad__client__cta()}
|
||||
</Button>
|
||||
</Box>
|
||||
</Stack>
|
||||
</Paper>
|
||||
<Paper withBorder radius="md" p="md">
|
||||
<Stack gap="xs">
|
||||
<Title order={4}>{m.common__pages__launchpad__availability__title()}</Title>
|
||||
<Text size="sm" c="dimmed">
|
||||
{m.common__pages__launchpad__availability__body()}
|
||||
</Text>
|
||||
<Box>
|
||||
<Button component="a" href="/availability" size="sm" variant="light">
|
||||
{m.common__pages__launchpad__availability__cta()}
|
||||
</Button>
|
||||
</Box>
|
||||
</Stack>
|
||||
</Paper>
|
||||
<Paper withBorder radius="md" p="md">
|
||||
<Stack gap="xs">
|
||||
<Title order={4}>{m.common__pages__launchpad__admin__title()}</Title>
|
||||
<Text size="sm" c="dimmed">
|
||||
{m.common__pages__launchpad__admin__body()}
|
||||
</Text>
|
||||
<Box>
|
||||
<Button component="a" href="/admin/bookings" size="sm" variant="light">
|
||||
{m.common__pages__launchpad__admin__cta()}
|
||||
</Button>
|
||||
</Box>
|
||||
</Stack>
|
||||
</Paper>
|
||||
<Paper withBorder radius="md" p="md">
|
||||
<Stack gap="xs">
|
||||
<Title order={4}>{m.common__pages__launchpad__events__title()}</Title>
|
||||
<Text size="sm" c="dimmed">
|
||||
{m.common__pages__launchpad__events__body()}
|
||||
</Text>
|
||||
<Box>
|
||||
<Button component="a" href="/events" size="sm" variant="light">
|
||||
{m.common__pages__launchpad__events__cta()}
|
||||
</Button>
|
||||
</Box>
|
||||
</Stack>
|
||||
</Paper>
|
||||
<Paper withBorder radius="md" p="md">
|
||||
<Stack gap="xs">
|
||||
<Title order={4}>{m.common__pages__launchpad__enquiry__title()}</Title>
|
||||
<Text size="sm" c="dimmed">
|
||||
{m.common__pages__launchpad__enquiry__body()}
|
||||
</Text>
|
||||
<Box>
|
||||
<Button component="a" href="/enquiry" size="sm" variant="light">
|
||||
{m.common__pages__launchpad__enquiry__cta()}
|
||||
</Button>
|
||||
</Box>
|
||||
</Stack>
|
||||
</Paper>
|
||||
</Stack>
|
||||
</Paper>,
|
||||
]}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
function ClientOverview() {
|
||||
useLocale()
|
||||
usePageTitle(m.common__pages__client__title())
|
||||
|
||||
const { data, isLoading, error } = usePikkuQuery('getClientOverview', null)
|
||||
|
||||
if (isLoading && !data) {
|
||||
return <Container py="xl"><Text>{m.common__states__loading()}</Text></Container>
|
||||
}
|
||||
if (error && !data) {
|
||||
return <Container py="xl"><Text c="red">{m.common__states__error()}</Text></Container>
|
||||
}
|
||||
if (!data) return null
|
||||
|
||||
return (
|
||||
<Container size="lg" py="md">
|
||||
<Stack gap="lg">
|
||||
<Paper withBorder radius="md" p="lg">
|
||||
<Stack gap="xs">
|
||||
<Text size="xs" c="dimmed" fw={600} tt="uppercase">
|
||||
{m.common__pages__client__title()}
|
||||
</Text>
|
||||
<Title order={2}>{m.common__brand__name()}</Title>
|
||||
{data.clients.length > 0 && (
|
||||
<Text c="dimmed">
|
||||
{asI18n(data.clients.map((client) => client.name).join(', '))}
|
||||
</Text>
|
||||
)}
|
||||
</Stack>
|
||||
</Paper>
|
||||
|
||||
<Paper withBorder radius="md" p="lg">
|
||||
<Stack gap="md">
|
||||
<Title order={3}>{m.common__pages__client__upcoming()}</Title>
|
||||
{data.upcomingBookings.length === 0 ? (
|
||||
<Text c="dimmed">{m.common__pages__client__no_upcoming()}</Text>
|
||||
) : (
|
||||
data.upcomingBookings.map((booking) => (
|
||||
<Paper key={booking.bookingId} withBorder radius="md" p="md">
|
||||
<Stack gap="xs">
|
||||
<Stack gap={4}>
|
||||
<Text fw={600}>{asI18n(`${booking.eventName}`)}</Text>
|
||||
<Text size="sm" c="dimmed">
|
||||
{asI18n(`${fmtDate(booking.startDate, getLocale())} - ${fmtDate(booking.endDate, getLocale())}`)}
|
||||
</Text>
|
||||
</Stack>
|
||||
{booking.isCurrentEvent && (
|
||||
<Box>
|
||||
<Badge color="plum" variant="light">
|
||||
{m.common__pages__client__current_badge()}
|
||||
</Badge>
|
||||
</Box>
|
||||
)}
|
||||
<Text size="sm">
|
||||
{booking.expectedPersons
|
||||
? m.common__pages__client__participants_count({
|
||||
count: booking.participantCount,
|
||||
target: booking.expectedPersons,
|
||||
})
|
||||
: m.common__pages__client__participants_count_unknown({
|
||||
count: booking.participantCount,
|
||||
})}
|
||||
</Text>
|
||||
<Stack gap={4}>
|
||||
<Text size="sm" c="dimmed">
|
||||
{m.common__pages__client__completeness()}
|
||||
</Text>
|
||||
<Progress
|
||||
color="plum"
|
||||
radius="xl"
|
||||
size="lg"
|
||||
value={booking.completenessPercent}
|
||||
/>
|
||||
</Stack>
|
||||
</Stack>
|
||||
</Paper>
|
||||
))
|
||||
)}
|
||||
</Stack>
|
||||
</Paper>
|
||||
|
||||
<Paper withBorder radius="md" p="lg">
|
||||
<Stack gap="md">
|
||||
<Title order={3}>{m.common__pages__client__recent_invoices()}</Title>
|
||||
{data.recentInvoices.length === 0 ? (
|
||||
<Text c="dimmed">{m.common__pages__client__no_invoices()}</Text>
|
||||
) : (
|
||||
<Table>
|
||||
<Table.Thead>
|
||||
<Table.Tr>
|
||||
<Table.Th>{m.common__pages__client__invoice_cols__booking()}</Table.Th>
|
||||
<Table.Th>{m.common__pages__client__invoice_cols__number()}</Table.Th>
|
||||
<Table.Th>{m.common__pages__client__invoice_cols__kind()}</Table.Th>
|
||||
<Table.Th>{m.common__pages__client__invoice_cols__amount()}</Table.Th>
|
||||
<Table.Th>{m.common__pages__client__invoice_cols__status()}</Table.Th>
|
||||
<Table.Th>{m.common__pages__client__invoice_cols__issued()}</Table.Th>
|
||||
</Table.Tr>
|
||||
</Table.Thead>
|
||||
<Table.Tbody>
|
||||
{data.recentInvoices.map((invoice) => (
|
||||
<Table.Tr key={invoice.invoiceId}>
|
||||
<Table.Td>{invoice.bookingName}</Table.Td>
|
||||
<Table.Td>
|
||||
{invoice.pdfUrl ? (
|
||||
<Button
|
||||
component="a"
|
||||
href={invoice.pdfUrl}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
download={`${invoice.invoiceNumber}.pdf`}
|
||||
variant="subtle"
|
||||
px={0}
|
||||
>
|
||||
{asI18n(`${invoice.invoiceNumber}`)}
|
||||
</Button>
|
||||
) : (
|
||||
invoice.invoiceNumber
|
||||
)}
|
||||
</Table.Td>
|
||||
<Table.Td>{invoice.kind}</Table.Td>
|
||||
<Table.Td>{(invoice.amountCents / 100).toFixed(2)} €</Table.Td>
|
||||
<Table.Td>{invoice.status}</Table.Td>
|
||||
<Table.Td>{fmtDate(invoice.issuedOn, getLocale())}</Table.Td>
|
||||
</Table.Tr>
|
||||
))}
|
||||
</Table.Tbody>
|
||||
</Table>
|
||||
)}
|
||||
</Stack>
|
||||
</Paper>
|
||||
</Stack>
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
|
||||
function Dashboard() {
|
||||
const { role } = useAuth()
|
||||
if (role === 'admin' || role === 'owner') return <Navigate to="/admin/bookings" />
|
||||
if (role === 'client') return <ClientOverview />
|
||||
return <IndexPage />
|
||||
}
|
||||
|
||||
function IndexRoute() {
|
||||
return (
|
||||
<RequireAuth>
|
||||
<Dashboard />
|
||||
</RequireAuth>
|
||||
)
|
||||
}
|
||||
|
||||
export const Route = createFileRoute('/')({
|
||||
component: IndexRoute,
|
||||
})
|
||||
Reference in New Issue
Block a user