improvement: task assignment flow added. cases and tasks feature updated accordingly.
This commit is contained in:
@@ -435,6 +435,10 @@
|
||||
"errorLoadList": "Failed to load cases.",
|
||||
"errorLoadDetail": "Failed to load case details.",
|
||||
"errorUpdateTask": "Failed to update task.",
|
||||
"errorAssignTask": "Failed to assign task.",
|
||||
"assigneeLabel": "Assigned to",
|
||||
"assigneeUnassigned": "Unassigned",
|
||||
"assignedTo": "Assigned to {name}",
|
||||
"filterClinic": "Clinic",
|
||||
"filterClinicAll": "All clinics",
|
||||
"filterTreatmentType": "Treatment type",
|
||||
@@ -483,6 +487,8 @@
|
||||
"filterStepCompletedAll": "Any step",
|
||||
"showCompleted": "Show completed",
|
||||
"importantOnly": "Important first",
|
||||
"assignedToMe": "Assigned to me",
|
||||
"assignedToStaff": "Assigned to {name}",
|
||||
"resetView": "Reset filters & sort",
|
||||
"showInCase": "Show in case",
|
||||
"showInCaseNotFound": "This task is not in the default in-progress list.",
|
||||
|
||||
@@ -435,6 +435,10 @@
|
||||
"errorLoadList": "بارگذاری پروندهها ناموفق بود.",
|
||||
"errorLoadDetail": "بارگذاری جزئیات پرونده ناموفق بود.",
|
||||
"errorUpdateTask": "بهروزرسانی وظیفه ناموفق بود.",
|
||||
"errorAssignTask": "واگذاری وظیفه ناموفق بود.",
|
||||
"assigneeLabel": "واگذار شده به",
|
||||
"assigneeUnassigned": "واگذار نشده",
|
||||
"assignedTo": "واگذار شده به {name}",
|
||||
"filterClinic": "کلینیک",
|
||||
"filterClinicAll": "همه کلینیکها",
|
||||
"filterTreatmentType": "نوع درمان",
|
||||
@@ -484,6 +488,8 @@
|
||||
"filterStepCompletedAll": "هر مرحلهای",
|
||||
"showCompleted": "نمایش تکمیلشدهها",
|
||||
"importantOnly": "مهمها در ابتدا",
|
||||
"assignedToMe": "واگذار شده به من",
|
||||
"assignedToStaff": "واگذار شده به {name}",
|
||||
"resetView": "بازنشانی فیلترها و مرتبسازی",
|
||||
"showInCase": "نمایش در پرونده",
|
||||
"showInCaseNotFound": "این کار در فهرست پیشفرض در حال انجام نیست.",
|
||||
|
||||
@@ -435,6 +435,10 @@
|
||||
"errorLoadList": "Dossiers laden mislukt.",
|
||||
"errorLoadDetail": "Dossierdetails laden mislukt.",
|
||||
"errorUpdateTask": "Taak bijwerken mislukt.",
|
||||
"errorAssignTask": "Taak toewijzen mislukt.",
|
||||
"assigneeLabel": "Toegewezen aan",
|
||||
"assigneeUnassigned": "Niet toegewezen",
|
||||
"assignedTo": "Toegewezen aan {name}",
|
||||
"filterClinic": "Kliniek",
|
||||
"filterClinicAll": "Alle klinieken",
|
||||
"filterTreatmentType": "Behandeltype",
|
||||
@@ -484,6 +488,8 @@
|
||||
"filterStepCompletedAll": "Elke stap",
|
||||
"showCompleted": "Voltooide tonen",
|
||||
"importantOnly": "Belangrijke cases eerst",
|
||||
"assignedToMe": "Toegewezen aan mij",
|
||||
"assignedToStaff": "Toegewezen aan {name}",
|
||||
"resetView": "Filters en sortering resetten",
|
||||
"showInCase": "In case tonen",
|
||||
"showInCaseNotFound": "Deze taak staat niet in de standaardlijst met taken in uitvoering.",
|
||||
|
||||
@@ -1,6 +1,16 @@
|
||||
import type { CSSProperties } from 'react';
|
||||
import type { BadgeVariant } from '@/components/ui/shared/Badge';
|
||||
import type { LabTaskStatus } from '@/types/cases';
|
||||
import type { LabTaskListItem, LabTaskStatus } from '@/types/cases';
|
||||
|
||||
export function canEditLabTaskStatus(
|
||||
task: Pick<LabTaskListItem, 'assignee'>,
|
||||
currentUserId: string | undefined,
|
||||
canEditTasks: boolean,
|
||||
): boolean {
|
||||
if (!canEditTasks || !currentUserId) return false;
|
||||
if (!task.assignee) return true;
|
||||
return task.assignee.id === currentUserId;
|
||||
}
|
||||
|
||||
export function labTaskStatusVariant(status: LabTaskStatus): BadgeVariant {
|
||||
return status === 'COMPLETED' ? 'success' : 'warning';
|
||||
|
||||
@@ -8,6 +8,7 @@ export type TasksViewState = {
|
||||
sortBy: TaskSortField;
|
||||
sortDir: 'asc' | 'desc';
|
||||
importantOnly: boolean;
|
||||
assignedToMe: boolean;
|
||||
page: number;
|
||||
highlightTaskId: string | null;
|
||||
};
|
||||
@@ -20,6 +21,7 @@ export const DEFAULT_TASKS_VIEW: TasksViewState = {
|
||||
sortBy: 'date',
|
||||
sortDir: 'desc',
|
||||
importantOnly: false,
|
||||
assignedToMe: false,
|
||||
page: 1,
|
||||
highlightTaskId: null,
|
||||
};
|
||||
@@ -35,6 +37,7 @@ export function isDefaultTasksView(state: TasksViewState): boolean {
|
||||
state.sortBy === DEFAULT_TASKS_VIEW.sortBy &&
|
||||
state.sortDir === DEFAULT_TASKS_VIEW.sortDir &&
|
||||
state.importantOnly === DEFAULT_TASKS_VIEW.importantOnly &&
|
||||
state.assignedToMe === DEFAULT_TASKS_VIEW.assignedToMe &&
|
||||
state.page === DEFAULT_TASKS_VIEW.page &&
|
||||
state.highlightTaskId === DEFAULT_TASKS_VIEW.highlightTaskId
|
||||
);
|
||||
|
||||
@@ -7,6 +7,7 @@ import { Badge } from '@/components/ui/shared/Badge';
|
||||
import { Button } from '@/components/ui/shared/Button';
|
||||
import { Checkbox } from '@/components/ui/shared/Checkbox';
|
||||
import { CaseToothChartPanel } from '@/components/ui/lab/CaseToothChartPanel';
|
||||
import { FORM_SELECT_CLASS } from '@/components/shared/formSelectStyles';
|
||||
import { LabCaseAttachmentPreview } from '@/components/ui/lab/LabCaseAttachmentPreview';
|
||||
import { LabCaseAttachmentsDialog } from '@/components/ui/lab/LabCaseAttachmentsDialog';
|
||||
import { labTaskStatusVariant } from '@/components/lab/labTaskStatusDisplay';
|
||||
@@ -21,7 +22,7 @@ import {
|
||||
formatPatientName,
|
||||
latestCaseAttachment,
|
||||
} from '@/components/lab/caseDetailUtils';
|
||||
import type { LabCaseDetail, LabTaskStatus } from '@/types/cases';
|
||||
import type { AssignableTaskStaff, LabCaseDetail, LabTaskStatus } from '@/types/cases';
|
||||
import type { ProsthesisCatalogEntry } from '@/types/treatment-catalog';
|
||||
|
||||
function CaseTaskProgressBar({ completed, total }: { completed: number; total: number }) {
|
||||
@@ -60,6 +61,10 @@ export interface CaseDetailPanelProps {
|
||||
updatingImportant?: boolean;
|
||||
onImportantChange?: (checked: boolean) => void;
|
||||
commentsSection?: ReactNode;
|
||||
assignableStaff?: AssignableTaskStaff[];
|
||||
canAssignTasks?: boolean;
|
||||
assigningTaskId?: string | null;
|
||||
onAssignTask?: (taskId: string, assigneeUserId: string | null) => void;
|
||||
}
|
||||
|
||||
export function CaseDetailPanel({
|
||||
@@ -76,6 +81,10 @@ export function CaseDetailPanel({
|
||||
updatingImportant = false,
|
||||
onImportantChange,
|
||||
commentsSection,
|
||||
assignableStaff = [],
|
||||
canAssignTasks = false,
|
||||
assigningTaskId = null,
|
||||
onAssignTask,
|
||||
}: CaseDetailPanelProps) {
|
||||
const t = useTranslations('cases');
|
||||
const [attachmentsDialogOpen, setAttachmentsDialogOpen] = useState(false);
|
||||
@@ -213,24 +222,42 @@ export function CaseDetailPanel({
|
||||
</div>
|
||||
<ul className="space-y-2">
|
||||
{group.tasks.map((task) => (
|
||||
<li key={task.id} className="rounded bg-background p-2 text-sm space-y-1">
|
||||
<div className="flex flex-wrap items-center gap-2">
|
||||
<span className="min-w-0 flex-1">
|
||||
<li key={task.id} className="rounded bg-background px-2 py-1.5 text-sm">
|
||||
<div className="flex flex-wrap items-center gap-x-2 gap-y-1">
|
||||
<span className="min-w-0 flex-1 font-medium text-text-primary">
|
||||
{task.stepOrder}. {task.stepLabel}
|
||||
</span>
|
||||
<span className="text-[11px] text-text-muted shrink-0">
|
||||
{task.lastStatusChangedBy
|
||||
? t('lastUpdatedBy', { name: task.lastStatusChangedBy.name })
|
||||
: t('lastUpdatedUnknown')}
|
||||
{task.lastStatusChangedAt
|
||||
? ` · ${formatCaseDateTime(task.lastStatusChangedAt, locale)}`
|
||||
: ''}
|
||||
</span>
|
||||
{canAssignTasks && onAssignTask ? (
|
||||
<select
|
||||
value={task.assignee?.id ?? ''}
|
||||
disabled={assigningTaskId === task.id}
|
||||
onChange={(e) =>
|
||||
onAssignTask(task.id, e.target.value ? e.target.value : null)
|
||||
}
|
||||
aria-label={t('assigneeLabel')}
|
||||
className={`${FORM_SELECT_CLASS} max-w-[9.5rem] rounded-md px-2 py-0.5 text-xs`}
|
||||
>
|
||||
<option value="">{t('assigneeUnassigned')}</option>
|
||||
{assignableStaff.map((staff) => (
|
||||
<option key={staff.id} value={staff.id}>
|
||||
{staff.name}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
) : null}
|
||||
<Badge variant={labTaskStatusVariant(task.status)} fixedWidth={false}>
|
||||
{statusOptions.find((opt) => opt.value === task.status)?.label ??
|
||||
task.status}
|
||||
</Badge>
|
||||
</div>
|
||||
<p className="text-[11px] text-text-muted">
|
||||
{task.lastStatusChangedBy
|
||||
? t('lastUpdatedBy', { name: task.lastStatusChangedBy.name })
|
||||
: t('lastUpdatedUnknown')}
|
||||
{task.lastStatusChangedAt
|
||||
? ` · ${formatCaseDateTime(task.lastStatusChangedAt, locale)}`
|
||||
: ''}
|
||||
</p>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
|
||||
@@ -24,6 +24,7 @@ import { FORM_SELECT_CLASS } from '@/components/shared/formSelectStyles';
|
||||
import { SearchBar } from '@/components/ui/shared/SearchBar';
|
||||
import type { TreatmentCatalogEntry } from '@/types/treatment-catalog';
|
||||
import type {
|
||||
AssignableTaskStaff,
|
||||
CasesFilterOptions,
|
||||
LabCaseDetail,
|
||||
LabCaseListItem,
|
||||
@@ -67,6 +68,8 @@ export function CasesPage() {
|
||||
const [loadingList, setLoadingList] = useState(false);
|
||||
const [loadingDetail, setLoadingDetail] = useState(false);
|
||||
const [updatingImportant, setUpdatingImportant] = useState(false);
|
||||
const [assignableStaff, setAssignableStaff] = useState<AssignableTaskStaff[]>([]);
|
||||
const [assigningTaskId, setAssigningTaskId] = useState<string | null>(null);
|
||||
const [commentCount, setCommentCount] = useState(0);
|
||||
|
||||
const canEdit = canEditCases(currentOrganization);
|
||||
@@ -142,8 +145,11 @@ export function CasesPage() {
|
||||
useEffect(() => {
|
||||
void casesApi.listFilterOptions().then((r) => setFilterOptions(r.data)).catch(() => {});
|
||||
void treatmentCatalogApi.list().then((r) => setTreatmentCatalog(r.data)).catch(() => {});
|
||||
if (canEdit) {
|
||||
void casesApi.listAssignableStaff().then((r) => setAssignableStaff(r.data)).catch(() => {});
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps -- mount-only initial fetch
|
||||
}, []);
|
||||
}, [canEdit]);
|
||||
|
||||
useEffect(() => {
|
||||
const caseIdFromUrl = searchParams.get('caseId');
|
||||
@@ -206,6 +212,21 @@ export function CasesPage() {
|
||||
setPage(1);
|
||||
}
|
||||
|
||||
async function handleAssignTask(taskId: string, assigneeUserId: string | null) {
|
||||
if (!selectedCaseId || !canEdit) return;
|
||||
|
||||
setAssigningTaskId(taskId);
|
||||
toast.setError('');
|
||||
try {
|
||||
const response = await casesApi.assignTask(selectedCaseId, taskId, assigneeUserId);
|
||||
setSelectedCase(response.data);
|
||||
} catch (error: unknown) {
|
||||
toast.showError(getUserFacingError(error, tErrors, t('errorAssignTask')));
|
||||
} finally {
|
||||
setAssigningTaskId(null);
|
||||
}
|
||||
}
|
||||
|
||||
async function handleCaseImportantToggle(isImportant: boolean) {
|
||||
if (!selectedCaseId || !canEdit || !selectedCase) return;
|
||||
|
||||
@@ -439,6 +460,12 @@ export function CasesPage() {
|
||||
canEditImportant={canEdit}
|
||||
updatingImportant={updatingImportant}
|
||||
onImportantChange={(checked) => void handleCaseImportantToggle(checked)}
|
||||
assignableStaff={assignableStaff}
|
||||
canAssignTasks={canEdit}
|
||||
assigningTaskId={assigningTaskId}
|
||||
onAssignTask={(taskId, assigneeUserId) =>
|
||||
void handleAssignTask(taskId, assigneeUserId)
|
||||
}
|
||||
headerMetaLines={
|
||||
<p className="text-sm text-text-muted">
|
||||
{t('fromClinic', { name: selectedCase.clinic.name })}
|
||||
|
||||
@@ -7,6 +7,7 @@ import { Button } from '@/components/ui/shared/Button';
|
||||
import { FORM_SELECT_CLASS } from '@/components/shared/formSelectStyles';
|
||||
import { LabCaseCommentsPanel } from '@/components/ui/lab/LabCaseCommentsPanel';
|
||||
import {
|
||||
canEditLabTaskStatus,
|
||||
labTaskStatusSelectStyle,
|
||||
labTaskStatusVariant,
|
||||
} from '@/components/lab/labTaskStatusDisplay';
|
||||
@@ -25,6 +26,7 @@ interface TaskRowProps {
|
||||
highlighted?: boolean;
|
||||
exiting?: boolean;
|
||||
canEdit: boolean;
|
||||
currentUserId?: string;
|
||||
statusOptions: { value: LabTaskStatus; label: string }[];
|
||||
updatingTaskId: string | null;
|
||||
commentsOpen: boolean;
|
||||
@@ -46,6 +48,7 @@ export function TaskRow({
|
||||
highlighted = false,
|
||||
exiting = false,
|
||||
canEdit,
|
||||
currentUserId,
|
||||
statusOptions,
|
||||
updatingTaskId,
|
||||
commentsOpen,
|
||||
@@ -56,6 +59,9 @@ export function TaskRow({
|
||||
onShowInCase,
|
||||
}: TaskRowProps) {
|
||||
const t = useTranslations('tasks');
|
||||
const canEditStatus = canEditLabTaskStatus(task, currentUserId, canEdit);
|
||||
const assignedToOther =
|
||||
Boolean(task.assignee) && task.assignee!.id !== currentUserId;
|
||||
const taskDate = new Intl.DateTimeFormat(locale, {
|
||||
year: 'numeric',
|
||||
month: 'short',
|
||||
@@ -75,7 +81,7 @@ export function TaskRow({
|
||||
return (
|
||||
<li id={`task-row-${task.id}`} className={rowClassName}>
|
||||
<div
|
||||
className={`flex flex-col gap-3 px-3 py-3 sm:grid sm:grid-cols-[minmax(0,1fr)_132px_auto] sm:items-center sm:gap-x-3 sm:gap-y-0.5 sm:py-2 ${
|
||||
className={`flex flex-col gap-3 px-3 py-3 sm:grid sm:grid-cols-[minmax(0,1fr)_minmax(8.5rem,11rem)_auto] sm:items-center sm:gap-x-3 sm:gap-y-0.5 sm:py-2 ${
|
||||
flatMode ? '' : 'ps-5'
|
||||
}`}
|
||||
>
|
||||
@@ -113,8 +119,12 @@ export function TaskRow({
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="flex sm:justify-center">
|
||||
{canEdit ? (
|
||||
<div
|
||||
className={`flex sm:justify-center pe-2 sm:pe-3 shrink-0 ${
|
||||
assignedToOther ? 'mb-1 sm:mb-0' : ''
|
||||
}`}
|
||||
>
|
||||
{canEditStatus && !exiting ? (
|
||||
<select
|
||||
value={task.status}
|
||||
disabled={updatingTaskId === task.id || exiting}
|
||||
@@ -128,6 +138,14 @@ export function TaskRow({
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
) : assignedToOther && !exiting ? (
|
||||
<Badge
|
||||
variant="default"
|
||||
fixedWidth={false}
|
||||
className="text-center whitespace-normal leading-snug max-w-[10.5rem]"
|
||||
>
|
||||
{t('assignedToStaff', { name: task.assignee!.name })}
|
||||
</Badge>
|
||||
) : (
|
||||
<Badge variant={labTaskStatusVariant(task.status)} fixedWidth={false}>
|
||||
{statusOptions.find((opt) => opt.value === task.status)?.label ?? task.status}
|
||||
@@ -135,7 +153,7 @@ export function TaskRow({
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-1.5 shrink-0 justify-between sm:justify-end flex-wrap">
|
||||
<div className="flex items-center gap-1.5 shrink-0 justify-between sm:justify-end flex-wrap ps-1 sm:ps-0">
|
||||
{flatMode && onShowInCase && !exiting ? (
|
||||
<Button
|
||||
type="button"
|
||||
|
||||
@@ -67,6 +67,7 @@ export function TasksPage() {
|
||||
);
|
||||
const [stepCompleted, setStepCompleted] = useState(DEFAULT_TASKS_VIEW.stepCompleted);
|
||||
const [importantOnly, setImportantOnly] = useState(DEFAULT_TASKS_VIEW.importantOnly);
|
||||
const [assignedToMe, setAssignedToMe] = useState(DEFAULT_TASKS_VIEW.assignedToMe);
|
||||
const [sortBy, setSortBy] = useState<TaskSortField>(DEFAULT_TASKS_VIEW.sortBy);
|
||||
const [sortDir, setSortDir] = useState<'asc' | 'desc'>(DEFAULT_TASKS_VIEW.sortDir);
|
||||
const [highlightTaskId, setHighlightTaskId] = useState<string | null>(
|
||||
@@ -101,8 +102,9 @@ export function TasksPage() {
|
||||
if (statusFilter) params.status = statusFilter;
|
||||
if (stepCompleted) params.stepCompleted = stepCompleted;
|
||||
if (importantOnly) params.pinImportant = true;
|
||||
if (assignedToMe) params.assignedToMe = true;
|
||||
return params;
|
||||
}, [page, search, clinicId, statusFilter, stepCompleted, importantOnly, sortBy, sortDir]);
|
||||
}, [page, search, clinicId, statusFilter, stepCompleted, importantOnly, assignedToMe, sortBy, sortDir]);
|
||||
|
||||
const displayModel = useMemo(() => groupTasksForDisplay(tasks, sortBy), [tasks, sortBy]);
|
||||
|
||||
@@ -117,6 +119,7 @@ export function TasksPage() {
|
||||
sortBy,
|
||||
sortDir,
|
||||
importantOnly,
|
||||
assignedToMe,
|
||||
page,
|
||||
highlightTaskId,
|
||||
}),
|
||||
@@ -128,6 +131,7 @@ export function TasksPage() {
|
||||
sortBy,
|
||||
sortDir,
|
||||
importantOnly,
|
||||
assignedToMe,
|
||||
page,
|
||||
highlightTaskId,
|
||||
],
|
||||
@@ -192,6 +196,7 @@ export function TasksPage() {
|
||||
setStatusFilter(DEFAULT_TASKS_VIEW.statusFilter);
|
||||
setStepCompleted(DEFAULT_TASKS_VIEW.stepCompleted);
|
||||
setImportantOnly(DEFAULT_TASKS_VIEW.importantOnly);
|
||||
setAssignedToMe(DEFAULT_TASKS_VIEW.assignedToMe);
|
||||
setSortBy(DEFAULT_TASKS_VIEW.sortBy);
|
||||
setSortDir(DEFAULT_TASKS_VIEW.sortDir);
|
||||
setPage(DEFAULT_TASKS_VIEW.page);
|
||||
@@ -307,6 +312,7 @@ export function TasksPage() {
|
||||
highlighted={highlightTaskId === task.id}
|
||||
exiting={exitingTaskIds.has(task.id)}
|
||||
canEdit={canEdit}
|
||||
currentUserId={user?.id}
|
||||
statusOptions={statusOptions}
|
||||
updatingTaskId={updatingTaskId}
|
||||
commentsOpen={expandedCommentsTaskId === task.id}
|
||||
@@ -428,6 +434,12 @@ export function TasksPage() {
|
||||
label={t('importantOnly')}
|
||||
className="text-xs [&_span:last-child]:text-xs"
|
||||
/>
|
||||
<Checkbox
|
||||
checked={assignedToMe}
|
||||
onChange={(checked) => applyFilterChange(() => setAssignedToMe(checked))}
|
||||
label={t('assignedToMe')}
|
||||
className="text-xs [&_span:last-child]:text-xs"
|
||||
/>
|
||||
{showReset ? (
|
||||
<Button type="button" variant="ghost" size="sm" onClick={resetView}>
|
||||
{t('resetView')}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { apiClient } from './client';
|
||||
import type {
|
||||
CasesFilterOptions,
|
||||
AssignableTaskStaff,
|
||||
LabCaseDetail,
|
||||
ListLabCasesParams,
|
||||
PaginatedLabCases,
|
||||
@@ -24,6 +25,22 @@ export const casesApi = {
|
||||
return response.data;
|
||||
},
|
||||
|
||||
listAssignableStaff: async (): Promise<{ success: boolean; data: AssignableTaskStaff[] }> => {
|
||||
const response = await apiClient.get('/cases/assignable-staff');
|
||||
return response.data;
|
||||
},
|
||||
|
||||
assignTask: async (
|
||||
caseId: string,
|
||||
taskId: string,
|
||||
assigneeUserId: string | null,
|
||||
): Promise<{ success: boolean; data: LabCaseDetail }> => {
|
||||
const response = await apiClient.patch(`/cases/${caseId}/tasks/${taskId}/assign`, {
|
||||
assigneeUserId,
|
||||
});
|
||||
return response.data;
|
||||
},
|
||||
|
||||
setCaseImportant: async (
|
||||
caseId: string,
|
||||
isImportant: boolean,
|
||||
|
||||
@@ -39,6 +39,8 @@ export interface LabCaseTask {
|
||||
stepOrder: number;
|
||||
stepLabel: string;
|
||||
status: LabTaskStatus;
|
||||
assignee: LabTaskUser | null;
|
||||
assignedAt: string | null;
|
||||
createdAt: string;
|
||||
lastStatusChangedAt: string | null;
|
||||
lastStatusChangedBy: LabTaskUser | null;
|
||||
@@ -150,6 +152,7 @@ export interface ListLabTasksParams {
|
||||
completed?: boolean;
|
||||
important?: boolean;
|
||||
pinImportant?: boolean;
|
||||
assignedToMe?: boolean;
|
||||
sentFrom?: string;
|
||||
sentTo?: string;
|
||||
stepCompleted?: string;
|
||||
@@ -167,6 +170,7 @@ export interface LocateTaskPageParams {
|
||||
status?: LabTaskStatus;
|
||||
important?: boolean;
|
||||
pinImportant?: boolean;
|
||||
assignedToMe?: boolean;
|
||||
stepCompleted?: string;
|
||||
sortBy?: TaskSortField;
|
||||
sortDir?: 'asc' | 'desc';
|
||||
@@ -192,6 +196,8 @@ export interface LabTaskListItem {
|
||||
stepLabel: string;
|
||||
status: LabTaskStatus;
|
||||
isImportant: boolean;
|
||||
assignee: LabTaskUser | null;
|
||||
assignedAt: string | null;
|
||||
lastStatusChangedAt: string | null;
|
||||
lastStatusChangedBy: LabTaskUser | null;
|
||||
createdAt: string;
|
||||
@@ -205,6 +211,11 @@ export interface TaskFilterOptions {
|
||||
workflowSteps: { code: string; label: string }[];
|
||||
}
|
||||
|
||||
export interface AssignableTaskStaff {
|
||||
id: string;
|
||||
name: string;
|
||||
}
|
||||
|
||||
export interface PaginatedLabTasks {
|
||||
items: LabTaskListItem[];
|
||||
pagination: {
|
||||
|
||||
Reference in New Issue
Block a user