Przejdź do głównej zawartości

Tasks

Na tej stronie
KomendaOpisTyp
list_tasksPobiera zadania (filtrowalne)query
create_taskTworzy pojedyncze zadaniewrite
bulk_create_tasksTworzy wiele zadańwrite
update_taskAktualizuje zadanie (status/priority/assignments/tags)write
delete_taskUsuwa zadaniewrite
unified_ai_generate_tasksGeneruje sugestie AI dla wizytywrite
unified_ai_list_task_suggestionsLista sugestii AIquery
unified_ai_accept_task_suggestionsAkceptuje sugestie AI → tworzy zadaniawrite
unified_ai_dismiss_task_suggestionsOdrzuca sugestie AIwrite

erDiagram
TASK {
string task_id PK
string user_id FK
string visit_id FK "Opcjonalnie"
string patient_id FK "Opcjonalnie"
string title
text description
string priority "low, medium, high"
string status "active, completed"
string task_type "manual | follow_up | general | ... (dowolny string)"
string source "user | ai | system"
string[] tags
json metadata
float confidence
string content_hash "deduplikacja dla wizyty"
timestamp created_at
timestamp updated_at
timestamp resolved_at "ustawiane gdy status=completed"
}
USER ||--o{ TASK : "assigned_to/created_by"
VISIT ||--o{ TASK : "context (visit_id)"
PATIENT ||--o{ TASK : "context (patient_id)"

Vista rozróżnia dwa typy zadań:

flowchart LR
subgraph TaskTypes["Typy zadań"]
SYSTEM[🤖 System/AI Tasks]
USER[👤 User Tasks]
end
subgraph Sources["Źródła"]
AI[AI Task Suggestions / System]
MANUAL[Manual Creation (drawer/workspace)]
end
AI --> SYSTEM
MANUAL --> USER
TypOpisŹródło
systemWygenerowane automatycznie (AI/automat)AI suggestions, automaty
userUtworzone ręcznie przez użytkownikaDashboard, Workspace

Pobiera zadania użytkownika z opcjonalnymi filtrami:

interface Task {
task_id: string;
title: string;
description: string | null;
status: 'active' | 'completed';
task_type: string;
priority: 'low' | 'medium' | 'high';
resolved_at: string | null;
patient_id: string | null;
visit_id: string | null;
assigned_to: string | null;
created_by: string | null;
source: string; // user | ai | system
tags?: string[];
metadata?: Record<string, unknown>;
confidence?: number;
content_hash?: string;
created_at: string;
updated_at: string;
}
const tasks = await safeInvoke<Task[]>(
'list_tasks',
withSessionPayload({
filter: {
assigned_to?: string;
created_by?: string;
patient_id?: string;
visit_id?: string;
statuses?: ('active' | 'completed')[];
priorities?: ('low' | 'medium' | 'high')[];
task_types?: string[];
sources?: string[];
search?: string;
include_resolved?: boolean; // false = filtruje completed
limit?: number; // domyślnie 200, max 500
},
})
);

Tworzy nowe zadanie:

interface CreateTaskRequest {
title: string;
description?: string;
priority?: 'low' | 'medium' | 'high'; // default: medium
status?: 'active' | 'completed'; // completed ustawia resolved_at
task_type?: string; // np. "general"
patient_id?: string;
visit_id?: string;
assigned_to?: string;
created_by?: string;
source?: string; // user|ai|system
tags?: string[];
metadata?: Record<string, unknown>;
}
const task = await safeInvoke<Task>(
'create_task',
withSessionPayload(request)
);

interface UpdateTaskRequest {
task_id: string;
title?: string;
description?: string;
priority?: 'low' | 'medium' | 'high';
status?: 'active' | 'completed'; // completed ustawia resolved_at, inne czyszczą resolved_at
task_type?: string;
patient_id?: string; // '' lub undefined = bez zmian; '' => NULL
visit_id?: string; // '' lub undefined = bez zmian; '' => NULL
assigned_to?: string;
metadata?: Record<string, unknown>;
tags?: string[];
clear_tags?: boolean;
}
const updated = await safeInvoke<Task>(
'update_task',
withSessionPayload(request)
);

await safeInvoke(
'delete_task',
withSessionPayload({ task_id })
);

Po finalizacji wizyty, AI może automatycznie zasugerować follow-up tasks:

sequenceDiagram
participant UI as VisitEditor
participant Tauri as Tauri Backend
participant AI as Unified AI
participant DB as SQLite
UI->>Tauri: finalize_visit(visit_id)
Tauri->>DB: UPDATE visits SET status = 'completed'
alt autoGenerateTasks enabled
Tauri->>AI: unified_ai_generate_tasks(visit_id)
AI->>AI: Analyze visit data (SOAP, transcript)
AI-->>Tauri: TaskSuggestion[]
Tauri->>DB: INSERT INTO ai_task_suggestions
Tauri--)UI: Event: vista-tasks-refresh
end
Tauri-->>UI: Success

Generuje sugestie zadań na podstawie wizyty:

const result = await safeInvoke<GenerateTasksResponse>(
'unified_ai_generate_tasks',
withSessionPayload({ visit_id })
);
interface GenerateTasksResponse {
success: boolean;
suggestions: TaskSuggestion[];
reused: boolean; // true jeśli użyto wcześniejszych
}
interface TaskSuggestion {
suggestion_id: string;
visit_id: string;
title: string;
description?: string;
priority: 'low' | 'medium' | 'high';
suggested_due_date?: string;
rationale?: string;
status: 'pending' | 'accepted' | 'dismissed';
}

Lista sugestii AI dla wizyty:

// Frontend (src/hooks/tasks/useAiTaskSuggestions.ts)
const suggestions = await safeInvoke<TaskSuggestion[]>(
'unified_ai_list_task_suggestions',
withSessionPayload({ visit_id })
);
// Tylko pending suggestions (nie accepted/dismissed)

Akceptuje sugestie i tworzy z nich prawdziwe zadania:

// Frontend
const result = await safeInvoke<AcceptResult>(
'unified_ai_accept_task_suggestions',
withSessionPayload({
suggestion_ids: ['sugg-1', 'sugg-2']
})
);
interface AcceptResult {
accepted_count: number;
created_tasks: Task[];
}
// Po akceptacji:
// 1. suggestion.status = 'accepted'
// 2. Tworzone są prawdziwe Task z task_type = 'system', source = 'ai'
// 3. Operacja odbywa się w transakcji (SQLite) żeby uniknąć "database is locked"
// 4. Event 'vista-tasks-refresh'

Odrzuca sugestie (nie tworzy zadań):

await safeInvoke(
'unified_ai_dismiss_task_suggestions',
withSessionPayload({
suggestion_ids: ['sugg-3']
})
);
// suggestion.status = 'dismissed'
// Nie pojawi się ponownie w liście

Workspace ma dedykowaną zakładkę “Reminders” dla zadań:

flowchart TB
subgraph Workspace["WorkspaceView"]
TABS[Tabs: Drafts | Transcripts | Saved | Reminders]
end
subgraph Reminders["Reminders Tab"]
PINNED[📌 Pinned Tasks]
USER_TASKS[👤 User Tasks]
AI_SUGG[🤖 AI Suggestions]
end
TABS --> Reminders
AI_SUGG -->|Accept| USER_TASKS
AI_SUGG -->|Dismiss| HIDDEN[Hidden]
src/hooks/workspace/useWorkspaceTasks.ts
const {
tasks,
pinnedTasks,
aiSuggestions,
loading,
createTask,
updateTask,
deleteTask,
toggleComplete,
pinTask,
unpinTask,
acceptAiSuggestions,
dismissAiSuggestions,
refreshTasks,
} = useWorkspaceTasks(visitId);

Użytkownik może skonfigurować zachowanie AI tasks w preferencjach:

// UserPreferences
interface TaskPreferences {
tasks_panel_mode: 'manual' | 'auto' | 'off';
auto_generate_tasks: boolean;
}
// manual: AI generuje sugestie, user musi zaakceptować
// auto: AI generuje i automatycznie akceptuje (tworzy zadania)
// off: AI nie generuje sugestii

EventPayloadKiedy
vista-tasks-refresh-Po zmianach w zadaniach
task-created{ task_id }Po utworzeniu
task-updated{ task_id }Po aktualizacji
task-deleted{ task_id }Po usunięciu
ai-suggestions-generated{ visit_id, count }Po wygenerowaniu sugestii AI
// Nasłuchiwanie
useEffect(() => {
const unlisten = window.addEventListener('vista-tasks-refresh', () => {
refreshTasks();
});
return () => window.removeEventListener('vista-tasks-refresh', unlisten);
}, []);

KodOpisRozwiązanie
task_not_foundZadanie nie istniejeSprawdź ID
visit_not_foundWizyta nie istnieje (dla AI tasks)Sprawdź visit_id
ai_generation_failedBłąd generowania sugestii AISprawdź dostępność AI
suggestion_already_processedSugestia już zaakceptowana/odrzuconaRefresh listy
invalid_priorityNieprawidłowy priorytetUżyj: low, medium, high


Jawne kontrakty pomiędzy frontendem (React/TS) a komendami Tauri.

Odpowiedź z list_tasks, create_task, update_task dekodowana przez decodeTask:

interface Task {
task_id: string; // globalny identyfikator (task_<uuid>)
title: string;
description: string | null;
status: 'active' | 'completed';
task_type: string; // general, follow-up, ...
priority: 'low' | 'medium' | 'high';
resolved_at: string | null;
patient_id: string | null;
visit_id: string | null;
assigned_to: string | null;
created_by: string | null;
source: string; // user, ai, assistant
tags: string[] | undefined;
metadata: Record<string, unknown> | undefined;
confidence: number | undefined;
content_hash: string | undefined;
created_at: string; // ISO timestamp
updated_at: string;
}

Normalizacja w tasksService.ts:

PoleWartość UIWysyłane do Tauri
status'active''active'
'completed''completed'
'pending', 'in_progress', 'snoozed', 'cancelled''active'
priority'urgent''high'
'normal''low'
'high', 'medium', 'low'bez zmian
{
"command": {
"title": "Sprawdź wyniki badań",
"description": "Zobacz panel krwi Mruczka",
"status": "active",
"priority": "medium",
"task_type": "general",
"patient_id": "pat_123",
"visit_id": "visit_456",
"source": "user",
"tags": ["lab"],
"metadata": {
"visit_reason": "kontrola"
}
}
}
{
"tasks": [
{ /* CreateTaskCommand */ },
{ /* CreateTaskCommand */ }
]
}
{
"filter": {
"assigned_to": "user_001",
"statuses": ["active"],
"priorities": ["high", "medium"],
"sources": ["user", "ai"],
"include_resolved": false,
"limit": 200
}
}

Sortowanie: priority DESC, created_at DESC. Default limit: 200, max: 500.

{
"command": {
"task_id": "task_abcd",
"title": "Sprawdź wyniki badań",
"priority": "high",
"status": "completed",
"metadata": { "completed_by": "user_001" },
"tags": ["lab", "done"]
}
}

Jeśli status = completed, backend ustawia resolved_at. Pusty patient_id/visit_id = NULL.

{
"command": { "task_id": "task_abcd" }
}

Response: void (błąd jeśli nie istnieje).


  • Status i priorytety są walidowane po stronie Tauri: status ∈ {active, completed}, priority ∈ {low, medium, high}. Inne wartości są odrzucane.
  • update_task z pustym patient_id/visit_id czyści powiązania (NULL).
  • content_hash używany do deduplikacji tytułów w kontekście wizyty (AI suggestions).
  • Operacje AI (generate/accept) pracują w transakcjach, aby uniknąć błędu database is locked.
  • Brak pin/unpin w backendzie (UI zarządza widocznością lokalnie).