feat: implement sorting and visibility changes #23

Merged
kailasdevdas merged 4 commits from feat/inactive-field into dev 2026-05-11 05:35:40 +00:00
3 changed files with 95 additions and 132 deletions
Showing only changes of commit 4808e99ae5 - Show all commits
+35 -42
View File
@@ -1,7 +1,6 @@
import { useState, useEffect, useCallback } from "react"; import { useState, useEffect, useCallback } from "react";
import { getCareersApi, deleteCareerApi } from "@/api/career"; import { getCareersApi, updateCareerApi, createCareerApi } from "@/api/career";
import apiClient from "@/api/client";
import { import {
Table, Table,
@@ -31,7 +30,6 @@ import {
Loader2, Loader2,
Plus, Plus,
Pencil, Pencil,
Trash,
RefreshCw, RefreshCw,
ChevronLeft, ChevronLeft,
ChevronRight, ChevronRight,
@@ -96,6 +94,18 @@ export default function CareerPage() {
setForm({ ...form, [e.target.name]: e.target.value }); setForm({ ...form, [e.target.name]: e.target.value });
} }
const handleToggleStatus = async (item: any) => {
try {
await updateCareerApi(item.id, {
...item,
isActive: !item.isActive,
} as any);
fetchAll();
} catch (error) {
console.error("Failed to toggle status", error);
}
};
function openAdd() { function openAdd() {
setEditing(null); setEditing(null);
setForm({ setForm({
@@ -131,10 +141,11 @@ export default function CareerPage() {
async function handleSubmit() { async function handleSubmit() {
try { try {
if (editing) { if (editing) {
await apiClient.patch(`/careers/${editing.id}`, form); await updateCareerApi(editing.id, form);
} else { } else {
await apiClient.post("/careers", form); await createCareerApi(form);
} }
setOpenModal(false); setOpenModal(false);
fetchAll(); fetchAll();
} catch (err) { } catch (err) {
@@ -142,12 +153,6 @@ export default function CareerPage() {
} }
} }
async function handleDelete(id: number) {
if (!confirm("Delete career?")) return;
await deleteCareerApi(id);
fetchAll();
}
return ( return (
<div className="p-6 space-y-6"> <div className="p-6 space-y-6">
<div className="flex flex-col md:flex-row md:justify-between md:items-center gap-4"> <div className="flex flex-col md:flex-row md:justify-between md:items-center gap-4">
@@ -185,13 +190,13 @@ export default function CareerPage() {
<CardContent className="p-0 sm:p-6 space-y-4"> <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"> <div className="rounded-md border overflow-x-auto overflow-y-auto max-h-[650px] relative">
<Table className="w-full min-w-[1000px] table-fixed border-separate border-spacing-0"> <Table className="w-full min-w-[800px] table-fixed border-separate border-spacing-0">
<TableHeader className="sticky top-0 z-20 bg-background shadow-sm"> <TableHeader className="sticky top-0 z-20 bg-background shadow-sm">
<TableRow> <TableRow>
<TableHead className="w-[60px] bg-background font-bold text-sm"> <TableHead className="w-[80px] bg-background font-bold text-sm">
Priority Priority
</TableHead> </TableHead>
<TableHead className="w-[200px] bg-background font-bold text-sm"> <TableHead className="w-[250px] bg-background font-bold text-sm">
Post & Designation Post & Designation
</TableHead> </TableHead>
<TableHead className="w-[200px] bg-background font-bold text-sm"> <TableHead className="w-[200px] bg-background font-bold text-sm">
@@ -200,13 +205,10 @@ export default function CareerPage() {
<TableHead className="w-[120px] bg-background font-bold text-sm"> <TableHead className="w-[120px] bg-background font-bold text-sm">
Experience Experience
</TableHead> </TableHead>
<TableHead className="w-[200px] bg-background font-bold text-sm"> <TableHead className="w-[80px] bg-background font-bold text-sm">
Contact Info Status (Active)
</TableHead> </TableHead>
<TableHead className="w-[100px] bg-background font-bold text-sm"> <TableHead className="w-[80px] bg-background font-bold text-right text-sm">
Visibility
</TableHead>
<TableHead className="w-[120px] bg-background font-bold text-right text-sm">
Actions Actions
</TableHead> </TableHead>
</TableRow> </TableRow>
@@ -215,14 +217,14 @@ export default function CareerPage() {
<TableBody> <TableBody>
{loading ? ( {loading ? (
<TableRow> <TableRow>
<TableCell colSpan={7} className="text-center py-10"> <TableCell colSpan={6} className="text-center py-10">
<Loader2 className="h-8 w-8 animate-spin mx-auto" /> <Loader2 className="h-8 w-8 animate-spin mx-auto" />
</TableCell> </TableCell>
</TableRow> </TableRow>
) : currentItems.length === 0 ? ( ) : currentItems.length === 0 ? (
<TableRow> <TableRow>
<TableCell <TableCell
colSpan={7} colSpan={6}
className="text-center text-muted-foreground py-10 text-base" className="text-center text-muted-foreground py-10 text-base"
> >
No careers found No careers found
@@ -251,19 +253,19 @@ export default function CareerPage() {
{item.experienceNeed} {item.experienceNeed}
</TableCell> </TableCell>
<TableCell> <TableCell>
<div className="text-sm font-medium">{item.email}</div> <div className="flex items-center gap-2">
<div className="text-xs text-muted-foreground"> <Switch
{item.number} checked={item.isActive}
onCheckedChange={() => handleToggleStatus(item)}
/>
<Badge
variant={item.isActive ? "default" : "secondary"}
className="capitalize"
>
{item.isActive ? "Active" : "Hidden"}
</Badge>
</div> </div>
</TableCell> </TableCell>
<TableCell>
<Badge
variant={item.isActive ? "default" : "secondary"}
className="capitalize"
>
{item.isActive ? "Active" : "Hidden"}
</Badge>
</TableCell>
<TableCell className="text-right"> <TableCell className="text-right">
<div className="flex justify-end gap-2"> <div className="flex justify-end gap-2">
@@ -275,15 +277,6 @@ export default function CareerPage() {
> >
<Pencil className="h-4 w-4" /> <Pencil className="h-4 w-4" />
</Button> </Button>
<Button
size="icon"
variant="ghost"
className="h-9 w-9 text-destructive hover:text-destructive hover:bg-destructive/10"
onClick={() => handleDelete(item.id)}
>
<Trash className="h-4 w-4" />
</Button>
</div> </div>
</TableCell> </TableCell>
</TableRow> </TableRow>
+31 -57
View File
@@ -6,7 +6,6 @@ import {
getDepartmentsApi, getDepartmentsApi,
createDepartmentApi, createDepartmentApi,
updateDepartmentApi, updateDepartmentApi,
deleteDepartmentApi,
} from "@/api/department"; } from "@/api/department";
import { import {
@@ -40,7 +39,6 @@ import {
RefreshCw, RefreshCw,
Plus, Plus,
Pencil, Pencil,
Trash,
Eye, Eye,
ChevronLeft, ChevronLeft,
ChevronRight, ChevronRight,
@@ -134,10 +132,18 @@ export default function DepartmentPage() {
setForm({...form, [e.target.name]: value}); setForm({...form, [e.target.name]: value});
} }
function truncate(text: string, limit = 60) { const handleToggleStatus = async (dep: Department) => {
if (!text) return "-"; try {
return text.length > limit ? text.substring(0, limit) + "..." : text; const {departmentId, ...updateData} = dep;
} await updateDepartmentApi(departmentId, {
...updateData,
isActive: !dep.isActive,
} as any);
fetchDepartments();
} catch (error) {
console.error("Failed to toggle status", error);
}
};
function openAdd() { function openAdd() {
setEditing(null); setEditing(null);
@@ -175,7 +181,7 @@ export default function DepartmentPage() {
try { try {
if (editing) { if (editing) {
const {departmentId, ...updateData} = form; const {departmentId, ...updateData} = form;
await updateDepartmentApi(editing.departmentId, updateData); await updateDepartmentApi(editing.departmentId, form as any);
} else { } else {
await createDepartmentApi(form); await createDepartmentApi(form);
} }
@@ -187,17 +193,6 @@ export default function DepartmentPage() {
} }
} }
async function handleDelete(id: string) {
if (!confirm("Delete this department?")) return;
try {
await deleteDepartmentApi(id);
fetchDepartments();
} catch (error) {
console.error(error);
}
}
return ( return (
<div className="p-6 space-y-6"> <div className="p-6 space-y-6">
<div className="flex flex-col md:flex-row md:justify-between md:items-center gap-4"> <div className="flex flex-col md:flex-row md:justify-between md:items-center gap-4">
@@ -241,25 +236,19 @@ export default function DepartmentPage() {
<CardContent className="p-0 sm:p-6 space-y-4"> <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"> <div className="rounded-md border overflow-x-auto overflow-y-auto max-h-[650px] relative">
<Table className="w-full min-w-[1000px] table-fixed border-separate border-spacing-0"> <Table className="w-full min-w-[700px] table-fixed border-separate border-spacing-0">
<TableHeader className="sticky top-0 z-20 bg-background shadow-sm"> <TableHeader className="sticky top-0 z-20 bg-background shadow-sm">
<TableRow> <TableRow>
<TableHead className="w-[80px] bg-background text-sm font-bold"> <TableHead className="w-[100px] bg-background text-sm font-bold">
Priority Priority
</TableHead> </TableHead>
<TableHead className="w-[180px] bg-background text-sm font-bold"> <TableHead className="w-[300px] bg-background text-sm font-bold">
Name Name
</TableHead> </TableHead>
<TableHead className="w-[100px] bg-background text-sm font-bold"> <TableHead className="w-[80px] bg-background text-sm font-bold">
Status Status (Active)
</TableHead> </TableHead>
<TableHead className="w-[250px] bg-background text-sm font-bold"> <TableHead className="w-[80px] bg-background text-right text-sm font-bold">
Para 1
</TableHead>
<TableHead className="w-[180px] bg-background text-sm font-bold">
Facilities
</TableHead>
<TableHead className="w-[140px] bg-background text-right text-sm font-bold">
Actions Actions
</TableHead> </TableHead>
</TableRow> </TableRow>
@@ -268,14 +257,14 @@ export default function DepartmentPage() {
<TableBody> <TableBody>
{loading ? ( {loading ? (
<TableRow> <TableRow>
<TableCell colSpan={6} className="text-center py-10"> <TableCell colSpan={4} className="text-center py-10">
<Loader2 className="h-8 w-8 animate-spin mx-auto" /> <Loader2 className="h-8 w-8 animate-spin mx-auto" />
</TableCell> </TableCell>
</TableRow> </TableRow>
) : currentItems.length === 0 ? ( ) : currentItems.length === 0 ? (
<TableRow> <TableRow>
<TableCell <TableCell
colSpan={6} colSpan={4}
className="text-center text-muted-foreground py-10 text-base" className="text-center text-muted-foreground py-10 text-base"
> >
No departments found No departments found
@@ -302,22 +291,17 @@ export default function DepartmentPage() {
{dep.departmentId} {dep.departmentId}
</div> </div>
</TableCell> </TableCell>
<TableCell> <TableCell>
<Badge variant={dep.isActive ? "default" : "secondary"}> <div className="flex items-center gap-2">
{dep.isActive ? "Active" : "Hidden"} <Switch
</Badge> checked={dep.isActive}
</TableCell> onCheckedChange={() => handleToggleStatus(dep)}
/>
<TableCell> <Badge
<div className="text-sm break-words whitespace-normal"> variant={dep.isActive ? "default" : "secondary"}
{truncate(dep.para1)} >
</div> {dep.isActive ? "Active" : "Hidden"}
</TableCell> </Badge>
<TableCell>
<div className="text-sm break-words whitespace-normal">
{truncate(dep.facilities)}
</div> </div>
</TableCell> </TableCell>
@@ -340,15 +324,6 @@ export default function DepartmentPage() {
> >
<Pencil className="h-4 w-4" /> <Pencil className="h-4 w-4" />
</Button> </Button>
<Button
size="icon"
variant="ghost"
className="h-9 w-9 text-destructive hover:text-destructive hover:bg-destructive/10"
onClick={() => handleDelete(dep.departmentId)}
>
<Trash className="h-4 w-4" />
</Button>
</div> </div>
</TableCell> </TableCell>
</TableRow> </TableRow>
@@ -469,7 +444,6 @@ export default function DepartmentPage() {
placeholder="Services" 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="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"> <div className="flex items-center justify-between p-3 border rounded-md">
<Label htmlFor="isActive" className="text-base cursor-pointer"> <Label htmlFor="isActive" className="text-base cursor-pointer">
+29 -33
View File
@@ -7,7 +7,6 @@ import {
getDoctorsApi, getDoctorsApi,
createDoctorApi, createDoctorApi,
updateDoctorApi, updateDoctorApi,
deleteDoctorApi,
getDoctorTimingApi, getDoctorTimingApi,
} from "@/api/doctor"; } from "@/api/doctor";
import { getDepartmentsApi } from "@/api/department"; import { getDepartmentsApi } from "@/api/department";
@@ -39,7 +38,6 @@ import {
RefreshCw, RefreshCw,
Plus, Plus,
Pencil, Pencil,
Trash,
ChevronLeft, ChevronLeft,
ChevronRight, ChevronRight,
} from "lucide-react"; } from "lucide-react";
@@ -139,6 +137,16 @@ export default function DoctorPage() {
setForm({ ...form, [e.target.name]: value }); setForm({ ...form, [e.target.name]: value });
} }
const handleToggleStatus = async (doc: any) => {
try {
const updatedDoc = { ...doc, isActive: !doc.isActive };
await updateDoctorApi(doc.doctorId, updatedDoc);
fetchAll();
} catch (err) {
console.error("Failed to update status", err);
}
};
function handleDepartmentToggle(depId: string) { function handleDepartmentToggle(depId: string) {
const exists = form.departments.find((d: any) => d.departmentId === depId); const exists = form.departments.find((d: any) => d.departmentId === depId);
if (exists) { if (exists) {
@@ -236,16 +244,6 @@ export default function DoctorPage() {
} }
} }
async function handleDelete(id: string) {
if (!confirm("Delete this doctor?")) return;
try {
await deleteDoctorApi(id);
fetchAll();
} catch (error) {
console.error(error);
}
}
return ( return (
<div className="p-6 space-y-6"> <div className="p-6 space-y-6">
<div className="flex flex-col md:flex-row md:justify-between md:items-center gap-4"> <div className="flex flex-col md:flex-row md:justify-between md:items-center gap-4">
@@ -308,19 +306,19 @@ export default function DoctorPage() {
<TableHead className="w-[80px] bg-background text-sm font-bold"> <TableHead className="w-[80px] bg-background text-sm font-bold">
Priority{" "} Priority{" "}
</TableHead> </TableHead>
<TableHead className="w-[200px] bg-background text-sm font-bold"> <TableHead className="w-[180px] bg-background text-sm font-bold">
Doctor Info Doctor Info
</TableHead> </TableHead>
<TableHead className="w-[100px] bg-background text-sm font-bold">
Status
</TableHead>
<TableHead className="w-[150px] bg-background text-sm font-bold"> <TableHead className="w-[150px] bg-background text-sm font-bold">
Designation Designation
</TableHead> </TableHead>
<TableHead className="w-[220px] bg-background text-sm font-bold"> <TableHead className="w-[220px] bg-background text-sm font-bold">
Departments (Hierarchy) Departments (Hierarchy)
</TableHead> </TableHead>
<TableHead className="w-[120px] bg-background text-right text-sm font-bold"> <TableHead className="w-[80px] bg-background text-sm font-bold">
Status (Active)
</TableHead>
<TableHead className="w-[80px] bg-background text-right text-sm font-bold">
Actions Actions
</TableHead> </TableHead>
</TableRow> </TableRow>
@@ -361,12 +359,6 @@ export default function DoctorPage() {
</div> </div>
</TableCell> </TableCell>
<TableCell>
<Badge variant={doc.isActive ? "default" : "secondary"}>
{doc.isActive ? "Active" : "Hidden"}
</Badge>
</TableCell>
<TableCell> <TableCell>
<div <div
className="truncate text-sm font-medium" className="truncate text-sm font-medium"
@@ -396,6 +388,20 @@ export default function DoctorPage() {
</div> </div>
</TableCell> </TableCell>
<TableCell>
<div className="flex items-center gap-2">
<Switch
checked={doc.isActive}
onCheckedChange={() => handleToggleStatus(doc)}
/>
<Badge
variant={doc.isActive ? "default" : "secondary"}
>
{doc.isActive ? "Active" : "Hidden"}
</Badge>
</div>
</TableCell>
<TableCell className="text-right"> <TableCell className="text-right">
<div className="flex justify-end gap-2"> <div className="flex justify-end gap-2">
<Button <Button
@@ -406,14 +412,6 @@ export default function DoctorPage() {
> >
<Pencil className="h-4 w-4" /> <Pencil className="h-4 w-4" />
</Button> </Button>
<Button
size="icon"
variant="ghost"
className="h-9 w-9 text-destructive hover:text-destructive hover:bg-destructive/10"
onClick={() => handleDelete(doc.doctorId)}
>
<Trash className="h-4 w-4" />
</Button>
</div> </div>
</TableCell> </TableCell>
</TableRow> </TableRow>
@@ -622,8 +620,6 @@ export default function DoctorPage() {
const depName = departments.find( const depName = departments.find(
(d) => d.departmentId === dep.departmentId, (d) => d.departmentId === dep.departmentId,
)?.name; )?.name;
console.log("doctor department sortOrder", dep.sortOrder);
console.log("dep", dep);
return ( return (
<div <div
key={dep.departmentId} key={dep.departmentId}