feat: featured doctor toggle

This commit is contained in:
Kailasdevdas
2026-06-24 16:43:14 +05:30
parent d0860a3be4
commit 5da63492ff
3 changed files with 47 additions and 6 deletions
+1 -1
View File
@@ -19,8 +19,8 @@ router.get('/getAll', getAllDoctors);
router.get('/search', getDoctorsByDepartmentId); router.get('/search', getDoctorsByDepartmentId);
router.get('/getTimings', getDoctorTimings); router.get('/getTimings', getDoctorTimings);
router.get('/getTimings/:doctorId', getDoctorTimingById); router.get('/getTimings/:doctorId', getDoctorTimingById);
router.get('/:doctorId', getDoctorByDoctorId);
router.get('/featured', getFeaturedDoctors); router.get('/featured', getFeaturedDoctors);
router.get('/:doctorId', getDoctorByDoctorId);
router.post('/', jwtAuthMiddleware, createDoctor); router.post('/', jwtAuthMiddleware, createDoctor);
router.patch('/:doctorId/:action', jwtAuthMiddleware, updateDoctor); router.patch('/:doctorId/:action', jwtAuthMiddleware, updateDoctor);
+2 -1
View File
@@ -9,6 +9,7 @@ export interface Doctor {
workingStatus?: string; workingStatus?: string;
qualification?: string; qualification?: string;
isActive: boolean; isActive: boolean;
isFeatured: boolean;
globalSortOrder: number; globalSortOrder: number;
departments?: { departments?: {
@@ -53,7 +54,7 @@ export const createDoctorApi = async (data: Doctor) => {
export const updateDoctorApi = async ( export const updateDoctorApi = async (
doctorId: string, doctorId: string,
data: Partial<Doctor>, data: Partial<Doctor>,
action: 'toggleStatus' | 'updateDetails' = 'updateDetails' action: 'toggleStatus' | 'toggleFeatured' | 'updateDetails' = 'updateDetails'
) => { ) => {
try { try {
const res = await apiClient.patch(`/doctors/${doctorId}/${action}`, data); const res = await apiClient.patch(`/doctors/${doctorId}/${action}`, data);
+44 -4
View File
@@ -51,6 +51,7 @@ export default function DoctorPage() {
workingStatus: '', workingStatus: '',
qualification: '', qualification: '',
isActive: true, isActive: true,
isFeatured: false,
globalSortOrder: 0, globalSortOrder: 0,
departments: [], departments: [],
professionalSummary: '', professionalSummary: '',
@@ -156,6 +157,22 @@ export default function DoctorPage() {
} }
}; };
const handleToggleFeatured = async (doc: any) => {
try {
const newFeaturedStatus = !doc.isFeatured;
const payload = {
isFeatured: newFeaturedStatus,
};
await updateDoctorApi(doc.doctorId, payload, 'toggleFeatured');
fetchAll();
} catch (err) {
console.error('Failed to update featured 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) {
@@ -201,6 +218,7 @@ export default function DoctorPage() {
experience: '', experience: '',
professionalSummary: '', professionalSummary: '',
isActive: true, isActive: true,
isFeatured: false,
globalSortOrder: 0, globalSortOrder: 0,
specializations: [ specializations: [
{ {
@@ -235,6 +253,7 @@ export default function DoctorPage() {
workingStatus: doc.workingStatus, workingStatus: doc.workingStatus,
qualification: doc.qualification, qualification: doc.qualification,
isActive: doc.isActive ?? true, isActive: doc.isActive ?? true,
isFeatured: doc.isFeatured ?? false,
globalSortOrder: doc.globalSortOrder ?? 0, globalSortOrder: doc.globalSortOrder ?? 0,
experience: doc.experience || '', experience: doc.experience || '',
professionalSummary: doc.professionalSummary || '', professionalSummary: doc.professionalSummary || '',
@@ -353,14 +372,15 @@ export default function DoctorPage() {
<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-[1100px] table-fixed border-separate border-spacing-0"> <Table className="w-full min-w-[1200px] 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">Priority </TableHead> <TableHead className="w-[80px] bg-background text-sm font-bold">Priority </TableHead>
<TableHead className="w-[180px] bg-background text-sm font-bold">Doctor Info</TableHead> <TableHead className="w-[180px] bg-background text-sm font-bold">Doctor Info</TableHead>
<TableHead className="w-[150px] bg-background text-sm font-bold">Designation</TableHead> <TableHead className="w-[150px] bg-background text-sm font-bold">Designation</TableHead>
<TableHead className="w-[220px] bg-background text-sm font-bold">Departments (Hierarchy)</TableHead> <TableHead className="w-[220px] bg-background text-sm font-bold">Departments (Hierarchy)</TableHead>
<TableHead className="w-[80px] bg-background text-sm font-bold">Status (Active)</TableHead> <TableHead className="w-[100px] bg-background text-sm font-bold">Status (Active)</TableHead>
<TableHead className="w-[100px] bg-background text-sm font-bold">Featured</TableHead>
<TableHead className="w-[80px] bg-background text-right text-sm font-bold">Actions</TableHead> <TableHead className="w-[80px] bg-background text-right text-sm font-bold">Actions</TableHead>
</TableRow> </TableRow>
</TableHeader> </TableHeader>
@@ -368,13 +388,13 @@ export default function DoctorPage() {
<TableBody> <TableBody>
{loading ? ( {loading ? (
<TableRow> <TableRow>
<TableCell colSpan={6} className="text-center py-10"> <TableCell colSpan={7} 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 colSpan={6} className="text-center text-muted-foreground py-10 text-base"> <TableCell colSpan={7} className="text-center text-muted-foreground py-10 text-base">
No doctors found No doctors found
</TableCell> </TableCell>
</TableRow> </TableRow>
@@ -423,6 +443,15 @@ export default function DoctorPage() {
</div> </div>
</TableCell> </TableCell>
<TableCell>
<div className="flex items-center gap-2">
<Switch checked={doc.isFeatured || false} onCheckedChange={() => handleToggleFeatured(doc)} />
<Badge variant={doc.isFeatured ? 'default' : 'secondary'}>
{doc.isFeatured ? 'Featured' : '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 size="icon" variant="ghost" className="h-9 w-9" onClick={() => handlePreview(doc)}> <Button size="icon" variant="ghost" className="h-9 w-9" onClick={() => handlePreview(doc)}>
@@ -508,6 +537,17 @@ export default function DoctorPage() {
/> />
</div> </div>
<div className="flex items-center justify-between p-3 border rounded-md bg-muted/30">
<Label htmlFor="isFeatured" className="text-base font-semibold cursor-pointer">
Featured
</Label>
<Switch
id="isFeatured"
checked={form.isFeatured}
onCheckedChange={(val) => setForm({ ...form, isFeatured: val })}
/>
</div>
<div className="space-y-1"> <div className="space-y-1">
<Label htmlFor="globalSortOrder" className="text-sm font-semibold"> <Label htmlFor="globalSortOrder" className="text-sm font-semibold">
Sort Priority (Lower numbers show first) Sort Priority (Lower numbers show first)