chore: seminarhof customer project
This commit is contained in:
24
apps/app/src/lib/auth.ts
Normal file
24
apps/app/src/lib/auth.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import { createAuthClient } from 'better-auth/client'
|
||||
|
||||
function apiUrl(): string {
|
||||
if (typeof window !== 'undefined' && (window as any).__E2E_API_URL) {
|
||||
return (window as any).__E2E_API_URL as string
|
||||
}
|
||||
return import.meta.env.VITE_API_URL ?? '/api'
|
||||
}
|
||||
|
||||
// Better Auth mounts at /api/auth (default basePath); the client appends the
|
||||
// endpoint verbatim to baseURL, so the base must include /auth — otherwise it
|
||||
// emits /api/sign-in/email and 404s on both sandbox and deploy.
|
||||
export const authClient = createAuthClient({
|
||||
baseURL: `${apiUrl()}/auth`,
|
||||
})
|
||||
|
||||
export async function signIn(email: string, password: string): Promise<void> {
|
||||
const { error } = await authClient.signIn.email({ email, password })
|
||||
if (error) throw new Error('Invalid credentials')
|
||||
}
|
||||
|
||||
export async function signOut(): Promise<void> {
|
||||
await authClient.signOut()
|
||||
}
|
||||
24
apps/app/src/lib/dates.ts
Normal file
24
apps/app/src/lib/dates.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
// Placeholder shown for missing dates (an enquiry may not have dates yet).
|
||||
const NO_DATE = '—'
|
||||
|
||||
export function fmtDate(d: Date | null | undefined, locale: string): string {
|
||||
if (!d) return NO_DATE
|
||||
return d.toLocaleDateString(locale.startsWith('de') ? 'de-DE' : 'en-GB', {
|
||||
day: 'numeric',
|
||||
month: 'short',
|
||||
year: 'numeric',
|
||||
})
|
||||
}
|
||||
|
||||
export function fmtDateShort(d: Date | null | undefined, locale: string): string {
|
||||
if (!d) return NO_DATE
|
||||
return d.toLocaleDateString(locale.startsWith('de') ? 'de-DE' : 'en-GB', {
|
||||
day: 'numeric',
|
||||
month: 'short',
|
||||
})
|
||||
}
|
||||
|
||||
export function toDateInputStr(d: Date | null | undefined): string {
|
||||
if (!d) return ''
|
||||
return d.toISOString().slice(0, 10)
|
||||
}
|
||||
35
apps/app/src/lib/status.ts
Normal file
35
apps/app/src/lib/status.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
// Brand-aligned status palettes. Single source of truth for booking, invoice
|
||||
// and room state colours. Each entry: bg (badge background), fg (text colour)
|
||||
// and dot (8 px leading dot). Hex codes are checked for ≥4.5:1 on bg.
|
||||
|
||||
export type BookingStatus =
|
||||
| 'enquiry'
|
||||
| 'reserved'
|
||||
| 'confirmed'
|
||||
| 'ended'
|
||||
| 'cancelled'
|
||||
|
||||
export const BOOKING_STATUS: Record<BookingStatus, { bg: string; fg: string; dot: string }> = {
|
||||
enquiry: { bg: '#F7EEF5', fg: '#583651', dot: '#BC7AA6' },
|
||||
reserved: { bg: '#FFEDDC', fg: '#8A4A1A', dot: '#FFBC7D' },
|
||||
confirmed: { bg: '#E9D4E2', fg: '#4E3149', dot: '#88557F' },
|
||||
ended: { bg: '#EDEDED', fg: '#4A4A4A', dot: '#989499' },
|
||||
cancelled: { bg: '#F2F2F2', fg: '#6B6B6B', dot: '#B5B5B5' },
|
||||
}
|
||||
|
||||
/** Valid transitions per status (client-side, for CTA buttons). Enforced server-side too. */
|
||||
export const BOOKING_TRANSITIONS: Record<BookingStatus, BookingStatus[]> = {
|
||||
enquiry: ['reserved', 'cancelled'],
|
||||
reserved: ['confirmed', 'cancelled'],
|
||||
confirmed: ['ended', 'cancelled'],
|
||||
ended: [],
|
||||
cancelled: [],
|
||||
}
|
||||
|
||||
export type InvoiceStatus = 'outstanding' | 'paid' | 'cancelled'
|
||||
|
||||
export const INVOICE_STATUS: Record<InvoiceStatus, { bg: string; fg: string; dot: string }> = {
|
||||
outstanding: { bg: '#FFF8E0', fg: '#806600', dot: '#FFCE00' },
|
||||
paid: { bg: '#E9D4E2', fg: '#4E3149', dot: '#88557F' },
|
||||
cancelled: { bg: '#F2F2F2', fg: '#6B6B6B', dot: '#B5B5B5' },
|
||||
}
|
||||
Reference in New Issue
Block a user