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:
Asep Haryana Saputra
2026-05-22 14:26:13 +00:00
parent d0c17d67b2
commit 84c1f46841
24 changed files with 2378 additions and 54 deletions
+26 -1
View File
@@ -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(),
});