mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-06-19 21:31:46 +00:00
e78c2a97bd
Rebrand: - NOMAD → TREK branding across all UI, translations, server, PWA manifest - New TREK logos (dark/light, with/without icon) - Liquid glass toast notifications Bugs Fixed: - HTTPS redirect now opt-in only (FORCE_HTTPS=true), fixes #33 #43 #52 #54 #55 - PDF export "Tag" fallback uses i18n, fixes #15 - Vacay sharing color collision detection, fixes #25 - Backup settings import fix (PR #47) - Atlas country detection uses smallest bounding box, fixes #31 - JPY and zero-decimal currencies formatted correctly, fixes #32 - HTML lang="en" instead of hardcoded "de", fixes #34 - Duplicate translation keys removed - setSelectedAssignmentId crash fixed New Features: - OSM enrichment: Overpass API for opening hours, Wikimedia Commons for photos - Reverse geocoding on map right-click to add places - OIDC config via environment variables (OIDC_ISSUER, OIDC_CLIENT_ID, etc.), fixes #48 - Multi-arch Docker build (ARM64 + AMD64), fixes #11 - File management: star, trash/restore, upload owner, assign to places/bookings, notes - Markdown rendering in Collab Notes with expand modal, fixes #17 - Type-specific booking fields (flight: airline/number/airports, hotel: check-in/out/days, train: number/platform/seat), fixes #35 - Hotel bookings auto-create accommodations, bidirectional sync - Multiple hotels per day with check-in/check-out color coding - Ko-fi and Buy Me a Coffee support cards - GitHub releases proxy with server-side caching
348 lines
6.6 KiB
TypeScript
348 lines
6.6 KiB
TypeScript
// Shared types for the TREK travel planner
|
|
|
|
export interface User {
|
|
id: number
|
|
username: string
|
|
email: string
|
|
role: 'admin' | 'user'
|
|
avatar_url: string | null
|
|
maps_api_key: string | null
|
|
created_at: string
|
|
}
|
|
|
|
export interface Trip {
|
|
id: number
|
|
name: string
|
|
description: string | null
|
|
start_date: string
|
|
end_date: string
|
|
cover_url: string | null
|
|
is_archived: boolean
|
|
owner_id: number
|
|
created_at: string
|
|
updated_at: string
|
|
}
|
|
|
|
export interface Day {
|
|
id: number
|
|
trip_id: number
|
|
date: string
|
|
title: string | null
|
|
notes: string | null
|
|
assignments: Assignment[]
|
|
notes_items: DayNote[]
|
|
}
|
|
|
|
export interface Place {
|
|
id: number
|
|
trip_id: number
|
|
name: string
|
|
description: string | null
|
|
lat: number | null
|
|
lng: number | null
|
|
address: string | null
|
|
category_id: number | null
|
|
icon: string | null
|
|
price: string | null
|
|
image_url: string | null
|
|
google_place_id: string | null
|
|
osm_id: string | null
|
|
place_time: string | null
|
|
end_time: string | null
|
|
created_at: string
|
|
}
|
|
|
|
export interface Assignment {
|
|
id: number
|
|
day_id: number
|
|
place_id?: number
|
|
order_index: number
|
|
notes: string | null
|
|
place: Place
|
|
}
|
|
|
|
export interface DayNote {
|
|
id: number
|
|
day_id: number
|
|
text: string
|
|
time: string | null
|
|
icon: string | null
|
|
sort_order?: number
|
|
created_at: string
|
|
}
|
|
|
|
export interface PackingItem {
|
|
id: number
|
|
trip_id: number
|
|
name: string
|
|
category: string | null
|
|
checked: number
|
|
quantity: number
|
|
}
|
|
|
|
export interface Tag {
|
|
id: number
|
|
name: string
|
|
color: string | null
|
|
user_id: number
|
|
}
|
|
|
|
export interface Category {
|
|
id: number
|
|
name: string
|
|
icon: string | null
|
|
user_id: number
|
|
}
|
|
|
|
export interface BudgetItem {
|
|
id: number
|
|
trip_id: number
|
|
name: string
|
|
amount: number
|
|
currency: string
|
|
category: string | null
|
|
paid_by: number | null
|
|
persons: number
|
|
members: BudgetMember[]
|
|
}
|
|
|
|
export interface BudgetMember {
|
|
user_id: number
|
|
paid: boolean
|
|
}
|
|
|
|
export interface Reservation {
|
|
id: number
|
|
trip_id: number
|
|
name: string
|
|
title?: string
|
|
type: string | null
|
|
status: 'pending' | 'confirmed'
|
|
date: string | null
|
|
time: string | null
|
|
confirmation_number: string | null
|
|
notes: string | null
|
|
url: string | null
|
|
accommodation_id?: number | null
|
|
metadata?: Record<string, string> | null
|
|
created_at: string
|
|
}
|
|
|
|
export interface TripFile {
|
|
id: number
|
|
trip_id: number
|
|
place_id?: number | null
|
|
reservation_id?: number | null
|
|
note_id?: number | null
|
|
uploaded_by?: number | null
|
|
uploaded_by_name?: string | null
|
|
uploaded_by_avatar?: string | null
|
|
filename: string
|
|
original_name: string
|
|
file_size?: number | null
|
|
mime_type: string
|
|
description?: string | null
|
|
starred?: number
|
|
deleted_at?: string | null
|
|
created_at: string
|
|
reservation_title?: string
|
|
url?: string
|
|
}
|
|
|
|
export interface Settings {
|
|
map_tile_url: string
|
|
default_lat: number
|
|
default_lng: number
|
|
default_zoom: number
|
|
dark_mode: boolean | string
|
|
default_currency: string
|
|
language: string
|
|
temperature_unit: string
|
|
time_format: string
|
|
show_place_description: boolean
|
|
route_calculation?: boolean
|
|
}
|
|
|
|
export interface AssignmentsMap {
|
|
[dayId: string]: Assignment[]
|
|
}
|
|
|
|
export interface DayNotesMap {
|
|
[dayId: string]: DayNote[]
|
|
}
|
|
|
|
export interface RouteSegment {
|
|
mid: [number, number]
|
|
from: [number, number]
|
|
to: [number, number]
|
|
walkingText: string
|
|
drivingText: string
|
|
}
|
|
|
|
export interface RouteResult {
|
|
coordinates: [number, number][]
|
|
distance: number
|
|
duration: number
|
|
distanceText: string
|
|
durationText: string
|
|
walkingText: string
|
|
drivingText: string
|
|
}
|
|
|
|
export interface Waypoint {
|
|
lat: number
|
|
lng: number
|
|
}
|
|
|
|
// User with optional OIDC fields
|
|
export interface UserWithOidc extends User {
|
|
oidc_issuer?: string | null
|
|
}
|
|
|
|
// Accommodation type
|
|
export interface Accommodation {
|
|
id: number
|
|
trip_id: number
|
|
name: string
|
|
address: string | null
|
|
check_in: string | null
|
|
check_out: string | null
|
|
confirmation_number: string | null
|
|
notes: string | null
|
|
url: string | null
|
|
created_at: string
|
|
}
|
|
|
|
// Trip member (owner or collaborator)
|
|
export interface TripMember {
|
|
id: number
|
|
username: string
|
|
email?: string
|
|
avatar_url?: string | null
|
|
role?: string
|
|
}
|
|
|
|
// Photo type
|
|
export interface Photo {
|
|
id: number
|
|
trip_id: number
|
|
filename: string
|
|
original_name: string
|
|
mime_type: string
|
|
size: number
|
|
caption: string | null
|
|
place_id: number | null
|
|
day_id: number | null
|
|
created_at: string
|
|
}
|
|
|
|
// Atlas place detail
|
|
export interface AtlasPlace {
|
|
id: number
|
|
name: string
|
|
lat: number | null
|
|
lng: number | null
|
|
}
|
|
|
|
// GeoJSON types (simplified for atlas map)
|
|
export interface GeoJsonFeature {
|
|
type: 'Feature'
|
|
properties: Record<string, string | number | null | undefined>
|
|
geometry: {
|
|
type: string
|
|
coordinates: unknown
|
|
}
|
|
id?: string
|
|
}
|
|
|
|
export interface GeoJsonFeatureCollection {
|
|
type: 'FeatureCollection'
|
|
features: GeoJsonFeature[]
|
|
}
|
|
|
|
// App config from /auth/app-config
|
|
export interface AppConfig {
|
|
has_users: boolean
|
|
allow_registration: boolean
|
|
demo_mode: boolean
|
|
oidc_configured: boolean
|
|
oidc_display_name?: string
|
|
has_maps_key?: boolean
|
|
allowed_file_types?: string
|
|
}
|
|
|
|
// Translation function type
|
|
export type TranslationFn = (key: string, params?: Record<string, string | number | null>) => string
|
|
|
|
// WebSocket event type
|
|
export interface WebSocketEvent {
|
|
type: string
|
|
[key: string]: unknown
|
|
}
|
|
|
|
// Vacay types
|
|
export interface VacayPlan {
|
|
id: number
|
|
holidays_enabled: boolean
|
|
holidays_region: string | null
|
|
name?: string
|
|
year?: number
|
|
owner_id?: number
|
|
created_at?: string
|
|
updated_at?: string
|
|
}
|
|
|
|
export interface VacayUser {
|
|
id: number
|
|
username: string
|
|
color: string | null
|
|
}
|
|
|
|
export interface VacayEntry {
|
|
date: string
|
|
user_id: number
|
|
}
|
|
|
|
export interface VacayStat {
|
|
user_id: number
|
|
vacation_days: number
|
|
used: number
|
|
}
|
|
|
|
export interface HolidayInfo {
|
|
name: string
|
|
localName: string
|
|
}
|
|
|
|
export interface HolidaysMap {
|
|
[date: string]: HolidayInfo
|
|
}
|
|
|
|
// API error shape from axios
|
|
export interface ApiError {
|
|
response?: {
|
|
data?: {
|
|
error?: string
|
|
}
|
|
status?: number
|
|
}
|
|
message: string
|
|
}
|
|
|
|
/** Safely extract an error message from an unknown catch value */
|
|
export function getApiErrorMessage(err: unknown, fallback: string): string {
|
|
if (typeof err === 'object' && err !== null && 'response' in err) {
|
|
const apiErr = err as ApiError
|
|
if (apiErr.response?.data?.error) return apiErr.response.data.error
|
|
}
|
|
if (err instanceof Error) return err.message
|
|
return fallback
|
|
}
|
|
|
|
// MergedItem used in day notes hook
|
|
export interface MergedItem {
|
|
type: 'assignment' | 'note'
|
|
sortKey: number
|
|
data: Assignment | DayNote
|
|
}
|