first commit
This commit is contained in:
@@ -0,0 +1,16 @@
|
||||
import { drizzle } from 'drizzle-orm/postgres-js';
|
||||
import postgres from 'postgres';
|
||||
import * as schema from './schema';
|
||||
|
||||
const connectionString = process.env.DATABASE_URL!;
|
||||
|
||||
if (!connectionString) {
|
||||
throw new Error('DATABASE_URL is required');
|
||||
}
|
||||
|
||||
// For query client (server)
|
||||
const client = postgres(connectionString);
|
||||
|
||||
export const db = drizzle(client, { schema });
|
||||
|
||||
export * from './schema';
|
||||
@@ -0,0 +1,39 @@
|
||||
import { pgTable, serial, text, timestamp, numeric, integer, uuid, jsonb } from 'drizzle-orm/pg-core';
|
||||
|
||||
export const trips = pgTable('trips', {
|
||||
id: uuid('id').primaryKey().defaultRandom(),
|
||||
destination: text('destination').notNull(),
|
||||
lat: numeric('lat'),
|
||||
lng: numeric('lng'),
|
||||
startDate: timestamp('start_date', { withTimezone: true }),
|
||||
numDays: integer('num_days').notNull(),
|
||||
totalBudget: numeric('total_budget'),
|
||||
currency: text('currency').default('EUR'),
|
||||
preferences: jsonb('preferences').$type<string[]>().default([]),
|
||||
experienceMode: text('experience_mode').default('balanced'),
|
||||
estimatedTotalCost: numeric('estimated_total_cost'),
|
||||
createdAt: timestamp('created_at', { withTimezone: true }).defaultNow(),
|
||||
});
|
||||
|
||||
export const activities = pgTable('activities', {
|
||||
id: serial('id').primaryKey(),
|
||||
tripId: uuid('trip_id').references(() => trips.id, { onDelete: 'cascade' }),
|
||||
dayNumber: integer('day_number').notNull(),
|
||||
startTime: text('start_time'),
|
||||
title: text('title').notNull(),
|
||||
type: text('type').notNull(), // attraction | restaurant | bar | event | meal | other
|
||||
estimatedCost: numeric('estimated_cost'),
|
||||
address: text('address'),
|
||||
lat: numeric('lat'),
|
||||
lng: numeric('lng'),
|
||||
rating: numeric('rating'),
|
||||
source: text('source'),
|
||||
sourceUrl: text('source_url'),
|
||||
notes: text('notes'),
|
||||
orderInDay: integer('order_in_day').default(0),
|
||||
});
|
||||
|
||||
export type Trip = typeof trips.$inferSelect;
|
||||
export type NewTrip = typeof trips.$inferInsert;
|
||||
export type Activity = typeof activities.$inferSelect;
|
||||
export type NewActivity = typeof activities.$inferInsert;
|
||||
@@ -0,0 +1,40 @@
|
||||
export const EXPERIENCE_MODES = [
|
||||
{
|
||||
id: 'balanced',
|
||||
label: 'Balanced',
|
||||
description: 'A good mix of everything',
|
||||
icon: '⚖️',
|
||||
},
|
||||
{
|
||||
id: 'cultural',
|
||||
label: 'Cultural Explorer',
|
||||
description: 'Museums, history, landmarks, heritage',
|
||||
icon: '🏛️',
|
||||
},
|
||||
{
|
||||
id: 'foodie',
|
||||
label: 'Foodie',
|
||||
description: 'Restaurants, local eats, markets, cafes',
|
||||
icon: '🍽️',
|
||||
},
|
||||
{
|
||||
id: 'adventurous',
|
||||
label: 'Adventurous / Bold',
|
||||
description: 'Unique spots, active, hidden gems, evening fun',
|
||||
icon: '🧭',
|
||||
},
|
||||
{
|
||||
id: 'relaxed',
|
||||
label: 'Relaxed Local',
|
||||
description: 'Slower pace, parks, free time, fewer stops',
|
||||
icon: '🌳',
|
||||
},
|
||||
{
|
||||
id: 'nightlife',
|
||||
label: 'Nightlife & Vibes',
|
||||
description: 'Bars, concerts, theater, late experiences',
|
||||
icon: '🌙',
|
||||
},
|
||||
] as const;
|
||||
|
||||
export type ExperienceMode = typeof EXPERIENCE_MODES[number]['id'];
|
||||
Reference in New Issue
Block a user