Skip to content

Commit

Permalink
Add recieve state components
Browse files Browse the repository at this point in the history
  • Loading branch information
yash-learner committed Dec 8, 2024
1 parent cc466a9 commit 300de49
Show file tree
Hide file tree
Showing 5 changed files with 348 additions and 0 deletions.
3 changes: 3 additions & 0 deletions src/Routers/routes/LabTestRoutes.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { CollectSpecimen } from "@/components/LabTest/CollectSpecimen";
import { LabTest } from "@/components/LabTest/Index";
import { ReceiveSpecimen } from "@/components/LabTest/ReceiveSpecimen";
import { SendSpecimen } from "@/components/LabTest/SendSpecimen";

import { AppRoutes } from "@/Routers/AppRouter";
Expand All @@ -9,6 +10,8 @@ const LabTestRoutes: AppRoutes = {
"/lab_tests/specimen_collected": () => <LabTest />,
"/lab_tests/sent_to_lab": () => <LabTest />,
"/lab_tests/send_to_lab": () => <SendSpecimen />,
"/lab_tests/receive_at_lab": () => <ReceiveSpecimen />,
"/lab_tests/received_at_lab": () => <LabTest />,
"/lab_tests/:patientId/orders": ({ patientId }) => <CollectSpecimen />,
};

Expand Down
2 changes: 2 additions & 0 deletions src/components/LabTest/DataTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,14 @@ interface DataTableProps {
actions?: (row: Record<string, any>) => React.ReactNode;
}

// Todo: Properly map the status to the badge color
const badgeStyles: Record<string, string> = {
pending: "bg-orange-100 text-orange-800",
collected: "bg-blue-100 text-blue-800",
completed: "bg-green-100 text-green-800",
cancelled: "bg-red-100 text-red-800",
default: "bg-gray-100 text-gray-800",
transit: "bg-yellow-100 text-yellow-800",
};

