Przejdź do głównej zawartości

Component Structure

Vista zawiera ~330 komponentów React zorganizowanych w domenowe katalogi:

src/components/
├── ai/ # 12 komponentów AI
├── analytics/ # 8 komponentów raportów
├── audio/ # 15 komponentów audio
├── auth/ # 9 komponentów auth
├── calendar/ # 18 komponentów kalendarza
├── chat/ # 11 komponentów czatu AI
├── dashboard/ # 14 komponentów dashboardu
├── layout/ # 7 komponentów layoutu
├── onboarding/ # 12 komponentów onboardingu
├── patients/ # 22 komponenty pacjentów
├── settings/ # 16 komponentów ustawień
├── tasks/ # 9 komponentów zadań
├── ui/ # 70+ reusable UI components
└── visits/ # 38 komponentów wizyt

Najważniejszy moduł - workflow tworzenia wizyt medycznych.

KomponentOpis
NewVisitView.tsxGłówny widok tworzenia wizyty
VisitEditor.tsxEdycja istniejącej wizyty
SOAPEditor.tsxEdytor notatek SOAP
AudioRecordingSection.tsxSekcja nagrywania
TranscriptionViewer.tsxPodgląd transkrypcji
AISuggestionsViewer.tsxSugestie AI
VisitsList.tsxLista wizyt z filtrowaniem
VisitCard.tsxKarta pojedynczej wizyty
VisitDetails.tsxSzczegóły wizyty
PatientSelectionStep.tsxWybór pacjenta
VisitTypeSelector.tsxWybór typu wizyty
flowchart TD
NVV[NewVisitView] --> PSS[PatientSelectionStep]
NVV --> VTS[VisitTypeSelector]
NVV --> ARS[AudioRecordingSection]
NVV --> SE[SOAPEditor]
SE --> TV[TranscriptionViewer]
SE --> ASV[AISuggestionsViewer]
ARS --> RC[RecordingControls]
ARS --> WF[WaveformDisplay]

Zarządzanie pacjentami - zwierzęta i właściciele.

KomponentOpis
PatientsView.tsxGłówny widok pacjentów
PatientsList.tsxLista z wyszukiwaniem
PatientCard.tsxKarta pacjenta
PatientDetails.tsxSzczegóły pacjenta
NewPatientForm.tsxFormularz nowego pacjenta
PatientEditForm.tsxEdycja pacjenta
PatientHistory.tsxHistoria wizyt
OwnerInfoSection.tsxDane właściciela
MedicalInfoSection.tsxDane medyczne
VaccinationStatus.tsxStatus szczepień

System planowania wizyt z pełnym workflow.

KomponentOpis
CalendarView.tsxGłówny widok kalendarza
DayView.tsxWidok dzienny
WeekView.tsxWidok tygodniowy
MonthView.tsxWidok miesięczny
MiniCalendar.tsxMały kalendarz (picker)
AppointmentForm.tsxFormularz wizyty
AppointmentCard.tsxKarta wizyty
TimeSlotPicker.tsxWybór slotu czasowego
ConflictWarning.tsxOstrzeżenie o konflikcie
RecurringAppointment.tsxWizyty cykliczne

Przetwarzanie audio - nagrywanie i transkrypcja.

KomponentOpis
RecordingControls.tsxPrzyciski start/stop/pause
WaveformDisplay.tsxWizualizacja fali dźwiękowej
AudioPlayer.tsxOdtwarzacz nagrań
TranscriptionProgress.tsxPasek postępu STT
SpeakerLabels.tsxEtykiety mówców (diaryzacja)
AudioSettings.tsxUstawienia mikrofonu
VoiceActivityIndicator.tsxWskaźnik VAD
NoiseReductionToggle.tsxRedukcja szumów

Konfiguracja aplikacji - wszystkie preferencje.

KomponentOpis
SettingsView.tsxGłówny widok ustawień
ProfileSettings.tsxProfil użytkownika
AIPreferences.tsxPreferencje AI
NotificationSettings.tsxPowiadomienia
SecuritySettings.tsxBezpieczeństwo
BiometricSetup.tsxKonfiguracja biometrii
ThemeSelector.tsxWybór motywu
LanguageSelector.tsxWybór języka
DataRetention.tsxRetencja danych
ImportExport.tsxImport/eksport

70+ reusable components - design system.

