Przejdź do głównej zawartości

Calendar Module

Appointments są planowanymi wizytami, które mogą stać się Visits. ~25 plików, ~15k LOC.

sequenceDiagram
participant User
participant Calendar as CalendarView
participant AppService as appointmentsService
participant Rust as Tauri Commands
participant DB as SQLite
%% Tworzenie appointment
User->>Calendar: Schedule appointment
Calendar->>AppService: createAppointment(request)
AppService->>Rust: create_appointment
Rust->>DB: INSERT INTO appointments
DB-->>Rust: appointment_id
Rust-->>Calendar: Appointment created
%% Konwersja na wizytę
User->>Calendar: Start Visit from appointment
Calendar->>AppService: completeAppointment(aptId, visitId, userId)
AppService->>Rust: complete_appointment
Rust->>DB: UPDATE appointments SET status='completed', visit_id=?
DB-->>Calendar: Appointment linked to visit
erDiagram
APPOINTMENTS ||--o| VISITS : "becomes"
APPOINTMENTS ||--|| PATIENTS : "for"
APPOINTMENTS ||--|| USERS : "veterinarian"
APPOINTMENTS {
string appointment_id PK
string patient_id FK
string veterinarian_id FK
string scheduled_date
string scheduled_time
int duration_minutes
string appointment_type
enum status "scheduled|completed|cancelled|no_show"
string visit_id FK "nullable - set on complete"
string notes
bool is_recurring
string recurrence_pattern
bool reminder_enabled
bool reminder_email_sent
}
stateDiagram-v2
[*] --> scheduled: create_appointment
scheduled --> completed: complete_appointment (link visit_id)
scheduled --> cancelled: cancel_appointment
scheduled --> no_show: mark_no_show
completed --> [*]
cancelled --> [*]
no_show --> [*]
FE MethodRust CommandDescription
createAppointment(request)create_appointmentSchedule new appointment
updateAppointment(request)update_appointmentModify appointment
cancelAppointment(id)cancel_appointmentCancel appointment
completeAppointment(aptId, visitId)complete_appointmentLink to visit, mark done
deleteAppointment(id)delete_appointmentRemove appointment
getAppointments(filter)get_appointmentsQuery appointments
checkForConflicts(...)check_comprehensive_staff_conflictsCheck time conflicts
erDiagram
USERS ||--o{ APPOINTMENTS : creates
PATIENTS ||--o{ APPOINTMENTS : "scheduled for"
APPOINTMENTS ||--o| VISITS : "converts to"
USERS ||--o{ USER_WORKING_HOURS : "has availability"
USERS ||--o{ TIME_BLOCKS : "has blocked time"
APPOINTMENTS {
string appointment_id PK
string patient_id FK
string veterinarian_id FK
string scheduled_date "ISO date"
string scheduled_time "HH:MM"
int duration_minutes "default 30"
string appointment_type "consultation|surgery|etc"
string status "scheduled|confirmed|completed"
string visit_id FK "nullable"
bool is_recurring
string recurrence_pattern
json additional_staff "array of user_ids"
}
USER_WORKING_HOURS {
string id PK
string user_id FK
int day_of_week "0-6 (0=Sunday)"
string start_time "HH:MM"
string end_time "HH:MM"
bool is_available
string break_start_time "optional"
string break_end_time "optional"
}
TIME_BLOCKS {
string time_block_id PK
string user_id FK
string title
string start_date "ISO"
string end_date "ISO"
string start_time "HH:MM"
string end_time "HH:MM"
string block_type "busy|available|break|meeting"
bool is_recurring
}
#[tauri::command]
pub async fn check_comprehensive_staff_conflicts(
db: State<'_, Database>,
veterinarian_id: String,
date: String, // "2025-09-04"
start_time: String, // "14:30"
duration_minutes: i32, // 30
) -> Result<ConflictResult, String> {
// Check 1: Existing appointments
let existing_appointments = sqlx::query!(
"SELECT * FROM appointments
WHERE veterinarian_id = ?
AND scheduled_date = ?
AND status NOT IN ('cancelled', 'completed')",
veterinarian_id, date
).fetch_all(&db.pool).await?;
// Check 2: Working hours
let working_hours = sqlx::query!(
"SELECT * FROM user_working_hours
WHERE user_id = ?
AND day_of_week = ?
AND is_available = true",
veterinarian_id,
get_day_of_week(&date)?
).fetch_all(&db.pool).await?;
// Check 3: Time blocks (meetings, breaks, etc.)
let time_blocks = sqlx::query!(
"SELECT * FROM time_blocks
WHERE user_id = ?
AND start_date <= ?
AND end_date >= ?
AND block_type = 'busy'",
veterinarian_id, date, date
).fetch_all(&db.pool).await?;
// Complex overlap detection logic
let conflicts = detect_time_overlaps(
existing_appointments,
working_hours,
time_blocks,
&start_time,
duration_minutes
)?;
Ok(ConflictResult {
has_conflicts: !conflicts.is_empty(),
conflicts,
suggested_times: suggest_alternative_times(/* ... */)?,
})
}

Responsive calendar system:

  • Mobile: Day view with swipe navigation
  • Desktop: Week view with drag-and-drop
  • Analytics: Month view with statistics
  • Search: Global appointment search

View components:

  • DayView.tsx - Detailed hourly schedule
  • WeekView.tsx - 7-day overview with staff columns
  • MonthView.tsx - Monthly calendar with indicators
  • MiniCalendar.tsx - Date picker component