Przejdź do głównej zawartości

Workspace Commands

Workspace (Pracownia) to centralny hub do pracy z wieloma wizytami naraz. W odróżnieniu od VisitsHubView (edycja pojedynczej wizyty), Workspace agreguje:

  • Notes - szkice wizyt (draft SOAP + internal notes)
  • Transcripts - transkrypcje audio (pending + archiwum)
  • Saved - zapisane notatki
  • Reminders - zadania/przypomnienia w trybie Pracownia
src/components/workspace/
├── WorkspaceView.tsx # Entry point
├── WorkspaceDraftsPanel.tsx # Tab: Notes
├── WorkspaceTranscripts.tsx # Tab: Transcripts
├── WorkspaceSavedNotes.tsx # Tab: Saved
└── WorkspaceReminders.tsx # Tab: Reminders

flowchart TB
subgraph WorkspaceView
Tabs[Tab Navigation]
Tabs --> Notes[Notes Panel]
Tabs --> Transcripts[Transcripts Panel]
Tabs --> Saved[Saved Notes Panel]
Tabs --> Reminders[Reminders Panel]
end
subgraph Hooks
useWorkspaceData --> Notes
useWorkspaceData --> Transcripts
useNotes --> Saved
useTasks --> Reminders
useAiTaskSuggestions --> Reminders
end
subgraph Backend
visitApi[visitApi.getWithDetailsPaginated]
audioApi[audioApi.saveFile]
tasksService[tasksService CRUD]
end
useWorkspaceData --> visitApi
useWorkspaceData --> audioApi
Reminders --> tasksService

Plik: src/hooks/workspace/useWorkspaceData.ts

// Ładowanie wizyt z ostatnich 7 dni
visitApi.getWithDetailsPaginated(
{ page, page_size: 8, sort_by: 'updated_at', sort_order: 'desc' },
{ statuses: ['draft', 'sent', 'finalized'], days: 7 }
)

Przed zapisaniem do stanu, hook stosuje lokalną retencję:

  • Jeśli audio/transkrypt starsze niż AUDIO_RETENTION_HOURS / TRANSCRIPT_RETENTION_HOURS
  • Czyści pola audio/transkrypt w stanie FE (bez kasowania w DB)
BuilderTypeZawartość
buildDraftItem(visit)'draft'SOAP fields, internal notes, AI suggestions
buildTranscriptItem(visit)'transcript'transcript + pending, preview, tags
buildArchivedTranscriptItem(archive)'transcript'Archiwum z tagiem ARCHIWUM
// Event listeners w useWorkspaceData
'transcription_completed'fetchVisits()
'ai_suggestions_generated'fetchVisits()
'visit-processing-timeout'fetchVisits()
FunkcjaOpisBackend Call
updateSection(id, section, value)Lokalna aktualizacja SOAP-
persistInternalVisitNotes(id, value)Zapis internal notesvisitApi.update
persistSection(id, section, value)Zapis sekcji SOAPvisitApi.update
attachAudio(id, payload)Attach + transkrypcjaaudioApi.saveFile, transcribeAndSaveToVisit
mergePendingTranscript(id)Merge pending do głównegomerge_pending_transcript
dismissPendingTranscript(id)Odrzuć + archiwizujdismiss_pending_transcript
saveDraft(id, overrides?)Zapisz jako draftvisitApi.update

Panel pracy nad szkicami wizyt z ostatnich 7 dni.

interface WorkspaceDraftsPanelProps {
drafts: WorkspaceItem[];
transcripts: WorkspaceItem[];
loading: boolean;
error: Error | null;
// Operations
updateSection: (id, section, value) => void;
persistInternalVisitNotes: (id, value) => Promise<void>;
persistSection: (id, section, value) => Promise<void>;
attachAudio: (id, payload) => Promise<void>;
saveDraft: (id, overrides?) => Promise<void>;
// Transcript actions
runTranscriptAction: (...) => Promise<void>;
getTranscriptActionState: (...) => ActionState;
// Navigation
onOpenPatientProfile: (patientId) => void;
onOpenPatientHistory: (patientId) => void;
}
  • Widzi listę szkiców wizyt (SOAP + internal notes)
  • Koryguje sekcje SOAP → persistSectionvisitApi.update
  • Edytuje wewnętrzne notatki → persistInternalVisitNotes
  • Podpina audio → attachAudio → zapis i transkrypcja
  • Zapisuje całą notatkę → saveDraft

