feat: implement sorting and visibility changes

This commit is contained in:
Kailasdevdas
2026-05-11 00:04:22 +05:30
parent 9aaf1879a8
commit 1717507555
12 changed files with 335 additions and 112 deletions
+37 -7
View File
@@ -24,6 +24,8 @@ import {
import { Input } from "@/components/ui/input";
import { Badge } from "@/components/ui/badge";
import { Switch } from "@/components/ui/switch";
import { Label } from "@/components/ui/label";
import {
Loader2,
@@ -55,6 +57,8 @@ export default function CareerPage() {
email: "",
number: "",
status: "new",
isActive: true,
sortOrder: 0,
});
const fetchAll = useCallback(async () => {
@@ -102,6 +106,8 @@ export default function CareerPage() {
email: "",
number: "",
status: "new",
isActive: true,
sortOrder: 0,
});
setOpenModal(true);
}
@@ -116,6 +122,8 @@ export default function CareerPage() {
email: item.email || "",
number: item.number || "",
status: item.status || "new",
isActive: item.isActive ?? true,
sortOrder: item.sortOrder ?? 0,
});
setOpenModal(true);
}
@@ -181,7 +189,7 @@ export default function CareerPage() {
<TableHeader className="sticky top-0 z-20 bg-background shadow-sm">
<TableRow>
<TableHead className="w-[60px] bg-background font-bold text-sm">
ID
Priority
</TableHead>
<TableHead className="w-[200px] bg-background font-bold text-sm">
Post & Designation
@@ -196,7 +204,7 @@ export default function CareerPage() {
Contact Info
</TableHead>
<TableHead className="w-[100px] bg-background font-bold text-sm">
Status
Visibility
</TableHead>
<TableHead className="w-[120px] bg-background font-bold text-right text-sm">
Actions
@@ -224,7 +232,7 @@ export default function CareerPage() {
currentItems.map((item) => (
<TableRow key={item.id} className="hover:bg-muted/50">
<TableCell className="font-mono text-xs">
{item.id}
{item.sortOrder}
</TableCell>
<TableCell>
<div className="font-semibold text-base truncate">
@@ -250,12 +258,10 @@ export default function CareerPage() {
</TableCell>
<TableCell>
<Badge
variant={
item.status === "active" ? "default" : "secondary"
}
variant={item.isActive ? "default" : "secondary"}
className="capitalize"
>
{item.status}
{item.isActive ? "Active" : "Hidden"}
</Badge>
</TableCell>
@@ -393,6 +399,30 @@ export default function CareerPage() {
onChange={handleChange}
className="text-base"
/>
<div className="flex items-center justify-between p-2 border rounded-md">
<Label htmlFor="isActive" className="text-base">
Active
</Label>
<Switch
id="isActive"
checked={form.isActive}
onCheckedChange={(val) => setForm({ ...form, isActive: val })}
/>
</div>
<div className="space-y-1">
<Label htmlFor="sortOrder" className="text-sm">
Sort Priority (Lower numbers show first)
</Label>
<Input
id="sortOrder"
name="sortOrder"
type="number"
placeholder="Sort Order"
value={form.sortOrder}
onChange={handleChange}
className="text-base"
/>
</div>
</div>
</div>
+72 -21
View File
@@ -31,6 +31,9 @@ import {
import {Input} from "@/components/ui/input";
import {Textarea} from "@/components/ui/textarea";
import {Switch} from "@/components/ui/switch";
import {Label} from "@/components/ui/label";
import {Badge} from "@/components/ui/badge";
import {
Loader2,
@@ -52,6 +55,8 @@ interface Department {
para3: string;
facilities: string;
services: string;
isActive: boolean;
sortOrder: number;
}
export default function DepartmentPage() {
@@ -79,6 +84,8 @@ export default function DepartmentPage() {
para3: "",
facilities: "",
services: "",
isActive: true,
sortOrder: 0,
});
const fetchDepartments = useCallback(async () => {
@@ -122,7 +129,9 @@ export default function DepartmentPage() {
);
function handleChange(e: any) {
setForm({...form, [e.target.name]: e.target.value});
const value =
e.target.type === "number" ? Number(e.target.value) : e.target.value;
setForm({...form, [e.target.name]: value});
}
function truncate(text: string, limit = 60) {
@@ -141,13 +150,19 @@ export default function DepartmentPage() {
para3: "",
facilities: "",
services: "",
isActive: true,
sortOrder: 0,
});
setOpenModal(true);
}
function openEdit(dep: Department) {
setEditing(dep);
setForm(dep);
setForm({
...dep,
isActive: dep.isActive ?? true,
sortOrder: dep.sortOrder ?? 0,
});
setOpenModal(true);
}
@@ -226,24 +241,24 @@ export default function DepartmentPage() {
<CardContent className="p-0 sm:p-6 space-y-4">
<div className="rounded-md border overflow-x-auto overflow-y-auto max-h-[650px] relative">
<Table className="w-full min-w-[900px] table-fixed border-separate border-spacing-0">
<Table className="w-full min-w-[1000px] table-fixed border-separate border-spacing-0">
<TableHeader className="sticky top-0 z-20 bg-background shadow-sm">
<TableRow>
<TableHead className="w-[100px] bg-background text-sm font-bold">
ID
<TableHead className="w-[80px] bg-background text-sm font-bold">
Priority
</TableHead>
<TableHead className="w-[200px] bg-background text-sm font-bold">
<TableHead className="w-[180px] bg-background text-sm font-bold">
Name
</TableHead>
<TableHead className="w-[100px] bg-background text-sm font-bold">
Status
</TableHead>
<TableHead className="w-[250px] bg-background text-sm font-bold">
Para 1
</TableHead>
<TableHead className="w-[220px] bg-background text-sm font-bold">
<TableHead className="w-[180px] bg-background text-sm font-bold">
Facilities
</TableHead>
<TableHead className="w-[220px] bg-background text-sm font-bold">
Services
</TableHead>
<TableHead className="w-[140px] bg-background text-right text-sm font-bold">
Actions
</TableHead>
@@ -272,8 +287,8 @@ export default function DepartmentPage() {
key={dep.departmentId}
className="hover:bg-muted/50"
>
<TableCell className="font-mono text-xs">
{dep.departmentId}
<TableCell className="font-mono text-sm">
{dep.sortOrder}
</TableCell>
<TableCell>
@@ -283,6 +298,15 @@ export default function DepartmentPage() {
>
{dep.name}
</div>
<div className="text-xs text-muted-foreground">
{dep.departmentId}
</div>
</TableCell>
<TableCell>
<Badge variant={dep.isActive ? "default" : "secondary"}>
{dep.isActive ? "Active" : "Hidden"}
</Badge>
</TableCell>
<TableCell>
@@ -297,12 +321,6 @@ export default function DepartmentPage() {
</div>
</TableCell>
<TableCell>
<div className="text-sm break-words whitespace-normal">
{truncate(dep.services)}
</div>
</TableCell>
<TableCell className="text-right">
<div className="flex justify-end gap-2">
<Button
@@ -450,6 +468,31 @@ export default function DepartmentPage() {
onChange={handleChange}
placeholder="Services"
/>
{/* Status and Priority at bottom to keep original UI flow */}
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 border-t pt-4">
<div className="flex items-center justify-between p-3 border rounded-md">
<Label htmlFor="isActive" className="text-base cursor-pointer">
Active Visibility
</Label>
<Switch
id="isActive"
checked={form.isActive}
onCheckedChange={(val) => setForm({...form, isActive: val})}
/>
</div>
<div className="space-y-1">
<Label htmlFor="sortOrder">Sort Order (Priority)</Label>
<Input
id="sortOrder"
name="sortOrder"
type="number"
value={form.sortOrder}
onChange={handleChange}
placeholder="0"
/>
</div>
</div>
</div>
<DialogFooter>
@@ -470,9 +513,17 @@ export default function DepartmentPage() {
</DialogHeader>
{viewData && (
<div className="space-y-4 text-sm">
<p>
<b>ID:</b> {viewData.departmentId}
</p>
<div className="flex gap-4 items-center border-b pb-4">
<Badge variant={viewData.isActive ? "default" : "secondary"}>
{viewData.isActive ? "ACTIVE" : "HIDDEN"}
</Badge>
<p>
<b>Sort Order:</b> {viewData.sortOrder}
</p>
<p>
<b>ID:</b> {viewData.departmentId}
</p>
</div>
<p>
<b>Name:</b> {viewData.name}
</p>
+107 -38
View File
@@ -31,6 +31,9 @@ import {
} from "@/components/ui/dialog";
import { Input } from "@/components/ui/input";
import { Badge } from "@/components/ui/badge";
import { Switch } from "@/components/ui/switch";
import { Label } from "@/components/ui/label";
import {
Loader2,
RefreshCw,
@@ -39,7 +42,6 @@ import {
Trash,
ChevronLeft,
ChevronRight,
User,
} from "lucide-react";
interface Department {
@@ -80,6 +82,8 @@ export default function DoctorPage() {
designation: "",
workingStatus: "",
qualification: "",
isActive: true,
globalSortOrder: 0,
departments: [],
});
@@ -130,7 +134,9 @@ export default function DoctorPage() {
const currentItems = filteredDoctors.slice(indexOfFirstItem, indexOfLastItem);
function handleChange(e: any) {
setForm({ ...form, [e.target.name]: e.target.value });
const value =
e.target.type === "number" ? Number(e.target.value) : e.target.value;
setForm({ ...form, [e.target.name]: value });
}
function handleDepartmentToggle(depId: string) {
@@ -145,11 +151,23 @@ export default function DoctorPage() {
} else {
setForm({
...form,
departments: [...form.departments, { departmentId: depId, timing: {} }],
departments: [
...form.departments,
{ departmentId: depId, sortOrder: 0, timing: {} },
],
});
}
}
function handleDeptSortChange(depId: string, value: string) {
setForm({
...form,
departments: form.departments.map((d: any) =>
d.departmentId === depId ? { ...d, sortOrder: Number(value) } : d,
),
});
}
function handleTimingChange(depId: string, day: string, value: string) {
setForm({
...form,
@@ -170,6 +188,8 @@ export default function DoctorPage() {
designation: "",
workingStatus: "",
qualification: "",
isActive: true,
globalSortOrder: 0,
departments: [],
});
setOpenModal(true);
@@ -180,6 +200,7 @@ export default function DoctorPage() {
try {
const timingRes = await getDoctorTimingApi(doc.doctorId);
const timingData = timingRes?.data?.departments || [];
setForm({
doctorId: doc.doctorId,
name: doc.name,
@@ -187,14 +208,17 @@ export default function DoctorPage() {
designation: doc.designation,
workingStatus: doc.workingStatus,
qualification: doc.qualification,
isActive: doc.isActive ?? true,
globalSortOrder: doc.globalSortOrder ?? 0,
departments: timingData.map((d: any) => ({
departmentId: d.departmentId,
sortOrder: d.deptSortOrder ?? 0,
timing: d.timing || {},
})),
});
setOpenModal(true);
} catch (err) {
console.error(err);
console.error("Error fetching doctor details:", err);
}
}
@@ -278,23 +302,23 @@ export default function DoctorPage() {
<CardContent className="p-0 sm:p-6 space-y-4">
<div className="rounded-md border overflow-x-auto overflow-y-auto max-h-[650px] relative">
<Table className="w-full min-w-[900px] table-fixed border-separate border-spacing-0">
<Table className="w-full min-w-[1100px] table-fixed border-separate border-spacing-0">
<TableHeader className="sticky top-0 z-20 bg-background shadow-sm">
<TableRow>
<TableHead className="w-[100px] bg-background text-sm font-bold">
ID
<TableHead className="w-[80px] bg-background text-sm font-bold">
Priority{" "}
</TableHead>
<TableHead className="w-[200px] bg-background text-sm font-bold">
Name
Doctor Info
</TableHead>
<TableHead className="w-[180px] bg-background text-sm font-bold">
<TableHead className="w-[100px] bg-background text-sm font-bold">
Status
</TableHead>
<TableHead className="w-[150px] bg-background text-sm font-bold">
Designation
</TableHead>
<TableHead className="w-[180px] bg-background text-sm font-bold">
Qualification
</TableHead>
<TableHead className="w-[220px] bg-background text-sm font-bold">
Departments
Departments (Hierarchy)
</TableHead>
<TableHead className="w-[120px] bg-background text-right text-sm font-bold">
Actions
@@ -321,8 +345,8 @@ export default function DoctorPage() {
) : (
currentItems.map((doc) => (
<TableRow key={doc.doctorId} className="hover:bg-muted/50">
<TableCell className="truncate font-mono text-xs">
{doc.doctorId}
<TableCell className="font-mono text-sm">
{doc.globalSortOrder}
</TableCell>
<TableCell>
@@ -332,26 +356,26 @@ export default function DoctorPage() {
>
{doc.name}
</div>
<div className="text-xs text-muted-foreground truncate italic">
{doc.workingStatus}
<div className="text-xs text-muted-foreground truncate font-mono">
{doc.doctorId}
</div>
</TableCell>
<TableCell>
<Badge variant={doc.isActive ? "default" : "secondary"}>
{doc.isActive ? "Active" : "Hidden"}
</Badge>
</TableCell>
<TableCell>
<div
className="truncate text-sm"
className="truncate text-sm font-medium"
title={doc.designation}
>
{doc.designation || "-"}
</div>
</TableCell>
<TableCell>
<div
className="truncate text-sm"
title={doc.qualification}
>
{doc.qualification || "-"}
<div className="text-xs italic text-muted-foreground truncate">
{doc.workingStatus}
</div>
</TableCell>
@@ -361,14 +385,14 @@ export default function DoctorPage() {
<Badge
key={d.departmentId}
variant="secondary"
className="text-xs px-2 h-5 leading-none"
className="text-xs px-2 h-6 leading-none flex items-center gap-1"
>
{d.departmentName}
<span className="bg-primary text-primary-foreground px-1 rounded-full text-[10px]">
{d.deptSortOrder}
</span>
</Badge>
))}
{doc.departments?.length === 0 && (
<span className="text-muted-foreground">-</span>
)}
</div>
</TableCell>
@@ -457,7 +481,7 @@ export default function DoctorPage() {
<div className="grid grid-cols-1 md:grid-cols-2 gap-8">
<div className="space-y-6">
<h3 className="font-bold text-base border-b pb-2">
Basic Information
Profile & Visibility
</h3>
<div className="space-y-4">
<div className="space-y-2">
@@ -471,6 +495,36 @@ export default function DoctorPage() {
/>
</div>
<div className="flex items-center justify-between p-3 border rounded-md bg-muted/30">
<Label
htmlFor="isActive"
className="text-base font-semibold cursor-pointer"
>
Active
</Label>
<Switch
id="isActive"
checked={form.isActive}
onCheckedChange={(val) =>
setForm({ ...form, isActive: val })
}
/>
</div>
<div className="space-y-1">
<Label htmlFor="globalSortOrder">
Homepage Scroll Order (Priority)
</Label>
<Input
id="globalSortOrder"
name="globalSortOrder"
type="number"
value={form.globalSortOrder}
onChange={handleChange}
className="text-base"
/>
</div>
<div className="space-y-1">
<label className="text-sm font-semibold">Doctor ID</label>
<Input
@@ -556,11 +610,11 @@ export default function DoctorPage() {
<div className="space-y-6">
<h3 className="font-bold text-base border-b pb-2">
Working Hours / Timing
Department Hierarchy & Timing
</h3>
{form.departments.length === 0 ? (
<div className="text-base text-muted-foreground italic py-24 text-center border-2 border-dashed rounded-lg">
Select a department to configure timing slots
Select a department to configure hierarchy and timing
</div>
) : (
<div className="space-y-8">
@@ -568,18 +622,33 @@ export default function DoctorPage() {
const depName = departments.find(
(d) => d.departmentId === dep.departmentId,
)?.name;
console.log("doctor department sortOrder", dep.sortOrder);
console.log("dep", dep);
return (
<div
key={dep.departmentId}
className="space-y-4 p-5 border rounded-lg bg-background shadow-sm"
className="space-y-4 p-5 border rounded-lg bg-background shadow-sm border-primary/20"
>
<div className="flex items-center justify-between">
<div className="flex items-center justify-between border-b pb-2">
<p className="font-bold text-base text-primary">
{depName}
</p>
<Badge variant="outline" className="text-xs">
Timing Slot
</Badge>
<div className="flex items-center gap-2">
<Label className="text-xs font-bold">
Hierarchy Order:
</Label>
<Input
type="number"
className="w-20 h-8 text-sm"
value={dep.sortOrder}
onChange={(e) =>
handleDeptSortChange(
dep.departmentId,
e.target.value,
)
}
/>
</div>
</div>
<div className="grid grid-cols-1 sm:grid-cols-2 gap-x-6 gap-y-3">
{DAYS.map((day) => (