Implement fullstack education catalog with disease detail pages, API routes, and shared types
- Add disease detail page component with data fetching and error handling - Create shared types for diseases and classifications - Implement API routes for diseases, classifications, and dashboard summary - Develop reusable components for risk badge and disease card - Build catalog page with search and filter functionality - Update dashboard page with data-backed summary and manual classification form - Register new routes in the web application - Ensure type safety and consistency across shared modules
This commit is contained in:
@@ -3,11 +3,13 @@ import postgres from 'postgres';
|
||||
import { env } from '../config/env';
|
||||
import * as schema from './schema';
|
||||
|
||||
let db: ReturnType<typeof drizzle<typeof schema>> | undefined;
|
||||
|
||||
export function createDbClient() {
|
||||
if (!env.databaseUrl) {
|
||||
throw new Error('DATABASE_URL is required to create a database client');
|
||||
}
|
||||
|
||||
const client = postgres(env.databaseUrl);
|
||||
return drizzle(client, { schema });
|
||||
db ??= drizzle(postgres(env.databaseUrl), { schema });
|
||||
return db;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,32 @@
|
||||
import { pgTable, timestamp, uuid, varchar } from 'drizzle-orm/pg-core';
|
||||
import { pgTable, timestamp, uuid, varchar, text, integer } from 'drizzle-orm/pg-core';
|
||||
|
||||
export const appEvents = pgTable('app_events', {
|
||||
id: uuid('id').primaryKey().defaultRandom(),
|
||||
name: varchar('name', { length: 120 }).notNull(),
|
||||
createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
|
||||
});
|
||||
|
||||
export const diseaseCatalog = pgTable('disease_catalog', {
|
||||
slug: varchar('slug', { length: 80 }).primaryKey(),
|
||||
label: varchar('label', { length: 80 }).notNull(),
|
||||
commonName: varchar('common_name', { length: 120 }).notNull(),
|
||||
summary: text('summary').notNull(),
|
||||
description: text('description').notNull(),
|
||||
symptoms: text('symptoms').array().notNull(),
|
||||
recommendations: text('recommendations').array().notNull(),
|
||||
riskLevel: varchar('risk_level', { length: 20 }).notNull(),
|
||||
accentColor: varchar('accent_color', { length: 40 }).notNull(),
|
||||
displayOrder: integer('display_order').notNull(),
|
||||
createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
|
||||
updatedAt: timestamp('updated_at', { withTimezone: true }).notNull().defaultNow(),
|
||||
});
|
||||
|
||||
export const manualClassifications = pgTable('manual_classifications', {
|
||||
id: uuid('id').primaryKey().defaultRandom(),
|
||||
diseaseSlug: varchar('disease_slug', { length: 80 })
|
||||
.notNull()
|
||||
.references(() => diseaseCatalog.slug),
|
||||
observation: text('observation').notNull(),
|
||||
location: varchar('location', { length: 160 }).notNull(),
|
||||
createdAt: timestamp('created_at', { withTimezone: true }).notNull().defaultNow(),
|
||||
});
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
import { diseaseCatalogSeed } from '@zeavis/shared';
|
||||
import type { InferInsertModel } from 'drizzle-orm';
|
||||
import { diseaseCatalog } from './schema';
|
||||
|
||||
export type DiseaseCatalogRow = InferInsertModel<typeof diseaseCatalog>;
|
||||
|
||||
export const diseaseCatalogRows: DiseaseCatalogRow[] = diseaseCatalogSeed.map((item) => ({
|
||||
slug: item.slug,
|
||||
label: item.label,
|
||||
commonName: item.commonName,
|
||||
summary: item.summary,
|
||||
description: item.description,
|
||||
symptoms: item.symptoms,
|
||||
recommendations: item.recommendations,
|
||||
riskLevel: item.riskLevel,
|
||||
accentColor: item.accentColor,
|
||||
displayOrder: item.displayOrder,
|
||||
}));
|
||||
Reference in New Issue
Block a user