frontend: pharmacy inventory, lab add-result & analysis charts

- Pharmacy: the sidebar entry now expands into Pharmacy + a new Inventory page
  (searchable medication stock with derived in-stock/low/out availability,
  backed by /api/inventory). Fix the dispensing-queue "Expiring" badge so it
  only flags courses ending within the next 7 days, not ones already elapsed.
- Lab "Add analysis result": the patient picker no longer lists patients until
  you type and supports arrow-key + Enter selection; the test field offers a
  catalog of common analyses with an Advanced option (custom analysis + ref
  range); submitted results now show immediately in a Recent results feed.
- Analysis: add a Live panel (real-time line chart) and replace the flat trend
  sparklines with bklit-ui area + bar charts (installed from the @bklit shadcn
  registry; chart CSS variables wired into the theme for light and dark).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Khalid Abdi
2026-06-12 19:22:07 +03:00
parent e2bcb2cfbc
commit 321a6298a4
81 changed files with 11538 additions and 67 deletions
+10
View File
@@ -0,0 +1,10 @@
import { InventoryView } from "@/components/pharmacy/inventory-view";
import { SidebarInset } from "@/components/ui/sidebar";
export default function InventoryPage() {
return (
<SidebarInset className="flex flex-1 flex-col overflow-y-auto">
<InventoryView />
</SidebarInset>
);
}
+43
View File
@@ -64,6 +64,22 @@
--animate-toast-error-odd: toast-error-odd 0.28s cubic-bezier(0.5, 1, 0.89, 1);
--animate-toast-error-even: toast-error-even 0.28s cubic-bezier(0.5, 1, 0.89, 1)
;
--color-chart-label: var(--chart-label);
--color-chart-ring-background: var(--chart-ring-background);
--color-chart-marker-foreground: var(--chart-marker-foreground);
--color-chart-marker-border: var(--chart-marker-border);
--color-chart-marker-background: var(--chart-marker-background);
--color-chart-tooltip-muted: var(--chart-tooltip-muted);
--color-chart-tooltip-foreground: var(--chart-tooltip-foreground);
--color-chart-tooltip-background: var(--chart-tooltip-background);
--chart-brush-border: var(--chart-brush-border);
--color-chart-grid: var(--chart-grid);
--color-chart-crosshair: var(--chart-crosshair);
--chart-line-secondary: var(--chart-line-secondary);
--chart-line-primary: var(--chart-line-primary);
--color-chart-foreground-muted: var(--chart-foreground-muted);
--color-chart-foreground: var(--chart-foreground);
--color-chart-background: var(--chart-background);
@keyframes toast-success-odd {
0% {
scale: 1;}
@@ -145,6 +161,22 @@
--success-foreground: var(--color-emerald-700);
--warning: var(--color-amber-500);
--warning-foreground: var(--color-amber-700);
--chart-background: oklch(1 0 0);
--chart-foreground: oklch(0.145 0.004 285);
--chart-foreground-muted: oklch(0.55 0.014 260);
--chart-line-primary: var(--chart-1);
--chart-line-secondary: var(--chart-2);
--chart-crosshair: oklch(0.4 0.1828 274.34);
--chart-grid: oklch(0.9 0 0);
--chart-brush-border: var(--chart-grid);
--chart-tooltip-background: oklch(0.21 0.006 285 / 0.8);
--chart-tooltip-foreground: oklch(0.985 0 0);
--chart-tooltip-muted: oklch(0.65 0.01 260);
--chart-marker-background: oklch(0.97 0.005 260);
--chart-marker-border: oklch(0.85 0.01 260);
--chart-marker-foreground: oklch(0.3 0.01 260);
--chart-ring-background: oklch(0.9 0.005 260 / 0.25);
--chart-label: oklch(0.45 0.01 260);
}
/* COSS neutral dark palette: near-black canvas, neutral accent, hairline borders. */
@@ -187,6 +219,17 @@
--success-foreground: var(--color-emerald-400);
--warning: var(--color-amber-500);
--warning-foreground: var(--color-amber-400);
--chart-background: oklch(0.145 0 0);
--chart-foreground: oklch(0.45 0 0);
--chart-foreground-muted: oklch(0.65 0.01 260);
--chart-crosshair: oklch(0.45 0 0);
--chart-grid: oklch(0.25 0 0);
--chart-brush-border: var(--chart-grid);
--chart-marker-background: oklch(0.25 0.01 260);
--chart-marker-border: oklch(0.4 0.01 260);
--chart-marker-foreground: oklch(0.9 0 0);
--chart-ring-background: oklch(0.35 0.01 260 / 0.25);
--chart-label: oklch(0.75 0.01 260);
}
@layer base {