{error.message}
+ +diff --git a/src/App.css b/src/App.css
index f90339d..a541039 100644
--- a/src/App.css
+++ b/src/App.css
@@ -1,184 +1,591 @@
-.counter {
- font-size: 16px;
- padding: 5px 10px;
- border-radius: 5px;
- color: var(--accent);
- background: var(--accent-bg);
- border: 2px solid transparent;
- transition: border-color 0.3s;
+:root {
+ --surface: #f4f4f6;
+ --surface-hover: #ededf0;
+
+ --danger-bg: #fff5f5;
+ --danger-border: #fecaca;
+ --danger-header: #fee2e2;
+ --danger-text: #991b1b;
+
+ --warning-bg: #fffbeb;
+ --warning-border: #fde68a;
+ --warning-header: #fef3c7;
+ --warning-text: #92400e;
+
+ --info-bg: #f0f9ff;
+ --info-border: #bae6fd;
+ --info-header: #e0f2fe;
+ --info-text: #075985;
+
+ --badge-low-bg: #dcfce7;
+ --badge-low-text: #166534;
+ --badge-medium-bg: #fef9c3;
+ --badge-medium-text: #854d0e;
+ --badge-high-bg: #fee2e2;
+ --badge-high-text: #991b1b;
+}
+
+@media (prefers-color-scheme: dark) {
+ :root {
+ --surface: #1e1f27;
+ --surface-hover: #25262f;
+
+ --danger-bg: #1f1315;
+ --danger-border: #5b2128;
+ --danger-header: #2d1517;
+ --danger-text: #fca5a5;
+
+ --warning-bg: #1c1509;
+ --warning-border: #5c3d10;
+ --warning-header: #271c0a;
+ --warning-text: #fcd34d;
+
+ --info-bg: #091520;
+ --info-border: #0c3a54;
+ --info-header: #0d1f2e;
+ --info-text: #7dd3fc;
+
+ --badge-low-bg: rgba(34, 197, 94, 0.15);
+ --badge-low-text: #86efac;
+ --badge-medium-bg: rgba(234, 179, 8, 0.15);
+ --badge-medium-text: #fde047;
+ --badge-high-bg: rgba(239, 68, 68, 0.15);
+ --badge-high-text: #fca5a5;
+ }
+}
+
+/* ── Layout ───────────────────────────────────────────────────────────────── */
+
+.app {
+ max-width: 1300px;
+ margin: 0 auto;
+ padding: 24px 20px 48px;
+}
+
+.app-header {
+ display: flex;
+ align-items: center;
+ gap: 16px;
margin-bottom: 24px;
- &:hover {
- border-color: var(--accent-border);
- }
- &:focus-visible {
- outline: 2px solid var(--accent);
- outline-offset: 2px;
+ h1 {
+ margin: 0;
+ font-size: 1.6rem;
+ letter-spacing: -0.5px;
+ color: var(--text-h);
}
}
-.hero {
- position: relative;
+.fetching-badge {
+ font-size: 0.75rem;
+ color: var(--text);
+ background: var(--surface);
+ border: 1px solid var(--border);
+ padding: 2px 10px;
+ border-radius: 12px;
+}
- .base,
- .framework,
- .vite {
- inset-inline: 0;
- margin: 0 auto;
- }
+/* ── Alert cards ──────────────────────────────────────────────────────────── */
- .base {
- width: 170px;
- position: relative;
- z-index: 0;
- }
+.alerts-panel {
+ display: grid;
+ grid-template-columns: repeat(3, 1fr);
+ gap: 14px;
+ margin-bottom: 28px;
- .framework,
- .vite {
- position: absolute;
- }
-
- .framework {
- z-index: 1;
- top: 34px;
- height: 28px;
- transform: perspective(2000px) rotateZ(300deg) rotateX(44deg) rotateY(39deg)
- scale(1.4);
- }
-
- .vite {
- z-index: 0;
- top: 107px;
- height: 26px;
- width: auto;
- transform: perspective(2000px) rotateZ(300deg) rotateX(40deg) rotateY(39deg)
- scale(0.8);
+ @media (max-width: 768px) {
+ grid-template-columns: 1fr;
}
}
-#center {
+.alert-card {
+ border-radius: 10px;
+ border: 1px solid;
+ overflow: hidden;
+}
+
+.alert-card--danger {
+ background: var(--danger-bg);
+ border-color: var(--danger-border);
+}
+.alert-card--warning {
+ background: var(--warning-bg);
+ border-color: var(--warning-border);
+}
+.alert-card--info {
+ background: var(--info-bg);
+ border-color: var(--info-border);
+}
+
+.alert-card__header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 10px 14px;
+ font-weight: 600;
+ font-size: 0.82rem;
+ letter-spacing: 0.02em;
+ text-transform: uppercase;
+
+ .alert-card--danger & {
+ background: var(--danger-header);
+ color: var(--danger-text);
+ border-bottom: 1px solid var(--danger-border);
+ }
+ .alert-card--warning & {
+ background: var(--warning-header);
+ color: var(--warning-text);
+ border-bottom: 1px solid var(--warning-border);
+ }
+ .alert-card--info & {
+ background: var(--info-header);
+ color: var(--info-text);
+ border-bottom: 1px solid var(--info-border);
+ }
+}
+
+.alert-card__count {
+ background: rgba(0 0 0 / 0.1);
+ border-radius: 10px;
+ padding: 1px 8px;
+ font-size: 0.8rem;
+ font-weight: 700;
+ text-transform: none;
+ letter-spacing: 0;
+}
+
+@media (prefers-color-scheme: dark) {
+ .alert-card__count {
+ background: rgba(255 255 255 / 0.1);
+ }
+}
+
+.alert-card__list {
+ list-style: none;
+ margin: 0;
+ padding: 10px 14px 12px;
+ display: flex;
+ flex-wrap: wrap;
+ gap: 6px;
+
+ li {
+ font-size: 0.8rem;
+ font-family: var(--mono);
+ padding: 2px 8px;
+ border-radius: 4px;
+
+ .alert-card--danger & {
+ background: var(--danger-header);
+ color: var(--danger-text);
+ }
+ .alert-card--warning & {
+ background: var(--warning-header);
+ color: var(--warning-text);
+ }
+ .alert-card--info & {
+ background: var(--info-header);
+ color: var(--info-text);
+ }
+ }
+}
+
+.alert-card__empty {
+ margin: 0;
+ padding: 10px 14px 12px;
+ font-size: 0.85rem;
+ color: var(--text);
+
+ &--error {
+ color: var(--danger-text);
+ }
+}
+
+/* ── State messages ───────────────────────────────────────────────────────── */
+
+.state-message {
+ text-align: center;
+ padding: 48px;
+ color: var(--text);
+
+ &.error {
+ color: var(--danger-text);
+
+ button {
+ margin-top: 12px;
+ padding: 6px 16px;
+ border-radius: 6px;
+ border: 1px solid var(--danger-border);
+ background: var(--danger-bg);
+ color: var(--danger-text);
+ cursor: pointer;
+
+ &:hover {
+ background: var(--danger-header);
+ }
+ }
+ }
+}
+
+/* ── Table ────────────────────────────────────────────────────────────────── */
+
+.table-wrapper {
+ overflow-x: auto;
+ border: 1px solid var(--border);
+ border-radius: 10px;
+}
+
+table {
+ width: 100%;
+ border-collapse: collapse;
+ font-size: 0.875rem;
+
+ th {
+ background: var(--surface);
+ color: var(--text);
+ text-align: left;
+ padding: 10px 14px;
+ font-weight: 600;
+ font-size: 0.78rem;
+ letter-spacing: 0.05em;
+ text-transform: uppercase;
+ border-bottom: 1px solid var(--border);
+ white-space: nowrap;
+ }
+
+ td {
+ padding: 10px 14px;
+ color: var(--text-h);
+ border-bottom: 1px solid var(--border);
+ vertical-align: middle;
+ }
+
+ tbody tr:last-child td {
+ border-bottom: none;
+ }
+
+ tbody tr:hover td {
+ background: var(--surface);
+ }
+}
+
+.missing {
+ color: var(--border);
+}
+
+/* ── Risk badge ───────────────────────────────────────────────────────────── */
+
+.risk-badge {
+ display: inline-block;
+ min-width: 24px;
+ text-align: center;
+ font-weight: 700;
+ font-size: 0.78rem;
+ padding: 2px 8px;
+ border-radius: 12px;
+}
+
+.risk-badge--low {
+ background: var(--badge-low-bg);
+ color: var(--badge-low-text);
+}
+.risk-badge--medium {
+ background: var(--badge-medium-bg);
+ color: var(--badge-medium-text);
+}
+.risk-badge--high {
+ background: var(--badge-high-bg);
+ color: var(--badge-high-text);
+}
+
+.data-issue-flag {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ margin-left: 6px;
+ font-size: 0.7rem;
+ font-weight: 800;
+ color: var(--warning-text);
+ background: var(--warning-header);
+ border: 1px solid var(--warning-border);
+ border-radius: 50%;
+ width: 16px;
+ height: 16px;
+ cursor: default;
+ vertical-align: middle;
+}
+
+/* ── Pagination ───────────────────────────────────────────────────────────── */
+
+.pagination {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ gap: 12px;
+ margin-top: 20px;
+
+ span {
+ font-size: 0.875rem;
+ color: var(--text);
+ }
+
+ button {
+ padding: 6px 18px;
+ border-radius: 6px;
+ border: 1px solid var(--accent-border);
+ background: var(--accent-bg);
+ color: var(--accent);
+ cursor: pointer;
+ font-size: 0.875rem;
+ font-weight: 500;
+ transition: background 0.15s, border-color 0.15s;
+
+ &:hover:not(:disabled) {
+ background: var(--accent-bg);
+ border-color: var(--accent);
+ filter: brightness(1.1);
+ }
+
+ &:disabled {
+ opacity: 0.35;
+ cursor: not-allowed;
+ }
+ }
+}
+
+/* ── Submission panel ─────────────────────────────────────────────────────── */
+
+.submission-panel {
+ margin-top: 40px;
+ border: 1px solid var(--border);
+ border-radius: 10px;
+ overflow: hidden;
+}
+
+.submission-panel__header {
+ padding: 18px 20px 14px;
+ border-bottom: 1px solid var(--border);
+ background: var(--surface);
+
+ h2 {
+ margin: 0 0 4px;
+ font-size: 1.1rem;
+ color: var(--text-h);
+ }
+}
+
+.submission-panel__note {
+ font-size: 0.85rem;
+ color: var(--text);
+ margin: 0;
+}
+
+/* ── Payload preview ─────────────────────────────────────────────────── */
+
+.payload-preview {
display: flex;
flex-direction: column;
- gap: 25px;
- place-content: center;
- place-items: center;
- flex-grow: 1;
+}
- @media (max-width: 1024px) {
- padding: 32px 20px 24px;
- gap: 18px;
+.payload-row {
+ display: grid;
+ grid-template-columns: 280px 1fr;
+ align-items: start;
+ gap: 12px;
+ padding: 12px 20px;
+ border-bottom: 1px solid var(--border);
+
+ &:last-child {
+ border-bottom: none;
+ }
+
+ @media (max-width: 640px) {
+ grid-template-columns: 1fr;
}
}
-#next-steps {
- display: flex;
- border-top: 1px solid var(--border);
- text-align: left;
-
- & > div {
- flex: 1 1 0;
- padding: 32px;
- @media (max-width: 1024px) {
- padding: 24px 20px;
- }
- }
-
- .icon {
- margin-bottom: 16px;
- width: 22px;
- height: 22px;
- }
-
- @media (max-width: 1024px) {
- flex-direction: column;
- text-align: center;
- }
-}
-
-#docs {
- border-right: 1px solid var(--border);
-
- @media (max-width: 1024px) {
- border-right: none;
- border-bottom: 1px solid var(--border);
- }
-}
-
-#next-steps ul {
- list-style: none;
- padding: 0;
+.payload-row--danger { background: var(--danger-bg); }
+.payload-row--warning { background: var(--warning-bg); }
+.payload-row--info { background: var(--info-bg); }
+
+.payload-row__label {
display: flex;
+ align-items: center;
gap: 8px;
- margin: 32px 0 0;
+ padding-top: 2px;
- .logo {
- height: 18px;
- }
+ code {
+ font-size: 0.8rem;
+ background: transparent;
+ padding: 0;
- a {
- color: var(--text-h);
- font-size: 16px;
- border-radius: 6px;
- background: var(--social-bg);
- display: flex;
- padding: 6px 12px;
- align-items: center;
- gap: 8px;
- text-decoration: none;
- transition: box-shadow 0.3s;
-
- &:hover {
- box-shadow: var(--shadow);
- }
- .button-icon {
- height: 18px;
- width: 18px;
- }
- }
-
- @media (max-width: 1024px) {
- margin-top: 20px;
- flex-wrap: wrap;
- justify-content: center;
-
- li {
- flex: 1 1 calc(50% - 8px);
- }
-
- a {
- width: 100%;
- justify-content: center;
- box-sizing: border-box;
- }
+ .payload-row--danger & { color: var(--danger-text); }
+ .payload-row--warning & { color: var(--warning-text); }
+ .payload-row--info & { color: var(--info-text); }
}
}
-#spacer {
- height: 88px;
+.payload-row__count {
+ font-size: 0.75rem;
+ font-weight: 700;
+ border-radius: 10px;
+ padding: 1px 7px;
+
+ .payload-row--danger & { background: var(--danger-header); color: var(--danger-text); }
+ .payload-row--warning & { background: var(--warning-header); color: var(--warning-text); }
+ .payload-row--info & { background: var(--info-header); color: var(--info-text); }
+}
+
+.payload-row__ids {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 6px;
+ min-height: 24px;
+}
+
+.payload-row__empty {
+ font-size: 0.8rem;
+ font-family: var(--mono);
+ color: var(--text);
+ opacity: 0.5;
+}
+
+.payload-id {
+ font-size: 0.8rem;
+ font-family: var(--mono);
+ padding: 2px 8px;
+ border-radius: 4px;
+
+ .payload-row--danger & { background: var(--danger-header); color: var(--danger-text); }
+ .payload-row--warning & { background: var(--warning-header); color: var(--warning-text); }
+ .payload-row--info & { background: var(--info-header); color: var(--info-text); }
+}
+
+/* ── Submit button & error ────────────────────────────────────────────── */
+
+.submission-panel__actions {
+ padding: 16px 20px;
border-top: 1px solid var(--border);
- @media (max-width: 1024px) {
- height: 48px;
+ background: var(--surface);
+ display: flex;
+ align-items: center;
+ gap: 16px;
+}
+
+.submit-btn {
+ padding: 10px 28px;
+ border-radius: 7px;
+ border: none;
+ background: var(--accent);
+ color: #fff;
+ font-size: 0.95rem;
+ font-weight: 600;
+ cursor: pointer;
+ transition: opacity 0.15s, filter 0.15s;
+
+ &:hover:not(:disabled) {
+ filter: brightness(1.1);
+ }
+
+ &:disabled {
+ opacity: 0.45;
+ cursor: not-allowed;
}
}
-.ticks {
- position: relative;
+.submit-error {
+ margin: 0;
+ font-size: 0.875rem;
+ color: var(--danger-text);
+}
+
+/* ── Submission result ────────────────────────────────────────────────── */
+
+.submission-result {
+ margin: 0;
+ border-top: 1px solid var(--border);
+}
+
+.submission-result__header {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 16px;
+ padding: 16px 20px;
+ flex-wrap: wrap;
+}
+
+.submission-result--pass .submission-result__header { background: var(--badge-low-bg); }
+.submission-result--fail .submission-result__header { background: var(--danger-bg); }
+
+.submission-result__score {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+}
+
+.submission-result__pct {
+ font-size: 2rem;
+ font-weight: 800;
+ color: var(--text-h);
+ line-height: 1;
+}
+
+.submission-result__status {
+ font-size: 0.85rem;
+ font-weight: 700;
+ padding: 3px 12px;
+ border-radius: 12px;
+}
+
+.status--pass {
+ background: var(--badge-low-bg);
+ color: var(--badge-low-text);
+}
+.status--fail {
+ background: var(--badge-high-bg);
+ color: var(--badge-high-text);
+}
+
+.submission-result__meta {
+ display: flex;
+ gap: 16px;
+ font-size: 0.82rem;
+ color: var(--text);
+ flex-wrap: wrap;
+}
+
+.personal-best {
+ color: var(--accent);
+ font-weight: 600;
+}
+
+.breakdown-table {
width: 100%;
+ border-radius: 0;
+ border-top: 1px solid var(--border);
- &::before,
- &::after {
- content: '';
- position: absolute;
- top: -4.5px;
- border: 5px solid transparent;
- }
+ th, td {
+ text-align: right;
+ padding: 9px 20px;
- &::before {
- left: 0;
- border-left-color: var(--border);
- }
- &::after {
- right: 0;
- border-right-color: var(--border);
+ &:first-child {
+ text-align: left;
+ }
}
}
+
+.feedback-list {
+ margin: 0;
+ padding: 12px 20px;
+ list-style: none;
+ display: flex;
+ flex-direction: column;
+ gap: 6px;
+ border-top: 1px solid var(--border);
+ font-size: 0.875rem;
+
+ li { color: var(--text-h); }
+
+ &--strengths { background: var(--badge-low-bg); }
+ &--issues { background: var(--warning-bg); }
+}
diff --git a/src/App.tsx b/src/App.tsx
index 46a5992..1cd1c57 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -1,121 +1,62 @@
-import { useState } from 'react'
-import reactLogo from './assets/react.svg'
-import viteLogo from './assets/vite.svg'
-import heroImg from './assets/hero.png'
-import './App.css'
+import { useState } from 'react';
+import { AlertsPanel, PatientTable, SubmissionPanel } from './components'
+import { useAllPatients, usePatientRiskAnalysis } from './hooks';
+import './App.css';
-function App() {
- const [count, setCount] = useState(0)
+const PAGE_SIZE = 10;
+
+const App = () => {
+ const [page, setPage] = useState(1);
+ const {
+ data: allPatients,
+ isLoading,
+ isFetching,
+ isError,
+ error,
+ refetch,
+ } = useAllPatients();
+ const riskAnalysis = usePatientRiskAnalysis();
+ const totalPatients = allPatients?.length ?? 0;
+ const totalPages = Math.max(1, Math.ceil(totalPatients / PAGE_SIZE));
+ const pagePatients = allPatients?.slice(
+ (page - 1) * PAGE_SIZE, page * PAGE_SIZE
+ ) ?? [];
return (
- <>
-
- Edit
-
-
-
Get started
- src/App.tsx and save to test HMR
-
{error.message}
+ +Loading...
+ ): error ? ( +{error.message}
+ ): ids.length === 0 ? ( +None
+ ): ( +| ID | +Name | +Age | +Gender | +Blood Pressure | +Temp | +Visited | +Diagnosis | +Medications | +Risk | +
|---|---|---|---|---|---|---|---|---|---|
| {patient.patient_id} | +{patient.name} | +{patient.age ?? -} | +{patient.gender} | +{patient.blood_pressure} | +{patient.temperature} | +{patient.visit_date} | +{patient.diagnosis} | +{patient.medications} | +
+ |
+
{label}
+ {payload[key].length}
+ | Category | +Score | +Correct | +Submitted | +Matches | +
|---|---|---|---|---|
| {label} | +{cat.score} / {cat.max} | +{cat.correct} | +{cat.submitted} | +{cat.matches} | +
+ Review payload before submitting. +
+Loading patient data...
+ ): ( +{(error as Error).message}
+ )} +
Connect with us
-Join the Vite community
---
-
-
- GitHub
-
-
- -
-
-
- Discord
-
-
- -
-
-
- X.com
-
-
- -
-
-
- Bluesky
-
-
-
-