Jeśli backend zapisze ai_sugestie dla wizyty, parseAiSuggestions mapuje je na pola interview/exam/diagnosis/plan i panel pokazuje je jako propozycje.


const {
filteredTranscripts, // Po czasie i search
expandedTranscripts, // Rozwinięte karty
pagination,
// Delete lifecycle
pendingTranscript,
isDeletingTranscript,
handleConfirmDeleteTranscript,
handleCancelDeleteTranscript,
} = useWorkspaceTranscriptsPanel(transcripts);
  • Time range: getDefaultRangeForTab('transcripts') → domyślnie 48h
  • Search: pacjent, tagi, tekst transkryptu
ActionOpis
Expand/CollapseRozwijanie kart transkryptów
MergerunTranscriptAction → merge pending
DismissrunTranscriptAction → dismiss + archiwum
CopyhandleCopyTranscript → clipboard + toast
DeleteConfirmation flow → usunięcie
// WorkspaceView nasłuchuje
'vista.workspace.audioAttached' → { visitId, durationSec }
// Pokazuje toast
"Audio X:YY przypięte do [Pacjent]"

const {
sortedNotes, // sortowane po createdAt desc
filteredNotes, // po search
paginatedNotes,
// Operations
handleCopySavedNote, // clipboard + recordRoamEvent
handlePinSavedNote, // pinNote()
handleRemoveSavedNote, // removeSaved()
handleUpdateSavedNote, // updateSaved() z walidacją
} = useWorkspaceSavedNotesPanel();
const { saved } = useNotes();
// sortuje po createdAt desc
// filtruje po tekście notatki
// paginuje z DEFAULT_LIST_PAGE_SIZE

Zadania i przypomnienia w kontekście Pracowni.

flowchart LR
useTasks["useTasks({ pollIntervalMs: 120_000 })"] --> workspaceTasks
workspaceTasks --> filteredWorkspaceTasks
subgraph Filters
taskFilter["all | completed | system | user"]
classifyTaskOwnership
isTaskOpen
end
Filters --> filteredWorkspaceTasks
const {
reminderFilter, // 'mine' | 'vista' | 'all' | 'completed'
searchQuery,
selectedTags,
// Operations
completeTaskAndNotify,
handlePriorityChange, // low → medium → high (cyclic)
handleTitleChange,
handleDescriptionChange,
handleTagsChange,
// CRUD
handleAddTask, // opens TaskCreateDialog
handleCreateTask, // buildManualUserTaskCreateInput → create_task
handlePatientAssign, // update_task with patient_id
handleDeleteTask, // confirm → delete_task
} = useWorkspaceRemindersPanel();
// handleCreateTask flow
const payload = buildManualUserTaskCreateInput({
title,
description,
priority,
patientId, // optional
});
await tasksService.create(payload); // → create_task
refreshWorkspaceTasks();
// lastAiVisitId z localStorage
const lastAiVisitId = localStorage.getItem('vista.ai.lastVisitId');
// Event aktualizujący
'vista-open-workspace-ai-suggestions' → updates lastAiVisitId
// Hook
const { aiSuggestions, aiLoading, aiAccept, aiDismiss } =
useAiTaskSuggestions(aiTasksEnabled ? lastAiVisitId : null);
// Przekazane do panelu
onAiAccept(ids) → aiAccept(ids) + refreshWorkspaceTasks()
onAiDismiss(ids) → aiDismiss(ids)

AspektWorkspaceVisitsHubView
ScopeWiele wizyt (7 dni)Pojedyncza wizyta
Primary UsePrzegląd, transkrypcje, notatkiEdycja SOAP, finalizacja
AITask suggestionsSOAP generation
TabsNotes/Transcripts/Saved/RemindersVisit tabs
Entry PointWorkspaceView.tsxVisitsWorkspaceView.tsxVisitsHubView.tsx