Files
autocrm/resources/views/pdf/appointments.blade.php
T
Vasyka 80c3834263 feat: calendar enhancements — view modes, post CRUD, PDF, list
Closes 5 user-requested features in /app/calendar-board:

1. View mode switcher: Zi / Săpt / Lună / Custom / Listă
2. Editable post names + assignable default master per bay
3. Quick-add bay (+ Pod nou) from calendar toolbar — supports yard
   spaces without a lift ("Curte 1", "Atelier electric")
4. PDF export of programări for printing
5. Inline list view alongside the matrix view

== View modes ==
$viewMode: day | week | month | custom | list

- Day view: 1 column, just today (or navigated day). Shift moves day by day.
- Week view: current 7-column matrix (unchanged default).
- Month view: 30/31 columns shown smaller (70px each). Shift moves by month.
- Custom: 2 date pickers for arbitrary start..end range (max 31 days).
- List view: flat sortable table with Data/Ora/Subiect/Client/Telefon/
  Auto/Pod/Maistru/Status columns. Click row → opens detail panel.

getDays() computes the right day count + start anchor for each mode.
setViewMode() snaps weekStart to the right anchor (startOfMonth, today,
startOfWeek). shiftWeek delta semantics adapt: day mode shifts 1 day,
month mode shifts 1 month, others shift 7 days.

== Editable posts + default master ==
New PostResource (/app/posts) in Admin group: full CRUD with name,
color, hours_per_day, default_master_id, description, is_active,
sort_order. Gated by ADMIN_SETTINGS_EDIT.

Migration: posts.default_master_id FK → users (nullOnDelete).

Inline rename from calendar: click any post's row label opens a modal
with name field + default master dropdown. Saved values propagate
immediately to next appointment creation.

Auto-fill in new appointment: when creating an appointment via the "+"
cell button on a post row, master_id is pre-filled from
post.default_master_id (if not already set by groupBy='master' row).

== Quick-add bay ==
"+ Pod nou" button in toolbar opens a small modal (no full page nav):
name, color picker, hours/day, description. createPost() saves and
refreshes the row list. Designed for "yard space" use-cases — names
like "Curte 1" or "Atelier electric" are first-class, not workarounds.

== PDF export ==
"🖨 PDF programări" button calls exportPdf() which uses the existing
dompdf integration (already installed). Renders pdf/appointments.blade.php
grouped by day with table per day showing time/title/client+vehicle/
post/master/status. Romanian date headers ("Marți, 10 Iunie 2026").
streamDownload with filename programari_YYYY-MM-DD_YYYY-MM-DD.pdf.

== List view ==
getListAppointments() returns flat array of all appointments in the
visible period (date-range respects current viewMode), with full
client/vehicle/post/master joined. Status filter respected. Row click
opens the existing event detail panel.

== Tests ==
CalendarEnhancementsTest (8):
- viewMode='day' returns 1 day
- viewMode='month' returns 30 days for June 2026
- viewMode='custom' uses customStart..customEnd range
- quick-add post via Livewire createPost persists with all fields
- rename post updates name + default_master_id
- new appointment auto-fills master_id from post's default_master_id
- list view returns flat array with phone + post name joined
- exportPdf returns StreamedResponse with .pdf filename

Suite: 285 passed (802 assertions). Was 277.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-06 07:34:27 +00:00

66 lines
2.9 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Programări {{ $periodLabel }}</title>
<style>
@page { margin: 18mm 14mm; }
body { font-family: DejaVu Sans, sans-serif; font-size: 11px; color: #1a202c; }
h1 { font-size: 18px; margin: 0 0 6px; color: #1a202c; }
.sub { color: #718096; font-size: 11px; margin-bottom: 14px; }
h2 { font-size: 14px; margin: 16px 0 8px; padding: 4px 8px; background: #ebf5ff; border-left: 3px solid #3b82f6; }
table { width: 100%; border-collapse: collapse; }
th { text-align: left; background: #f7fafc; font-size: 10px; padding: 6px 8px; border-bottom: 2px solid #e2e8f0; }
td { padding: 6px 8px; border-bottom: 1px solid #edf2f7; vertical-align: top; }
.time { font-weight: 700; color: #2563eb; white-space: nowrap; width: 60px; }
.client-name { font-weight: 600; }
.meta { color: #718096; font-size: 10px; }
.footer { margin-top: 24px; padding-top: 8px; border-top: 1px solid #e2e8f0; font-size: 10px; color: #94a3b8; }
.empty { padding: 20px; text-align: center; color: #94a3b8; font-style: italic; }
</style>
</head>
<body>
<h1>Programări {{ $periodLabel }}</h1>
<div class="sub">Generat la {{ $generatedAt }} · {{ $appointments->flatten()->count() }} programări total</div>
@forelse ($appointments as $date => $dayAppts)
@php $dateLabel = \Carbon\Carbon::parse($date)->locale('ro')->isoFormat('dddd, D MMMM YYYY'); @endphp
<h2>{{ ucfirst($dateLabel) }} · {{ $dayAppts->count() }} programări</h2>
<table>
<thead>
<tr>
<th style="width:60px;">Ora</th>
<th>Subiect</th>
<th>Client / Auto</th>
<th>Pod</th>
<th>Maistru</th>
<th style="width:60px;">Status</th>
</tr>
</thead>
<tbody>
@foreach ($dayAppts as $a)
<tr>
<td class="time">{{ substr($a->time_start ?? '', 0, 5) }}{{ substr($a->time_end ?? '', 0, 5) }}</td>
<td>{{ $a->title }}@if ($a->notes)<div class="meta">{{ $a->notes }}</div>@endif</td>
<td>
<div class="client-name">{{ $a->client?->name ?? '—' }}</div>
<div class="meta">
@if ($a->vehicle?->make){{ $a->vehicle?->make }} {{ $a->vehicle?->model }}@endif
@if ($a->vehicle?->plate) · {{ $a->vehicle->plate }}@endif
</div>
</td>
<td>{{ $a->post?->name ?? '—' }}</td>
<td>{{ $a->master?->name ?? '—' }}</td>
<td>{{ ucfirst($a->status) }}</td>
</tr>
@endforeach
</tbody>
</table>
@empty
<div class="empty">Nicio programare în această perioadă.</div>
@endforelse
<div class="footer">AutoCRM PSauto · psauto.service.mir.md</div>
</body>
</html>