export const DataTable: React.FC<DataTableProps> = ({
Expand Down
11 changes: 11 additions & 0 deletions src/components/LabTest/Index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Link, usePath } from "raviger";

import Page from "@/components/Common/Page";
import { OrderPlaced } from "@/components/LabTest/OrderPlaced";
import { ReceivedAtLab } from "@/components/LabTest/ReceivedAtLab";
import { SpecimenCollected } from "@/components/LabTest/SpecimenCollected";

export const LabTest = () => {
Expand All @@ -20,6 +21,8 @@ export const LabTest = () => {
return <OrderPlaced />;
case "/lab_tests/specimen_collected":
return <SpecimenCollected />;
case "/lab_tests/received_at_lab":
return <ReceivedAtLab />;
default:
return <div className="text-center text-red-500">Tab not found</div>;
}
Expand Down Expand Up @@ -50,6 +53,14 @@ export const LabTest = () => {
>
Sent to Lab
</Link>
<Link
href="/lab_tests/received_at_lab"
className={tabButtonClasses(
currentPath === "/lab_tests/received_at_lab",
)}
>
Received at Lab
</Link>
</nav>

<div className="mt-4">{renderTabContent()}</div>
Expand Down
152 changes: 152 additions & 0 deletions src/components/LabTest/ReceiveSpecimen.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
import { ChevronDownIcon, ChevronUpIcon } from "@radix-ui/react-icons";
import React from "react";

import { Button } from "@/components/ui/button";
import {
Collapsible,
CollapsibleContent,
CollapsibleTrigger,
} from "@/components/ui/collapsible";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";

// Mock Labs Data
const labs = [
{ id: "lab-001", name: "Central Lab" },
{ id: "lab-002", name: "Northside Lab" },
{ id: "lab-003", name: "Southside Lab" },
{ id: "lab-004", name: "Eastside Lab" },
{ id: "lab-005", name: "Westside Lab" },
];

export const ReceiveSpecimen: React.FC = () => {
return (
<div className="mx-auto max-w-5xl flex flex-col gap-5">
<Button
variant="outline"
onClick={() => {
history.back();
}}
className="w-fit"
>
Back
</Button>
<h2 className="text-2xl leading-tight">Receive Specimen at Lab</h2>
<div className="flex flex-col bg-white shadow-sm rounded-sm p-4 gap-5">
<div className="space-y-2">
<Label className="text-sm font-normal text-gray-900">Barcode</Label>
<Input
type="text"
placeholder="Scan Barcode/Enter number"
className="text-center"
/>
</div>
<Button variant={"outline"} size={"lg"} className="self-end">
+ Add Note
</Button>
</div>
<div>
<Label className="text-xl font-medium text-gray-900">
Received at Lab
</Label>
<Collapsible>
<div className="relative before:content-[''] before:absolute before:top-0 before:left-0 before:h-7 before:w-1 before:bg-gray-400 before:mt-3.5 before:rounded-r-sm">
<div
className={`items-center px-4 py-3 border rounded-lg shadow-sm max-w-5xl mx-auto space-y-4`}
>
<div className="flex items-center gap-4 justify-between">
<div>
<span className="text-sm font-medium text-gray-600">
Specimen id
</span>
<div className="flex items-center gap-2">
<span className="text-lg font-semibold text-gray-900">
SPEC009213
</span>
<span className="px-2 py-1 text-xs font-medium bg-pink-100 text-pink-900 rounded">
Received at Lab
</span>
</div>
</div>
<div className="flex items-center">
<div className="flex items-center gap-4">
<CollapsibleTrigger asChild>
<Button variant="ghost" size="sm">
<div className="">
{/* {openOrders[order.orderId] ? (
<ChevronUpIcon className="h-6 w-8" />
) : ( */}
<ChevronDownIcon className="h-6 w-8" />
{/* )} */}
</div>
</Button>
</CollapsibleTrigger>
</div>
</div>
</div>

{/* Expanded Content */}
<CollapsibleContent>
<div className="space-y-4">
<div className="max-w-5xl bg-white shadow rounded-lg p-6">
<div className="flex flex-col md:flex-row gap-4">
{/* Left Section */}
<div className="w-full md:w-1/2 pl-2">
<h3 className="text-sm font-semibold text-gray-600">
Test
</h3>
<p className="font-semibold">
Complete Blood Count (CBC)
</p>
</div>

{/* Right Section */}
<div className="w-full md:w-1/2 md:border-l border-gray-300 sm:pl-4 sm:pb-4">
<h3 className="text-sm font-semibold text-gray-600">
Order Placed by
</h3>
<div className="flex gap-2">
<p className="text-lg font-semibold text-gray-900 mb-4">
Dr. Jahnab Dutta,
</p>
<p className="text-lg font-normal text-gray-900 mb-4">
Cardiologist
</p>
</div>

<h3 className="text-sm font-semibold text-gray-600">
Order Date/Time
</h3>
<p className="text-lg font-semibold text-gray-900 mb-4">
28-Nov-2024, 2:30PM
</p>

<h3 className="text-sm font-semibold text-gray-600">
Priority
</h3>
<span className="px-3 py-1 inline-block text-sm font-semibold text-red-600 bg-red-100 rounded-lg">
Stat
</span>
</div>
</div>
{/* Note Section */}
<div className="border-t border-gray-300 px-2 py-4 max-w-4xl">
<h3 className="text-sm font-semibold text-gray-600">
Note:
</h3>
<p className="text-gray-700">
Prescribed CBC to check for anemia or infection and LFT
to evaluate liver health due to complaints of fatigue
and mild abdominal discomfort.
</p>
</div>
</div>
</div>
</CollapsibleContent>
</div>
</div>
</Collapsible>
</div>
</div>
);
};
180 changes: 180 additions & 0 deletions src/components/LabTest/ReceivedAtLab.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
import { navigate } from "raviger";
import React, { useState } from "react";

import { Button } from "@/components/ui/button";

import { DataTable } from "@/components/LabTest/DataTable";
import TableFilter, { Filter } from "@/components/LabTest/TableFilter";

export const ReceivedAtLab: React.FC = () => {
const keys = [
{
key: "specimenId",
label: "Specimen ID",
type: "text",
operators: ["is", "is_not", "contains", "does_not_contain"],
},
{
key: "orderId",
label: "Order ID",
type: "text",
operators: ["is", "is_not", "contains", "does_not_contain"],
},
{
key: "patientName",
label: "Patient Name",
type: "text",
operators: ["is", "is_not", "contains", "does_not_contain"],
},
{
key: "specimen",
label: "Specimen",
type: "checkbox",
options: ["Blood", "Swab", "Tissue"],
operators: ["is", "is_not"],
},
{
key: "tests",
label: "Tests",
type: "text",
operators: ["is", "contains", "does_not_contain"],
},
{
key: "collector",
label: "Collector",
type: "text",
operators: ["is", "is_not", "contains"],
},
{
key: "status",
label: "Status",
type: "checkbox",
options: ["Pending", "Collected", "Completed", "Cancelled"],
operators: ["is", "is_not"],
render_as: "badge",
},
{
key: "priority",
label: "Priority",
type: "radio", // New filter type
options: ["High", "Medium", "Low"],
operators: ["is"], // Typically, only "is" makes sense for radio
defaultOperator: "is",
},
];

// dummy data until we have API wired up
const initialData = [
{
specimenId: "SPEC009213",
orderId: "CARE_LAB-001",
patientName: "John Honai",
specimen: "Blood",
tests: "Complete Blood Count (CBC)",
collector: "John Doe",
status: "Transit",
priority: "High",
},
{
specimenId: "SPEC009412",
orderId: "CARE_LAB-002",
patientName: "Jane Doe",
specimen: "Swab",
tests: "COVID-19 PCR, Influenza Test",
collector: "Jane Doe",
status: "Transit",
priority: "Medium",
},
{
specimenId: "SPEC009425",
orderId: "CARE_LAB-003",
patientName: "Narayani",
specimen: "Tissue",
tests: "Biopsy of tissue",
collector: "Dr. Rajmohan",
status: "Transit",
priority: "Low",
},
{
specimenId: "SPEC009876",
orderId: "CARE_LAB-004",
patientName: "Tintu Ukken",
specimen: "Swab",
tests: "COVID-19 PCR",
collector: "Jeena Mathew",
status: "Transit",
priority: "High",
},
{
specimenId: "SPEC001234",
orderId: "CARE_LAB-005",
patientName: "Paul Varghese",
specimen: "Blood",
tests: "Lipid Profile",
collector: "John Doe",
status: "Transit",
priority: "Medium",
},
];

const [data, setData] = useState(initialData);

// Remove this function and use the actual API call to fetch data
const handleFiltersChange = (appliedFilters: Filter[]) => {
const filteredData = initialData.filter((row) =>
appliedFilters.every((filter) => {
const columnKey = filter.column as keyof typeof row;
const value = row[columnKey];

if (Array.isArray(filter.value)) {
if (filter.operator === "is any of") {
return filter.value.includes(value);
}
} else {
if (filter.operator === "is") return value === filter.value;
if (filter.operator === "is_not") return value !== filter.value;
if (filter.operator === "contains")
return (
typeof value === "string" &&
value.toLowerCase().includes(filter.value.toLowerCase())
);
if (filter.operator === "does_not_contain")
return (
typeof value === "string" &&
!value.toLowerCase().includes(filter.value.toLowerCase())
);
if (filter.operator === "is_empty") return !value;
if (filter.operator === "is_not_empty") return !!value;
}
return true;
}),
);

setData(filteredData);
};

return (
<div className="p-4 space-y-4">
{/* Table Filter */}
<div className="flex justify-between">
<TableFilter keys={keys} onFiltersChange={handleFiltersChange} />

<Button
onClick={() => navigate(`/lab_tests/receive_at_lab`)}
variant={"primary"}
>
Receive at Lab
</Button>
</div>
{/* Data Table */}
<DataTable
columns={keys.map((key) => ({
label: key.label,
key: key.key,
render_as: key.render_as,
}))}
data={data}
/>
</div>
);
};

0 comments on commit 300de49

Please sign in to comment.