ui/
├── Button.tsx # Przycisk z wariantami
├── Input.tsx # Pole tekstowe
├── Select.tsx # Dropdown
├── Checkbox.tsx # Checkbox
├── Radio.tsx # Radio buttons
├── Switch.tsx # Toggle switch
├── Slider.tsx # Suwak
├── DatePicker.tsx # Wybór daty
├── TimePicker.tsx # Wybór czasu
└── FileUpload.tsx # Upload plików

// Każdy komponent ma zdefiniowany interfejs props
interface PatientCardProps {
patient: Patient;
onSelect?: (patient: Patient) => void;
onEdit?: (patient: Patient) => void;
isSelected?: boolean;
showActions?: boolean;
className?: string;
}
export const PatientCard: React.FC<PatientCardProps> = ({
patient,
onSelect,
onEdit,
isSelected = false,
showActions = true,
className,
}) => {
// ...
};
// Dla złożonych komponentów (np. Form, Modal)
<Form onSubmit={handleSubmit}>
<Form.Field name="name" label="Imię">
<Form.Input placeholder="Wpisz imię..." />
</Form.Field>
<Form.Field name="species" label="Gatunek">
<Form.Select options={speciesOptions} />
</Form.Field>
<Form.Actions>
<Form.Submit>Zapisz</Form.Submit>
<Form.Cancel>Anuluj</Form.Cancel>
</Form.Actions>
</Form>
// Formularze z react-hook-form + zod
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
const PatientForm = () => {
const form = useForm<PatientFormData>({
resolver: zodResolver(PatientSchema),
defaultValues: {
name: '',
species: 'dog',
},
});
return (
<form onSubmit={form.handleSubmit(onSubmit)}>
<Controller
name="name"
control={form.control}
render={({ field, fieldState }) => (
<Input
{...field}
error={fieldState.error?.message}
/>
)}
/>
</form>
);
};

import { cn } from '@/utils/cn';
interface ButtonProps {
variant?: 'primary' | 'secondary' | 'ghost';
size?: 'sm' | 'md' | 'lg';
className?: string;
}
const Button = ({ variant = 'primary', size = 'md', className, ...props }) => {
return (
<button
className={cn(
// Base styles
'inline-flex items-center justify-center rounded-lg font-medium transition-colors',
// Variant styles
{
'bg-blue-600 text-white hover:bg-blue-700': variant === 'primary',
'bg-gray-200 text-gray-900 hover:bg-gray-300': variant === 'secondary',
'bg-transparent hover:bg-gray-100': variant === 'ghost',
},
// Size styles
{
'px-3 py-1.5 text-sm': size === 'sm',
'px-4 py-2 text-base': size === 'md',
'px-6 py-3 text-lg': size === 'lg',
},
// Custom className override
className
)}
{...props}
/>
);
};
// Wszystkie komponenty wspierają dark mode
<div className="bg-white dark:bg-gray-900">
<h1 className="text-gray-900 dark:text-white">
{title}
</h1>
<p className="text-gray-600 dark:text-gray-400">
{description}
</p>
</div>

// Każdy major view ma error boundary
import { ErrorBoundary } from 'react-error-boundary';
const VisitsView = () => (
<ErrorBoundary
FallbackComponent={ErrorFallback}
onReset={() => window.location.reload()}
>
<VisitsList />
</ErrorBoundary>
);
const ErrorFallback = ({ error, resetErrorBoundary }) => (
<div className="p-6 text-center">
<h2 className="text-xl font-bold text-red-600">
Coś poszło nie tak
</h2>
<p className="text-gray-600 mt-2">{error.message}</p>
<Button onClick={resetErrorBoundary} className="mt-4">
Spróbuj ponownie
</Button>
</div>
);

src/components/patients/__tests__/PatientCard.test.tsx
import { render, screen, fireEvent } from '@testing-library/react';
import { PatientCard } from '../PatientCard';
const mockPatient = {
patient_id: '123',
name: 'Burek',
species: 'dog',
breed: 'Labrador',
owner_name: 'Jan Kowalski',
};
describe('PatientCard', () => {
it('displays patient information', () => {
render(<PatientCard patient={mockPatient} />);
expect(screen.getByText('Burek')).toBeInTheDocument();
expect(screen.getByText('Labrador')).toBeInTheDocument();
expect(screen.getByText('Jan Kowalski')).toBeInTheDocument();
});
it('calls onSelect when clicked', () => {
const onSelect = vi.fn();
render(<PatientCard patient={mockPatient} onSelect={onSelect} />);
fireEvent.click(screen.getByRole('button'));
expect(onSelect).toHaveBeenCalledWith(mockPatient);
});
});