diff --git a/backend/prisma/migrations/20260708120000_case_level_important/migration.sql b/backend/prisma/migrations/20260708120000_case_level_important/migration.sql
new file mode 100644
index 0000000..ee816ed
--- /dev/null
+++ b/backend/prisma/migrations/20260708120000_case_level_important/migration.sql
@@ -0,0 +1,13 @@
+-- Move the "important" flag from individual tasks to the case as a whole.
+ALTER TABLE "lab_cases" ADD COLUMN "isImportant" BOOLEAN NOT NULL DEFAULT false;
+
+-- Carry over existing importance: a case is important if any of its tasks were.
+UPDATE "lab_cases" lc
+SET "isImportant" = true
+WHERE EXISTS (
+ SELECT 1 FROM "lab_case_tasks" t
+ WHERE t."labCaseId" = lc."id" AND t."isImportant" = true
+);
+
+DROP INDEX IF EXISTS "lab_case_tasks_labCaseId_isImportant_idx";
+ALTER TABLE "lab_case_tasks" DROP COLUMN "isImportant";
diff --git a/backend/prisma/schema.prisma b/backend/prisma/schema.prisma
index 3e7c2df..1719f11 100644
--- a/backend/prisma/schema.prisma
+++ b/backend/prisma/schema.prisma
@@ -195,6 +195,7 @@ model LabCase {
sortOrder Int
destinationOrganizationId String?
sentAt DateTime?
+ isImportant Boolean @default(false)
treatment Treatment @relation(fields: [treatmentId], references: [id], onDelete: Cascade)
details LabCaseDetail[]
@@ -333,7 +334,6 @@ model LabCaseTask {
workflowStepCode String
stepOrder Int
stepLabel String
- isImportant Boolean @default(false)
status LabTaskStatus @default(IN_PROGRESS)
lastStatusChangedByUserId String?
lastStatusChangedAt DateTime?
@@ -348,7 +348,6 @@ model LabCaseTask {
@@unique([labCaseId, treatmentDetailId, prosthesisTypeCode, stepOrder])
@@index([labCaseId, status])
- @@index([labCaseId, isImportant])
@@map("lab_case_tasks")
}
diff --git a/backend/src/modules/cases/cases.controller.ts b/backend/src/modules/cases/cases.controller.ts
index 8b319d6..d54a7b1 100644
--- a/backend/src/modules/cases/cases.controller.ts
+++ b/backend/src/modules/cases/cases.controller.ts
@@ -14,7 +14,7 @@ import { ApiBearerAuth, ApiOperation, ApiTags } from '@nestjs/swagger';
import { LabOrgGuard } from '../../common/guards/lab-org.guard';
import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard';
import { CasesService } from './cases.service';
-import { ListLabCasesDto, UpdateLabCaseTaskDto } from './dto/cases.dto';
+import { ListLabCasesDto, UpdateLabCaseImportantDto } from './dto/cases.dto';
@ApiTags('cases')
@ApiBearerAuth('JWT-auth')
@@ -64,15 +64,14 @@ export class CasesController {
file.stream.pipe(res);
}
- @Patch(':id/tasks/:taskId')
- @ApiOperation({ summary: 'Toggle task important flag' })
- updateTask(
+ @Patch(':id/important')
+ @ApiOperation({ summary: 'Toggle the important flag for a whole case' })
+ setCaseImportant(
@Param('id') id: string,
- @Param('taskId') taskId: string,
- @Body() dto: UpdateLabCaseTaskDto,
+ @Body() dto: UpdateLabCaseImportantDto,
@Req() req,
) {
const organizationId = this.casesService.getOrganizationIdFromUser(req.user);
- return this.casesService.updateTask(id, taskId, dto, organizationId, req.user.id, req.user.language);
+ return this.casesService.setCaseImportant(id, dto, organizationId, req.user.id, req.user.language);
}
}
diff --git a/backend/src/modules/cases/cases.service.ts b/backend/src/modules/cases/cases.service.ts
index d7fe45c..5d7c482 100644
--- a/backend/src/modules/cases/cases.service.ts
+++ b/backend/src/modules/cases/cases.service.ts
@@ -14,7 +14,7 @@ import {
} from '../catalog/catalog-label.service';
import { TreatmentCatalogService } from '../treatment-catalog/treatment-catalog.service';
import { normalizeTeeth } from '../treatments/treatment.utils';
-import { ListLabCasesDto, UpdateLabCaseTaskDto } from './dto/cases.dto';
+import { ListLabCasesDto, UpdateLabCaseImportantDto } from './dto/cases.dto';
import { normalizeTaskTeeth } from './lab-case-task.util';
const labCaseListInclude = {
@@ -335,51 +335,39 @@ export class CasesService {
};
}
- async updateTask(
+ async setCaseImportant(
labCaseId: string,
- taskId: string,
- dto: UpdateLabCaseTaskDto,
+ dto: UpdateLabCaseImportantDto,
labOrganizationId: string,
actorUserId: string,
localeInput?: string | null,
) {
await this.assertCanEditCases(actorUserId, labOrganizationId);
- const task = await this.prisma.labCaseTask.findFirst({
+ const existing = await this.prisma.labCase.findFirst({
where: {
- id: taskId,
- labCaseId,
- labCase: {
- sentAt: { not: null },
- sends: { some: { organizationId: labOrganizationId } },
- },
+ id: labCaseId,
+ sentAt: { not: null },
+ sends: { some: { organizationId: labOrganizationId } },
},
+ select: { id: true },
});
- if (!task) {
- throw new NotFoundException('Task not found');
+ if (!existing) {
+ throw new NotFoundException('Case not found');
}
- const updated = await this.prisma.labCaseTask.update({
- where: { id: taskId },
+ await this.prisma.labCase.update({
+ where: { id: labCaseId },
data: { isImportant: dto.isImportant },
- include: {
- lastStatusChangedBy: { select: { id: true, name: true } },
- statusEvents: {
- orderBy: { changedAt: 'asc' },
- include: { changedBy: { select: { id: true, name: true } } },
- },
- },
});
- const locale = normalizeCatalogLocale(localeInput);
- const prosthesisLabels = await this.catalogLabels.resolveLabels(
- CatalogEntityKind.PROSTHESIS_TYPE,
- [updated.prosthesisTypeCode],
- locale,
- );
+ const labCase = await this.prisma.labCase.findFirstOrThrow({
+ where: { id: labCaseId },
+ include: labCaseListInclude,
+ });
- return { success: true, data: this.mapTask(updated, prosthesisLabels) };
+ return { success: true, data: await this.mapLabCaseDetail(labCase, localeInput) };
}
private buildListWhere(
@@ -499,6 +487,7 @@ export class CasesService {
return {
id: lc.id,
sentAt: lc.sentAt?.toISOString() ?? null,
+ isImportant: lc.isImportant,
clinic: lc.treatment.organization,
patient: lc.treatment.patient,
appointmentStartAt: lc.treatment.appointment?.startAt.toISOString() ?? null,
@@ -585,7 +574,6 @@ export class CasesService {
stepOrder: task.stepOrder,
stepLabel: task.stepLabel,
status: task.status,
- isImportant: task.isImportant,
createdAt: task.createdAt.toISOString(),
lastStatusChangedAt: task.lastStatusChangedAt?.toISOString() ?? null,
lastStatusChangedBy: task.lastStatusChangedBy
diff --git a/backend/src/modules/cases/dto/cases.dto.ts b/backend/src/modules/cases/dto/cases.dto.ts
index 6e567fd..afa969b 100644
--- a/backend/src/modules/cases/dto/cases.dto.ts
+++ b/backend/src/modules/cases/dto/cases.dto.ts
@@ -1,7 +1,7 @@
import { Transform } from 'class-transformer';
import { IsBoolean, IsDateString, IsInt, IsOptional, IsString, IsUUID, Max, Min } from 'class-validator';
-export class UpdateLabCaseTaskDto {
+export class UpdateLabCaseImportantDto {
@IsBoolean()
isImportant: boolean;
}
diff --git a/backend/src/modules/tasks/dto/tasks.dto.ts b/backend/src/modules/tasks/dto/tasks.dto.ts
index 9898378..c1ffe75 100644
--- a/backend/src/modules/tasks/dto/tasks.dto.ts
+++ b/backend/src/modules/tasks/dto/tasks.dto.ts
@@ -25,7 +25,14 @@ export class UpdateLabTaskDto {
status: LabTaskStatus;
}
-export type TaskSortField = 'date' | 'status' | 'clinic' | 'patient' | 'important';
+export type TaskSortField =
+ | 'date'
+ | 'status'
+ | 'clinic'
+ | 'patient'
+ | 'important'
+ | 'prosthesis'
+ | 'taskType';
export class ListLabTasksDto {
@IsOptional()
@@ -59,7 +66,7 @@ export class ListLabTasksDto {
sentTo?: string;
@IsOptional()
- @IsIn(['date', 'status', 'clinic', 'patient', 'important'])
+ @IsIn(['date', 'status', 'clinic', 'patient', 'important', 'prosthesis', 'taskType'])
sortBy?: TaskSortField;
@IsOptional()
diff --git a/backend/src/modules/tasks/tasks.service.ts b/backend/src/modules/tasks/tasks.service.ts
index 9c15b51..e3ded96 100644
--- a/backend/src/modules/tasks/tasks.service.ts
+++ b/backend/src/modules/tasks/tasks.service.ts
@@ -188,9 +188,9 @@ export class TasksService {
? { treatment: { organizationId: query.clinicOrganizationId } }
: {}),
...(query.q?.trim() ? { treatment: this.buildSearchWhere(query.q.trim()) } : {}),
+ ...(query.important !== undefined ? { isImportant: query.important } : {}),
},
...(status !== undefined ? { status } : {}),
- ...(query.important !== undefined ? { isImportant: query.important } : {}),
};
}
@@ -229,15 +229,24 @@ export class TasksService {
{ id: 'asc' },
];
case 'important':
- return [{ isImportant: dir }, { createdAt: 'desc' }, { id: 'asc' }];
+ return [{ labCase: { isImportant: dir } }, { createdAt: 'desc' }, { id: 'asc' }];
+ case 'prosthesis':
+ return [{ prosthesisTypeCode: dir }, { createdAt: 'desc' }, { id: 'asc' }];
+ case 'taskType':
+ return [
+ { workflowStepCode: dir },
+ { stepOrder: 'asc' },
+ { createdAt: 'desc' },
+ { id: 'asc' },
+ ];
case 'date':
default:
+ // date / caseId / taskId / stepId — newest first by default.
return [
{ labCase: { sentAt: dir } },
- { labCaseId: 'asc' },
- { treatmentDetailId: 'asc' },
- { stepOrder: 'asc' },
- { id: 'asc' },
+ { labCaseId: dir },
+ { id: dir },
+ { stepOrder: dir },
];
}
}
@@ -259,7 +268,7 @@ export class TasksService {
stepOrder: task.stepOrder,
stepLabel: task.stepLabel,
status: task.status,
- isImportant: task.isImportant,
+ isImportant: task.labCase.isImportant,
lastStatusChangedAt: task.lastStatusChangedAt?.toISOString() ?? null,
lastStatusChangedBy: task.lastStatusChangedBy
? { id: task.lastStatusChangedBy.id, name: task.lastStatusChangedBy.name }
diff --git a/frontend/messages/en.json b/frontend/messages/en.json
index 532a3e0..2ee0e28 100644
--- a/frontend/messages/en.json
+++ b/frontend/messages/en.json
@@ -335,6 +335,7 @@
"statusInProgress": "In progress",
"statusCompleted": "Completed",
"importantLabel": "Important",
+ "markCaseImportant": "Mark case as important",
"markImportant": "Mark as important",
"lastUpdatedBy": "Updated by {name}",
"lastUpdatedUnknown": "Not started yet",
@@ -353,7 +354,13 @@
"patientMobile": "Mobile",
"showComments": "Comments",
"commentsCount": "Comments ({count})",
- "latestAttachment": "Latest file",
+ "viewAttachments": "View all attachments",
+ "attachmentsDialogTitle": "Case attachments",
+ "attachmentsDialogSubtitle": "Preview and download files shared with this case.",
+ "noAttachments": "No attachments were shared with this case.",
+ "downloadAttachment": "Download",
+ "downloadAllAttachments": "Download all",
+ "attachmentPreviewUnavailable": "Preview unavailable",
"prevPage": "Previous",
"nextPage": "Next",
"pageSummary": "Page {page} of {totalPages} ({total} cases)",
@@ -391,6 +398,8 @@
"sortClinic": "Clinic",
"sortPatient": "Patient",
"sortImportant": "Important",
+ "sortProsthesis": "Prosthesis type",
+ "sortTaskType": "Task type",
"sortDirection": "Sort direction",
"clearFilters": "Clear filters",
"commentsButton": "Comments",
@@ -413,7 +422,10 @@
"clinicAuthor": "Clinic",
"errorLoad": "Failed to load comments.",
"errorPost": "Failed to post comment.",
- "errorToggle": "Failed to update comment visibility."
+ "errorToggle": "Failed to update comment visibility.",
+ "send": "Send comment",
+ "composerVisible": "Visible to clinic",
+ "composerHidden": "Hidden from clinic"
},
"appointments": {
"title": "Appointments",
diff --git a/frontend/messages/fa.json b/frontend/messages/fa.json
index d8e53b3..53b41c7 100644
--- a/frontend/messages/fa.json
+++ b/frontend/messages/fa.json
@@ -335,6 +335,7 @@
"statusInProgress": "در حال انجام",
"statusCompleted": "انجام شده",
"importantLabel": "مهم",
+ "markCaseImportant": "علامتگذاری پرونده بهعنوان مهم",
"markImportant": "علامتگذاری به عنوان مهم",
"lastUpdatedBy": "بهروزرسانی توسط {name}",
"lastUpdatedUnknown": "هنوز شروع نشده",
@@ -354,6 +355,13 @@
"showComments": "نظرات",
"commentsCount": "نظرات ({count})",
"latestAttachment": "آخرین فایل",
+ "viewAttachments": "مشاهده همه پیوستها",
+ "attachmentsDialogTitle": "پیوستهای پرونده",
+ "attachmentsDialogSubtitle": "پیشنمایش و دانلود فایلهای بهاشتراکگذاشتهشده با این پرونده.",
+ "noAttachments": "هیچ پیوستی با این پرونده بهاشتراک گذاشته نشده است.",
+ "downloadAttachment": "دانلود",
+ "downloadAllAttachments": "دانلود همه",
+ "attachmentPreviewUnavailable": "پیشنمایش در دسترس نیست",
"prevPage": "قبلی",
"nextPage": "بعدی",
"pageSummary": "صفحه {page} از {totalPages} ({total} پرونده)",
@@ -391,6 +399,8 @@
"sortClinic": "کلینیک",
"sortPatient": "بیمار",
"sortImportant": "مهم",
+ "sortProsthesis": "نوع پروتز",
+ "sortTaskType": "نوع کار",
"sortDirection": "جهت مرتبسازی",
"clearFilters": "پاک کردن فیلترها",
"commentsButton": "نظرات",
@@ -413,7 +423,10 @@
"clinicAuthor": "کلینیک",
"errorLoad": "بارگذاری نظرات ناموفق بود.",
"errorPost": "ثبت نظر ناموفق بود.",
- "errorToggle": "بهروزرسانی وضعیت نمایش نظر ناموفق بود."
+ "errorToggle": "بهروزرسانی وضعیت نمایش نظر ناموفق بود.",
+ "send": "ارسال نظر",
+ "composerVisible": "قابل مشاهده برای کلینیک",
+ "composerHidden": "پنهان از کلینیک"
},
"appointments": {
"title": "نوبتها",
diff --git a/frontend/messages/nl.json b/frontend/messages/nl.json
index 786520f..fb34543 100644
--- a/frontend/messages/nl.json
+++ b/frontend/messages/nl.json
@@ -335,6 +335,7 @@
"statusInProgress": "Bezig",
"statusCompleted": "Voltooid",
"importantLabel": "Belangrijk",
+ "markCaseImportant": "Zaak als belangrijk markeren",
"markImportant": "Markeren als belangrijk",
"lastUpdatedBy": "Bijgewerkt door {name}",
"lastUpdatedUnknown": "Nog niet gestart",
@@ -354,6 +355,13 @@
"showComments": "Opmerkingen",
"commentsCount": "Opmerkingen ({count})",
"latestAttachment": "Laatste bestand",
+ "viewAttachments": "Alle bijlagen bekijken",
+ "attachmentsDialogTitle": "Zaakbijlagen",
+ "attachmentsDialogSubtitle": "Bekijk en download bestanden die met deze zaak zijn gedeeld.",
+ "noAttachments": "Er zijn geen bijlagen met deze zaak gedeeld.",
+ "downloadAttachment": "Downloaden",
+ "downloadAllAttachments": "Alles downloaden",
+ "attachmentPreviewUnavailable": "Voorbeeld niet beschikbaar",
"prevPage": "Vorige",
"nextPage": "Volgende",
"pageSummary": "Pagina {page} van {totalPages} ({total} dossiers)",
@@ -391,6 +399,8 @@
"sortClinic": "Kliniek",
"sortPatient": "Patiënt",
"sortImportant": "Belangrijk",
+ "sortProsthesis": "Prothesetype",
+ "sortTaskType": "Taaktype",
"sortDirection": "Sorteerrichting",
"clearFilters": "Filters wissen",
"commentsButton": "Opmerkingen",
@@ -413,7 +423,10 @@
"clinicAuthor": "Kliniek",
"errorLoad": "Opmerkingen laden mislukt.",
"errorPost": "Opmerking plaatsen mislukt.",
- "errorToggle": "Zichtbaarheid bijwerken mislukt."
+ "errorToggle": "Zichtbaarheid bijwerken mislukt.",
+ "send": "Opmerking versturen",
+ "composerVisible": "Zichtbaar voor kliniek",
+ "composerHidden": "Verborgen voor kliniek"
},
"appointments": {
"title": "Afspraken",
diff --git a/frontend/src/app/[locale]/(dashboard)/cases/page.tsx b/frontend/src/app/[locale]/(dashboard)/cases/page.tsx
index c274887..da9bcf5 100644
--- a/frontend/src/app/[locale]/(dashboard)/cases/page.tsx
+++ b/frontend/src/app/[locale]/(dashboard)/cases/page.tsx
@@ -3,17 +3,17 @@
import { useCallback, useEffect, useMemo, useState } from 'react';
import { useSearchParams } from 'next/navigation';
import { useTranslations } from 'next-intl';
-import { MessageSquare } from 'lucide-react';
import { ToastStack } from '@/components/ui/shared/Toast';
import { formatApiErrorMessage } from '@/components/shared/formatApiError';
import { useAuth } from '@/lib/hooks/useAuth';
import { useToast } from '@/lib/hooks/useToast';
import { canEditCases, canEditTasks } from '@/components/shared/permissions';
-import { Badge } from '@/components/ui/shared/Badge';
+import { CaseDetailPanel, CaseTaskProgressBar } from '@/components/ui/lab/CaseDetailPanel';
import { LabCaseCommentsPanel } from '@/components/ui/lab/LabCaseCommentsPanel';
-import { CaseToothChartPanel } from '@/components/ui/lab/CaseToothChartPanel';
-import { LabCaseAttachmentPreview } from '@/components/ui/lab/LabCaseAttachmentPreview';
-import { labTaskStatusVariant } from '@/components/ui/lab/labTaskStatusDisplay';
+import {
+ formatCaseDateTime,
+ formatPatientName,
+} from '@/components/ui/lab/caseDetailUtils';
import { casesApi } from '@/lib/api/cases';
import { tasksApi } from '@/lib/api/tasks';
import { treatmentCatalogApi } from '@/lib/api/treatment-catalog';
@@ -29,47 +29,11 @@ import type {
LabTaskStatus,
PaginatedLabCases,
} from '@/types/cases';
-import {
- formatToothList,
- prosthesisTypeBadgeStyle,
-} from '@/components/ui/treatment/prosthesisTypeDisplay';
const PAGE_SIZE = 20;
-function formatPatientName(patient: { firstName: string; lastName: string }) {
- return `${patient.firstName} ${patient.lastName}`.trim();
-}
-
-function formatDateTime(value: string | null, locale: string) {
- if (!value) return '—';
- return new Intl.DateTimeFormat(locale, {
- dateStyle: 'medium',
- timeStyle: 'short',
- }).format(new Date(value));
-}
-
-function TaskProgressBar({ completed, total }: { completed: number; total: number }) {
- const pct = total > 0 ? Math.round((completed / total) * 100) : 0;
-
- return (
-
-
- {completed}/{total}
- {pct}%
-
-
-
- );
-}
-
export default function CasesPage() {
const t = useTranslations('cases');
- const tTreatment = useTranslations('treatment');
const tCommon = useTranslations('common');
const { currentOrganization, user } = useAuth();
const toast = useToast();
@@ -99,7 +63,7 @@ export default function CasesPage() {
const [selectedCase, setSelectedCase] = useState(null);
const [loadingList, setLoadingList] = useState(false);
const [loadingDetail, setLoadingDetail] = useState(false);
- const [updatingTaskId, setUpdatingTaskId] = useState(null);
+ const [updatingImportant, setUpdatingImportant] = useState(false);
const [commentCount, setCommentCount] = useState(0);
const canEdit = canEditCases(currentOrganization);
@@ -152,17 +116,23 @@ export default function CasesPage() {
}
};
- const loadDetail = async (caseId: string) => {
- setLoadingDetail(true);
+ const loadDetail = async (caseId: string, options?: { silent?: boolean }) => {
+ if (!options?.silent) {
+ setLoadingDetail(true);
+ }
toast.setError('');
try {
const response = await casesApi.getOne(caseId);
setSelectedCase(response.data);
} catch (error: unknown) {
toast.showError(formatApiErrorMessage(error, t('errorLoadDetail')));
- setSelectedCase(null);
+ if (!options?.silent) {
+ setSelectedCase(null);
+ }
} finally {
- setLoadingDetail(false);
+ if (!options?.silent) {
+ setLoadingDetail(false);
+ }
}
};
@@ -217,34 +187,6 @@ export default function CasesPage() {
[],
);
- const latestCaseAttachment = useMemo(() => {
- if (!selectedCase?.attachments.length) return null;
- return [...selectedCase.attachments].sort(
- (a, b) => new Date(b.createdAt).getTime() - new Date(a.createdAt).getTime(),
- )[0];
- }, [selectedCase?.attachments]);
-
- const caseProsthesisRows = useMemo(() => {
- if (!selectedCase) return [];
- if (selectedCase.toothProsthesis.length > 0) {
- const byCode = new Map();
- for (const row of selectedCase.toothProsthesis) {
- const key = row.prosthesisTypeCode;
- const teeth = byCode.get(key) ?? [];
- if (!teeth.includes(row.tooth)) teeth.push(row.tooth);
- byCode.set(key, teeth);
- }
- return [...byCode.entries()].map(([prosthesisTypeCode, teeth]) => ({
- prosthesisTypeCode,
- teeth,
- }));
- }
- return selectedCase.tasksByTooth.map((g) => ({
- prosthesisTypeCode: g.prosthesisTypeCode,
- teeth: g.teeth,
- }));
- }, [selectedCase]);
-
function clearFilters() {
setSearch('');
setClinicId('');
@@ -254,18 +196,22 @@ export default function CasesPage() {
setPage(1);
}
- async function handleImportantToggle(taskId: string, isImportant: boolean) {
- if (!selectedCaseId || !canEdit) return;
+ async function handleCaseImportantToggle(isImportant: boolean) {
+ if (!selectedCaseId || !canEdit || !selectedCase) return;
- setUpdatingTaskId(taskId);
+ const previousCase = selectedCase;
+ setSelectedCase({ ...selectedCase, isImportant });
+
+ setUpdatingImportant(true);
toast.setError('');
try {
- await casesApi.setTaskImportant(selectedCaseId, taskId, isImportant);
- await loadDetail(selectedCaseId);
+ const response = await casesApi.setCaseImportant(selectedCaseId, isImportant);
+ setSelectedCase(response.data);
} catch (error: unknown) {
+ setSelectedCase(previousCase);
toast.showError(formatApiErrorMessage(error, t('errorUpdateTask')));
} finally {
- setUpdatingTaskId(null);
+ setUpdatingImportant(false);
}
}
@@ -391,13 +337,13 @@ export default function CasesPage() {
{item.clinic.name}
- {formatDateTime(item.sentAt, locale)}
+ {formatCaseDateTime(item.sentAt, locale)}
{item.treatmentTypes.map(treatmentLabel).join(', ')}
-
@@ -445,181 +391,53 @@ export default function CasesPage() {
) : loadingDetail || !selectedCase ? (
{tCommon('loading')}
) : (
-
-
-
-
- {formatPatientName(selectedCase.patient)}
-
-
-
-
- {t('patientMobile')}: {selectedCase.patient.mobile}
-
+ void handleCaseImportantToggle(checked)}
+ headerMetaLines={
{t('fromClinic', { name: selectedCase.clinic.name })}
-
- {t('sentAt', { date: formatDateTime(selectedCase.sentAt, locale) })}
-
-
-
- {t('taskProgressLabel', {
- completed: selectedCase.taskProgress.completed,
- total: selectedCase.taskProgress.total,
- })}
-
-
-
-
-
-
-
- {latestCaseAttachment && selectedCaseId ? (
-
-
{t('latestAttachment')}
-
- ) : null}
-
-
- {selectedCase.details.length > 0 && (
-
-
{t('treatmentDetails')}
-
-
- )}
-
-
-
{t('tasksByTooth')}
- {selectedCase.tasksByTooth.length === 0 ? (
-
{t('noTasks')}
- ) : (
- selectedCase.tasksByTooth.map((group, groupIndex) => (
-
-
-
- {group.prosthesisTypeLabel}
-
-
- {t('toothGroupTitle', {
- teeth: formatToothList(group.teeth),
- prosthesis: group.prosthesisTypeLabel,
- })}
-
-
-
-
- ))
- )}
-
-
- {selectedCaseId ? (
-
- ) : null}
-
+
+ ) : null
+ }
+ />
)}
diff --git a/frontend/src/app/[locale]/(dashboard)/tasks/page.tsx b/frontend/src/app/[locale]/(dashboard)/tasks/page.tsx
index 64e9d36..91c47bf 100644
--- a/frontend/src/app/[locale]/(dashboard)/tasks/page.tsx
+++ b/frontend/src/app/[locale]/(dashboard)/tasks/page.tsx
@@ -51,13 +51,11 @@ export default function TasksPage() {
const [page, setPage] = useState(1);
const [loading, setLoading] = useState(false);
const [updatingTaskId, setUpdatingTaskId] = useState(null);
- const [expandedCommentsCaseId, setExpandedCommentsCaseId] = useState(null);
+ const [expandedCommentsTaskId, setExpandedCommentsTaskId] = useState(null);
const [search, setSearch] = useState('');
const [clinicId, setClinicId] = useState('');
- const [statusFilter, setStatusFilter] = useState<'' | LabTaskStatus>('');
- const [showCompleted, setShowCompleted] = useState(false);
- const [importantOnly, setImportantOnly] = useState(false);
+ const [statusFilter, setStatusFilter] = useState<'' | LabTaskStatus>('IN_PROGRESS');
const [sentFrom, setSentFrom] = useState('');
const [sentTo, setSentTo] = useState('');
const [sortBy, setSortBy] = useState('date');
@@ -87,18 +85,11 @@ export default function TasksPage() {
};
if (search.trim()) params.q = search.trim();
if (clinicId) params.clinicOrganizationId = clinicId;
- if (statusFilter) {
- params.status = statusFilter;
- } else if (showCompleted) {
- params.completed = undefined;
- } else {
- params.completed = false;
- }
- if (importantOnly) params.important = true;
+ if (statusFilter) params.status = statusFilter;
if (sentFrom) params.sentFrom = sentFrom;
if (sentTo) params.sentTo = sentTo;
return params;
- }, [page, search, clinicId, statusFilter, showCompleted, importantOnly, sentFrom, sentTo, sortBy, sortDir]);
+ }, [page, search, clinicId, statusFilter, sentFrom, sentTo, sortBy, sortDir]);
const clinicOptions = useMemo(() => {
const map = new Map();
@@ -228,10 +219,10 @@ export default function TasksPage() {
className={`${filterSelectClass} min-w-0 flex-1`}
>
-
-
+
+