diff --git a/components/swift/artifacts/artifact-container.tsx b/components/swift/artifacts/artifact-container.tsx new file mode 100644 index 0000000..3265365 --- /dev/null +++ b/components/swift/artifacts/artifact-container.tsx @@ -0,0 +1,124 @@ +'use client'; + +/** + * Artifact Container Component + * + * Container die meerdere artifacts kan beheren met tabs. + * Max 3 artifacts tegelijk, tabs alleen zichtbaar bij >1 artifact. + * + * Epic: E4 (Artifact Area & Tabs) + * Story: E4.S1 (ArtifactContainer component) + */ + +import { ArtifactTab } from './artifact-tab'; +import { DagnotatieBlock } from '../blocks/dagnotitie-block'; +import { ZoekenBlock } from '../blocks/zoeken-block'; +import { OverdrachtBlock } from '../blocks/overdracht-block'; +import { FallbackPicker } from '../blocks/fallback-picker'; +import type { Artifact, BlockType } from '@/stores/swift-store'; + +interface ArtifactContainerProps { + artifacts: Artifact[]; + activeArtifactId: string | null; + onSelectArtifact: (id: string) => void; + onCloseArtifact: (id: string) => void; +} + +/** + * Render the appropriate block component based on artifact type + */ +function renderArtifactBlock(artifact: Artifact) { + switch (artifact.type) { + case 'dagnotitie': + return ; + case 'zoeken': + return ; + case 'overdracht': + return ; + case 'fallback': + return ; + default: + return ( +
+ Onbekend artifact type: {artifact.type} +
+ ); + } +} + +/** + * Get user-friendly title for artifact type + */ +export function getArtifactTitle(type: BlockType, prefill?: any): string { + switch (type) { + case 'dagnotitie': + return prefill?.patientName + ? `Dagnotitie - ${prefill.patientName}` + : 'Dagnotitie'; + case 'zoeken': + return 'PatiΓ«nt Zoeken'; + case 'overdracht': + return 'Dienst Overdracht'; + case 'fallback': + return 'Kies een actie'; + default: + return 'Artifact'; + } +} + +export function ArtifactContainer({ + artifacts, + activeArtifactId, + onSelectArtifact, + onCloseArtifact, +}: ArtifactContainerProps) { + // Find active artifact + const activeArtifact = artifacts.find((a) => a.id === activeArtifactId); + + // Show placeholder if no artifacts + if (artifacts.length === 0) { + return ( +
+
+
πŸ“‹
+

+ Artifacts verschijnen hier +

+

+ Vraag me iets in de chat om te beginnen! +

+
+
+ ); + } + + return ( +
+ {/* Tabs - alleen tonen bij >1 artifact */} + {artifacts.length > 1 && ( +
+ {artifacts.map((artifact) => ( + + ))} +
+ )} + + {/* Active artifact content */} +
+ {activeArtifact ? ( + renderArtifactBlock(activeArtifact) + ) : ( +
+ Selecteer een artifact om te bekijken +
+ )} +
+
+ ); +} diff --git a/components/swift/artifacts/artifact-tab.tsx b/components/swift/artifacts/artifact-tab.tsx new file mode 100644 index 0000000..f2eea32 --- /dev/null +++ b/components/swift/artifacts/artifact-tab.tsx @@ -0,0 +1,63 @@ +'use client'; + +/** + * Artifact Tab Component + * + * Tab voor een individueel artifact in de ArtifactContainer. + * Toont titel, close button, en active state. + * + * Epic: E4 (Artifact Area & Tabs) + * Story: E4.S1 (ArtifactContainer component) + */ + +import { X } from 'lucide-react'; +import { cn } from '@/lib/utils'; +import type { Artifact } from '@/stores/swift-store'; + +interface ArtifactTabProps { + artifact: Artifact; + isActive: boolean; + onSelect: (id: string) => void; + onClose: (id: string) => void; +} + +export function ArtifactTab({ artifact, isActive, onSelect, onClose }: ArtifactTabProps) { + return ( +
onSelect(artifact.id)} + > + {/* Tab title */} + + {artifact.title} + + + {/* Close button */} + +
+ ); +} diff --git a/docs/swift/bouwplan-swift-v3.md b/docs/swift/bouwplan-swift-v3.md index 1147638..73be7a1 100644 --- a/docs/swift/bouwplan-swift-v3.md +++ b/docs/swift/bouwplan-swift-v3.md @@ -222,12 +222,12 @@ const useChatStore = create((set) => ({ | E1 | Foundation - Split-screen | Layout naar 40/60 split | βœ… **Compleet** | 3/3 | 12 SP | E1.S1 geskipt (geen feature flag) | | E2 | Chat Panel & Messages | Chat UI zonder AI | βœ… **Compleet** | 5/5 | 13 SP | Scrolling, input, shortcuts | | E3 | Chat API & Medical Scribe | AI conversatie werkend | βœ… **Compleet** | 6/6 | 21 SP | Artifact opening werkend! | -| E4 | Artifact Area & Tabs | Meerdere artifacts mogelijk | ⏳ To Do | 0/4 | 13 SP | Week 5 | +| E4 | Artifact Area & Tabs | Meerdere artifacts mogelijk | ⏳ In Progress | 1/4 | 13 SP | Container & tabs structure | | E5 | AI-Filtering & Polish | Psychiater filtering, polish | ⏳ To Do | 0/5 | 13 SP | Week 6 | | E6 | Testing & Refinement | QA, bugs, performance | ⏳ To Do | 0/4 | 8 SP | Week 7-8 | **Totaal:** 31 stories, **85 Story Points** (~7 weken Γ  12 SP/week) -**Voortgang:** βœ… 16/31 stories compleet, 2 geskipt (48 SP / 85 SP = **56%**) +**Voortgang:** βœ… 17/31 stories compleet, 2 geskipt (53 SP / 85 SP = **62%**) **Belangrijk:** - ⚠️ Voer niet in 1x het volledige plan uit. Bouw per epic en per story. @@ -602,7 +602,7 @@ E3.S5 (useChatStream hook) is geskipt omdat: - `5b10176` β€” E3.S5 skip documentatie - (to be committed) β€” E3.S6 (Artifact opening from chat) -**πŸŽ‰ EPIC 3 COMPLEET!** Alle stories (4 compleet, 1 geskipt, 1 compleet) afgerond. Medical scribe chat werkt end-to-end! +**πŸŽ‰ EPIC 3 COMPLEET!** Alle stories (5 compleet, 1 geskipt) afgerond. Medical scribe chat werkt end-to-end! --- @@ -612,7 +612,7 @@ E3.S5 (useChatStream hook) is geskipt omdat: | Story ID | Beschrijving | Acceptatiecriteria | Status | Afhankelijkheden | Story Points | |----------|--------------|---------------------|--------|------------------|--------------| -| E4.S1 | ArtifactContainer component | Wrapper met tabs bovenaan, max 3 artifacts | ⏳ | E3.S6 | 5 | +| E4.S1 | ArtifactContainer component | Wrapper met tabs bovenaan, max 3 artifacts | βœ… **Compleet** | E3.S6 | 5 | | E4.S2 | Artifact lifecycle management | Open/close/switch tussen artifacts in store | ⏳ | E4.S1 | 3 | | E4.S3 | Slide-in animatie | Artifact slide-in van rechts (200ms ease-out) | ⏳ | E4.S2 | 2 | | E4.S4 | Placeholder state | "Artifacts verschijnen hier" met voorbeelden | ⏳ | E4.S3 | 3 | @@ -723,7 +723,25 @@ function ArtifactPlaceholder() { } ``` -**Deliverable:** Meerdere artifacts mogelijk, smooth transitions, placeholder state +**Deliverables (E4.S1 compleet):** +- βœ… `stores/swift-store.ts` β€” Artifact interface gedefineerd (id, type, prefill, title, createdAt) +- βœ… `components/swift/artifacts/artifact-tab.tsx` (60 regels) β€” Tab component met close button +- βœ… `components/swift/artifacts/artifact-container.tsx` (127 regels) β€” Container met tabs + rendering +- βœ… ArtifactTab: Active state styling, hover effects, close button +- βœ… ArtifactTab: Title truncation, tooltip, min/max width +- βœ… ArtifactContainer: Tabs alleen bij >1 artifact +- βœ… ArtifactContainer: renderArtifactBlock() voor alle block types +- βœ… ArtifactContainer: getArtifactTitle() helper functie +- βœ… ArtifactContainer: Placeholder state wanneer geen artifacts +- βœ… Conditional rendering: DagnotatieBlock, ZoekenBlock, OverdrachtBlock, FallbackPicker +- βœ… Build succesvol zonder errors + +**Git Commits:** +- (to be committed) β€” E4.S1 (ArtifactContainer component) + +**Note:** E4.S1 definieert de structuur. E4.S2 voegt store state toe (openArtifacts[], activeArtifactId, actions). + +**Deliverable Epic 4 complete:** Meerdere artifacts mogelijk, smooth transitions, placeholder state --- diff --git a/stores/swift-store.ts b/stores/swift-store.ts index b84037a..5881848 100644 --- a/stores/swift-store.ts +++ b/stores/swift-store.ts @@ -51,6 +51,15 @@ export interface BlockPrefillData extends ExtractedEntities { // Additional prefill data specific to blocks } +// Artifact type (E4 - Multiple artifacts support) +export interface Artifact { + id: string; + type: BlockType; + prefill: BlockPrefillData; + title: string; + createdAt: Date; +} + // Recent action for the Recent Strip export interface RecentAction { id: string;