Commit Graph

20 Commits

Author SHA1 Message Date
Vasyka c413004930 Stage 15 — PWA complete: install prompt + Web Push notifications
Dependency:
- minishlink/web-push v10 (VAPID JWT + aes128gcm payload encryption)
- Dockerfile: add curl, mbstring, gmp extensions (web-push needs ext-curl)

VAPID:
- config/webpush.php from env; `php artisan push:vapid` generates keypair
- Shared platform keypair; .env.example has empty placeholders

Schema:
- push_subscriptions (user/company, endpoint unique, p256dh, auth, encoding)

WebPushService:
- send / sendToUser / dispatch via WebPush::flush
- Auto-prunes subscriptions reported expired (404/410)

Subscribe flow:
- POST /push/subscribe + /push/unsubscribe (auth, tenant)
- Tenant panel JS subscribes after SW registration with VAPID public key

Service worker (/sw.js):
- Cache v2, push listener → showNotification, notificationclick → focus/open

Install prompt:
- Floating "Instalează aplicația" button wired to beforeinstallprompt

Staff push:
- WorkOrder master_id change → push to assigned mechanic
- Settings "Test notificare push" action

Tests (6 new):
- subscribe stores + upserts; requires auth (401); validation (422);
  service configured; sendToUser with no subs returns zero

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 05:11:18 +00:00
Vasyka e48ef1b755 Stage 7+14 — Mechanic Board + Scan Center
Mechanic Workflow (Stage 7):
- /app/mechanic Filament page filtered to master_id = auth user
- Kanban 4 columns (in_work / awaiting_parts / ready / recent), each card
  shows WO#, plate, client, complaint summary, photo presence
- 2 KPI tiles (active now / closed today)
- Mobile-responsive grid (auto-fit, minmax 260px)

WarehouseService:
- issueNow(WorkOrderPart) — consume reservations immediately scoped to one
  line, without closing the WO (mechanic physically takes part now)
- returnPart(WorkOrderPart, qty?, notes?) — refund to stock as new batch
  at original buy_price, writes `return` event, capped at consumed total

WO PartsRelationManager:
- "Eliberează" action — visible when active reservation exists
- "Restituire" action — visible when consumed reservation exists, with qty
  modal + notes

Scan Center (Stage 14):
- PartResource "QR" action — per-part SVG QR with payload PART:<article|id>
- BulkAction "Tipărește etichete QR" → /parts/labels?ids=N,M (HTML A4 sheet,
  3-col grid, print CSS hides toolbar)
- /app/scan Filament page using html5-qrcode 2.3.8 (CDN), auto-picks back
  camera, decodes → Livewire dispatches scanner-decoded → resolveAndRedirect
- Lookup matches PART:N prefix, parts.article, parts.barcode, or numeric id
- Manual input fallback for browsers without camera

Tests (6 new):
- WarehouseIssueReturnTest (3): issueNow consumes immediately; returnPart
  creates positive batch + return event; over-return is capped
- ScannerLookupTest (3): PART: prefix lookup, raw barcode lookup, unknown miss

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 21:39:39 +00:00
Vasyka 1ff888131f Stage 16 — AI Layer: VIN decoder + diagnostic / parts / price helpers
VinDecoder (deterministic, no API):
- ISO 3779/3780 parsing: WMI manufacturer (~60 brands), year (cyclical with
  post-2010 disambiguation via position 7), region, plant, NA checksum
- Strip non-VIN chars, accept dashes/spaces, reject I/O/Q per spec

AiAssistantService:
- Refactored provider HTTP into postClaude/postOpenAI/postGemini so both
  chat history and one-shot calls share the same transport
- singleShot(system, userPrompt, provider?) for fire-and-forget calls
- 4 specialized helpers with tight prompts:
  - suggestDiagnosis(WO) — diagnostician based on complaint + VIN info
  - suggestParts(WO, task) — OEM parts list for an operation
  - suggestPrice(Part) — markup recommendation with justification
  - vinRecommendations(vin, mileage) — scheduled maintenance from decoded VIN
- monthlyUsage() — token spend MTD by provider

Filament:
- VehicleResource: "Decode VIN" + "AI: recomandări" actions
- WorkOrderResource Edit: "AI: sugerează diagnostic" header action
- PartResource: "AI: preț recomandat" action
- Shared views: filament.tenant.ai-reply, filament.tenant.vin-decode
- AiAssistant page shows monthly token usage banner

Tests (13 new):
- 8 VinDecoder unit tests with real VIN samples (Honda 2003, VW 1999, Audi
  2014, Dacia, unknown WMI, lowercase/dashes, forbidden chars)
- 5 AiHelpers feature tests with Http::fake covering all providers + no-key
  fallback + token usage aggregation

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 20:24:09 +00:00
Vasyka 85ef2f6e00 Stage 13 — Notifications: Telegram bot + multi-channel + service reminders
Schema:
- clients.telegram_chat_id (linked via /start contact-share)
- clients.notify_prefs (per-client channel order override)
- service_reminders_sent (dedup ledger for the daily cron)

Telegram (per tenant):
- TelegramService (sendMessage, getMe, setWebhook with auto-generated secret)
- Bot token stored in companies.settings.telegram.bot_token
- Webhook /telegram/webhook/{slug} validates X-Telegram-Bot-Api-Secret-Token,
  matches client by last 9 digits of phone, persists chat_id, replies confirm
- /start prompts share-contact; /stop unlinks chat_id

NotificationDispatcher refactor:
- Multi-channel: telegram first if chat_id + bot configured, then email
- Backwards-compat with legacy boolean notify.{type} flags
- 4 HTML-formatted Telegram messages (wo_ready with tracking link, payment,
  appointment, reminder)

Service reminders:
- `reminders:send` artisan command with --slug / --dry-run
- Policy: vehicles whose last closed WO is older than reminder.after_days
  (default 365). Skips if sent within reminder.cooldown_days (default 30).
- Schedule daily 09:00

Filament UI:
- Settings page: Telegram bot token field + "Test bot" + "Set webhook" actions
- Settings page: reminder_after_days + reminder_cooldown_days inputs
- ClientResource: telegram_chat_id readonly badge

Tests (6 new, all pass):
- webhook links client via shared contact
- webhook rejects wrong secret → 401
- dispatcher uses telegram when chat_id present (Http::fake)
- dispatcher falls back to email otherwise
- dispatcher returns false when no channel available
- reminder cron respects 30-day cooldown

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 20:14:17 +00:00
Vasyka 59409e1b11 fix: calendar — register createForm via getForms() + remove broken FullCalendar CSS link
- Filament v5 multi-form pages need protected getForms() returning form names
- FullCalendar v6 bundles CSS in JS — separate <link> 404'd silently
- Added Russian locale alongside Romanian for i18n switcher
2026-05-08 11:27:04 +00:00
Vasyka 8f324b7b8e Currency dropdown + logo on login/topbar via brandLogo closure
- Onboarding + Settings: Forms\Components\Select for currency
  (MDL/EUR/USD/RON/UAH/RUB) instead of TextInput
- TenantPanel: brandName/brandLogo/favicon as closures resolving from
  tenant. Logo now visible on login page, topbar, sidebar (Filament
  v5 brandLogo handles all 3 spots automatically)
- Favicon falls back to logo if favicon not separately uploaded
- Removed manual SIDEBAR_LOGO_BEFORE injection (replaced by brandLogo)
- Removed manual <link rel=icon> in HEAD_END (replaced by ->favicon)
2026-05-08 07:50:12 +00:00
Vasyka 0399262514 Deploy 3: Onboarding wizard + empty states + docs operationale
- 3-step onboarding wizard at /app/onboarding (auto-redirected via
  RequireOnboarding middleware on first login per tenant)
- Empty states with icon + heading + description on Client, Vehicle,
  WorkOrder, Lead, Part lists
- Docs: operations/{api,i18n,2fa,monitoring}.md, stack/reverb.md
- Updated 00-index.md and journal.md with status of all 15 items
2026-05-07 20:16:03 +00:00
Vasyka eaa05d68c1 Deploy 2: 2FA (App + Email) + REST API + CSV import-export + auto backup
- Filament v5 multiFactorAuthentication enabled on both panels (App + Email)
- HasAppAuthentication + HasEmailAuthentication on User and SuperAdmin
- Migration: app_authentication_secret + recovery_codes + email_authentication_at
- Sanctum REST API: /api/v1/login, /me, clients, vehicles, work-orders
- EnsureTokenMatchesTenant middleware blocks cross-tenant token usage
- CsvImportExport service: clients + vehicles bulk via plain CSV
- Import/Export buttons on Client + Vehicle list pages
- ApiTokens page in tenant panel (generate/revoke + last-used)
- BackupAllTenantsCommand + scheduler (daily 03:00, retain 14 days)
- Background scheduler in entrypoint.sh
2026-05-07 19:25:27 +00:00
Vasyka 6c72fc7db1 Batch 3: Integrări placeholder + Backup tenant
═══ Integrări (Marketing → Integrări) ═══
- /app/integrations Page cu 6 carduri (Telegram/WhatsApp/Google Ads/FB/SMS/Webhook)
- Toggle on/off per integrare; salvare în settings.integrations JSON
- Câmpuri specifice per integrare (token/key/id/secret)
- Banner explicativ: 'placeholder UI — implementare separată'

═══ Backup tenant ═══
- TenantBackupService::export($company) → ZIP cu:
  • data/ (1 JSON per tabel: clients/vehicles/leads/deals/work_orders cu sub-relații/...)
  • media/ (logo + favicon)
  • manifest.json (metadata + counts)
- /app/backup Page cu buton 'Descarcă backup acum'
- Streaming download cu deleteFileAfterSend
- Util pentru: backup local, migrare, audit, GDPR right-to-erasure

Total tenant routes: 104.
Toate cele ~26 module din prototip implementate (sau echivalent funcțional).
2026-05-07 17:36:00 +00:00
Vasyka 4b3201ca1c Batch 2: Workload heatmap + Site PSauto + VIN search
═══ Workload heatmap (Încărcare STO) ═══
- /app/workload custom Page (group Analiză)
- Săptămână (Lu-Du) × posturi → matrice ore programate
- Heatmap colorat: verde→galben→roșu pe ratio capacity (10h/zi)
- Navigare săpt anterior/curent/următor
- Programări fără pod → row '— fără pod —' separat

═══ Site PSauto (landing public) ═══
- / pe tenant subdomain → resources/views/site/landing.blade.php
- Hero cu logo + nume + slogan; gradient theme color
- Servicii (din settings.services) — grid card-uri
- Locație/contact + program lucru standardizat
- Mărci suportate (din settings.cars)
- CTA: phone + email
- Footer cu tenant name + powered by AutoCRM

═══ VIN search ═══
- VinDecoder service: WMI hardcoded (24 producători EU/Asia/USA)
  + year codes (2001-2026) — pure offline, fără API extern
- /app/vin-search Page (group Depozit) cu:
  • Input VIN cu uppercase + monospace
  • Decode → producător/țară/an/serial
  • Match VIN-uri din baza Vehicles
  • Search piese din catalog (live debounce 300ms)
- Rezultatele linkează la editor Vehicle/Part

Total tenant routes: 102.
2026-05-07 17:16:09 +00:00
Vasyka 67da97178d Batch 1: Procentaj + Finanțe consolidat + Recomandări
═══ Procentaj (markup rules) ═══
- markup_rules table cu type (category/brand/range), key, range_from/to, markup_pct, priority
- MarkupRule::bestForPart($part) — rezolvare brand → category → range → 30% default
- MarkupRule::applyToPart($part) — recalc sell_price = buy_price × (1 + pct/100)
- Filament resource sub Depozit cu form dinamic per tip
- Action 'Aplică toate regulile la stoc' — recalc tot catalogul (chunk 100)

═══ Finanțe consolidat ═══
- Custom Page /app/finance cu 4 tab-uri:
  • Overview: încasări/cheltuieli/profit/datorii (4 cards)
  • Cashflow: bar chart per zi (verde=in, roșu=out) + Net total
  • P&L: venituri (manopere + piese) vs costuri (cost piese + cheltuieli pe categorie)
    + profit net + marjă %
  • Balance: active (cash net + datorii + stoc), all-time totals
- Period filter: lună / luna trecută / an / 30 zile

═══ Recomandări ═══
- Custom Page /app/recommendations 4 sectiuni:
  • Clienți pierduți (>6 luni fără WO + are istoric)
  • Mașini km>100k (sugestie revizie)
  • Fișe neplătite (rest > 0)
  • VIP fără contact >30 zile

Total tenant routes: 100.
2026-05-07 15:30:04 +00:00
Vasyka 976c0f03e3 AI Assistant — multi-provider chat (Claude / GPT / Gemini)
Schema:
- ai_chats: company_id, user_id, title, provider; index pe activitate
- ai_messages: role (system/user/assistant), content, meta JSON (tokens, latency, model)

Service AiAssistantService (multi-provider):
- ask($chat, $message): persistă mesajul user, build system prompt cu context
  tenant (statistici clienți/mașini/cereri/datorii), apelează API-ul providerului,
  persistă răspunsul cu meta (tokens, latency)
- callClaude: api.anthropic.com/v1/messages cu claude-sonnet-4-5
- callOpenAI: api.openai.com/v1/chat/completions cu gpt-4o-mini
- callGemini: generativelanguage.googleapis.com cu gemini-1.5-flash
- Try/catch pe toate; eroare devine mesaj asistent fără să crape

System prompt include:
- Numele și orașul companiei
- Statistici curente (clienți, mașini, cereri noi, fișe active, datorii)
- Limita stricta: NU inventează date

Custom Filament Page /app/ai-assistant (group Analiză):
- Sidebar stâng: listă conversații (last 20), buton 'Nouă' + delete cu confirm
- Main: bubble chat (user dreapta albastru, asistent stânga gri)
- Meta jos pe răspuns: provider · latency · tokens
- Empty state friendly cu instrucțiuni configurare
- Loading indicator (3 dots animate) când AI răspunde
- Auto-scroll la mesaj nou
- Enter trimite, Shift+Enter newline
- Auto-titlu chat din primul mesaj user (60 chars)

Settings page extins cu secțiune 'Asistent AI':
- Provider implicit (claude/gpt/gemini)
- 3 chei API (password fields, revealable)
- Key-urile salvate în companies.settings.ai (per tenant, izolat)
2026-05-07 14:50:56 +00:00
Vasyka 09fd0bada2 Faza 2 (din continuare): Email notifications
4 Mailables auto-trigger pe model events:
- WorkOrderReadyMail: la WO.status → 'ready', către client.email
  • Atașat PDF fișa lucru (via WorkOrderPdfService)
  • Total/achitat/rest, recomandări (warning box)
- PaymentReceivedMail: la Payment::created, confirmare cu sumă/metodă/ref
- AppointmentConfirmedMail: la Appointment::created status='scheduled'
- ServiceReminderMail: dispatch manual (vehicle, type=itp/oil/general, note)

Layout email branded (resources/views/emails/layout.blade.php):
- Header cu logo tenant + theme_color border-bottom
- Footer cu telefon/email/disclaimer
- Stiluri inline (compatibil tot mail client)

Settings page extins cu 4 toggle:
- 'Mașina e gata de ridicat'
- 'Confirmare plată primită'
- 'Programare confirmată'
- 'Reminder ITP / revizie'
Salvate în companies.settings.notify (JSON), default true.

NotificationDispatcher service centralizat:
- Verifică isEnabled() pe settings.notify[$key]
- Skip dacă client n-are email
- Try/catch + Log::warning pe eșec (nu crapă request-ul)

Mailables folosesc UsesTenantBranding trait pentru context unitar.
Test prin Mailpit: https://mailpit.service.mir.md (capturează toate).
2026-05-07 13:20:19 +00:00
Vasyka bfe58ed286 Faza 1 (din lista de continuare): Calendar vizual cu FullCalendar 6
- Custom Filament Page CalendarBoard la /app/calendar-board (group CRM)
- FullCalendar 6.1.15 din CDN + locale RO
- View-uri: zi (timeGridDay), săptămână (timeGridWeek default), lună
- Programări colorate per maistru (din User.color)
- Live event loading: Livewire $wire.getEvents(start, end)
- Drag-drop reschedule: eventDrop → $wire.moveEvent(id, start, end)
- Resize event (extinde durată): eventResize
- Click slot gol → quick-create modal cu form Filament populat cu data/timpul
  - Câmpuri: title, time_start/end, client (live), vehicle (filtrat după client),
    master, post, notes
- Click event → confirm + delete
- Toolbar: prev/next/today + month/week/day switch
- 07:00–21:00 grid cu sloturi 30 min, today indicator live
- Modal stilizat (CSS scoped) cu close button + ESC

Total tenant routes: 93.
2026-05-07 13:10:27 +00:00
Vasyka f1d196f018 Faza 7: White-label per tenant — logo + theme color dinamic
- spatie/laravel-medialibrary instalat (migration media table)
- filament/spatie-laravel-media-library-plugin
- Company implements HasMedia + InteractsWithMedia
  - collections: 'logo' + 'favicon' (singleFile)
  - getLogoUrl() / getFaviconUrl() helpers
- Settings page extins: secțiune Logo & favicon cu FileUpload
  - On save: clear collection + addMedia from temp upload + cleanup tmp file
- TenantPanelProvider render hooks:
  - HEAD_END: theme-color meta + favicon + CSS vars override
    (--primary-50 → --primary-950 generate din hex theme_color)
  - SIDEBAR_LOGO_BEFORE: afișare logo upload-uit, max-height 56px

Cum funcționează:
- Tenant uploadează logo în Settings
- La fiecare request, render hook injectează <style> cu CSS vars custom
- Filament respectă --primary-* → toate butoanele/badge-urile primesc culoarea brand
- Logo apare deasupra meniului (sidebar)
2026-05-07 12:51:19 +00:00
Vasyka 06696727dd Faza 6: Activity log + Kanban + Payroll + cleanup
══════ Activity log (Spatie) ══════
- spatie/laravel-activitylog v5 instalat
- Migration cu company_id pentru tenant scoping
- Trait Auditable (App\Models\Concerns\Auditable):
  - LogOptions cu logFillable + logOnlyDirty + dontSubmitEmptyLogs
  - tapActivity auto-fill company_id + causer
  - Descrieri RO (creat/modificat/șters/restaurat)
- Aplicat pe: Client, Vehicle, Lead, Deal, WorkOrder, Payment, Expense
- ActivityResource (group Admin → Jurnal activitate)
  - Listă read-only, scope pe tenant, filtre by description/today

══════ Kanban Work Orders ══════
- Custom Filament page la /app/kanban (group Service)
- 6 coloane (new → diagnosis → agreement → in_work → awaiting_parts → ready)
- Drag-drop nativ HTML5 cu wire:click moveCard()
- Cards arată: număr fișă, client, auto, plate, master, total
- Link 'Deschide' direct la editare WO

══════ Payroll (Salarii) ══════
Schema:
- employee_profiles: user_id, position, base_salary, works_pct, parts_pct
- payroll_runs: period (YYYY-MM), base, works_revenue/pct, parts_margin/pct,
  bonus, fines, advance, total auto-calculat
- payroll_adjustments: bonus/fine/advance per period

PayrollCalculator service:
- compute($userId, $period) — calculează auto:
  - Manopere finalizate de mecanic în luna respectivă (sum total)
  - Marja pieselor montate de el (sell-buy * qty)
  - Bonus + fines + advance from adjustments
  - Total = base + works% + parts% + bonus - fines - advance

Resources Filament (group Finanțe):
- EmployeeProfileResource: profil cu % comisioane
- PayrollRunResource: salarii cu action 'Calculează luna curentă' (toți userii)
  + per-row 'Recalculează'; Sum summary pe total
- PayrollAdjustmentResource: gestionare bonus/penalizări/avansuri

══════ Cleanup ══════
- Șterse toate /__debug, /__seed, /__try-login, /__force-login, /__whoami,
  /__coolify-check (security)
- Routes/web.php conține doar / redirect, /manifest.json, /sw.js

Total Filament tenant routes: 92.
2026-05-07 09:52:01 +00:00
Vasyka 8d82af2f54 Faza 3.5+3.6+4+5: Marketing, Reports, Provisioning, PWA
═══ Faza 3.5: Marketing ═══
Schema: msg_templates, marketing_channels, calls
Modele cu logică:
- MessageTemplate::render($context) — substituie {key} tokens
- MarketingChannel: roi/conversion_rate/cost_per_lead computed attrs
- Call: duration_formatted helper

Resources Filament (group Marketing):
- MessageTemplateResource: 5 canale (telegram/whatsapp/viber/sms/email)
- MarketingChannelResource: budget vs revenue cu ROI live calculat
- CallResource: in/out/missed cu filtre azi/missed

═══ Faza 3.6: Analytics ═══
Custom Filament Page Reports cu 6 rapoarte tab-uite:
- Finanțe: încasări/cheltuieli/profit/datorii + breakdown pe metodă/categorie
- Încărcare: fișe deschise/închise + breakdown pe status
- Mecanici: ore lucrate, manopere, venit per mecanic
- Manopere top: cele mai frecvente cu nr/ore/venit
- Piese: top vândute + low-stock
- Clienți: noi în perioadă + lead-uri pe sursă
Selector perioadă: azi / săptămâna / luna / luna trecută / anul

═══ Faza 4: Central provisioning ═══
- CoolifyClient service (Coolify v4 REST API wrapper)
- CompanyProvisioner: creează Company + admin user + roles + adaugă
  subdomeniul în Coolify FQDN + trigger redeploy automat
- CreateCompany page override → folosește provisioner, returnează
  notificare cu credentialele admin
- Form CompanyResource extins cu admin_name/email/password (vizibil doar create)
- Action 'Suspendă' / 'Activează' pe table cu confirmation

Env vars necesare în Coolify pentru provisioning auto:
  COOLIFY_API_URL=http://65.21.20.141:8000
  COOLIFY_API_TOKEN=<token>
  COOLIFY_APP_UUID=g13hlrpd5g44zxl5af3ktio2

═══ Faza 5: PWA + branding ═══
- Route /manifest.json dinamic per tenant (nume, theme color, icons)
- Route /sw.js — service worker minimal (cache shell + static)
- TenantPanelProvider renderHook HEAD_END — link manifest + theme-color
  + apple-mobile-web-app meta
- TenantPanelProvider renderHook BODY_END — registrare service worker

Seed extins:
- 5 template-uri mesaje (programare/auto-gata/reminder/ITP/felicitare)
- 5 canale marketing (Google Ads/FB/IG/Telegram/Recomandări)
- 2 apeluri demo

Total Filament tenant routes: 81.
2026-05-07 04:55:33 +00:00
Vasyka f0f9fdd555 Faza 3.4: Finanțe — Plăți + Cheltuieli + Cashflow
Schema:
- payments: client_id, work_order_id, user_id (operator), paid_at, amount,
  method (cash/card/transfer/mobile), reference, notes
- expenses: supplier_id, purchase_id, paid_at, category (salary/purchase/rent/
  utilities/advance/tax/fuel/tools/marketing/other), name, amount, method, ref

Logică auto:
- Payment::saved/deleted recalculează automat work_order.pay_status
  (unpaid → partial → paid) based on suma totală vs work_order.total
- WO model are noi metode: payments(), paidAmount(), balanceDue()

Filament resources (group Finanțe):
- PaymentResource: form cu legare opțională la WO + client; tabel cu
  Sum summary, filtre azi/luna_curentă/method
- ExpenseResource: 10 categorii preset, badge categ, total summary,
  filtru luna curentă
- PaymentsRelationManager pe WO: "Plăți" tab cu auto-fill client_id +
  user_id la creare

Widget FinanceOverview:
- Încasări (luna), Cheltuieli (luna), Profit (luna), Datorii clienți
- color coded: profit verde sau roșu, datorii galben/verde

Settings page fix (Filament v5):
- mount() folosește acum $this->form->fill([...]) în loc de $this->data direct
- Filament v5 cere fill explicit pentru a inițializa state-ul schemei

Seed:
- 1 plată parțială pe fișa BMW (200 din 750)
- 6 cheltuieli demo: 3 salarii, chirie, electricitate, achiziție piese

Total Filament tenant routes: 69.
2026-05-06 22:55:50 +00:00
Vasyka 721c57ff97 Filament v5: Forms\Components\Section → Schemas\Components\Section, Forms\Get → Schemas\Components\Utilities\Get
Layout components (Section, Grid, Tabs, etc.) au fost mutate din
Filament\Forms\Components în Filament\Schemas\Components.
Forms\Components păstrează doar field-urile (TextInput, Select, etc.).

Forms\Get s-a mutat în Schemas\Components\Utilities\Get.
2026-05-06 18:07:10 +00:00
Vasyka c9cb3560ef Faza 3.1: CRM core — Leads, Deals, Appointments, Settings, Widgets, Users
Spatie Permission cu teams (team_foreign_key=company_id, teams=true):
- migrations create_permission_tables (model_has_roles cu company_id scope)
- HasRoles trait pe User
- ResolveTenant middleware setează permissions team_id la tenant.id
- Seed: 7 roluri default per tenant (admin/manager/receptionist/mechanic/parts_manager/accountant/marketer)

Module noi:
- Leads (cereri): name, phone, car/model, source, UTM, status, budget, assigned_to,
  acțiune "Convertește" → creează automat Client + Deal
- Deals (pipeline): client/vehicle, stage (8 stage-uri), price, source, lost_reason
- Posts + Appointments: post_id (boxă), master_id, date+time_start+time_end, status, color
- UserResource (tenant): CRUD users cu role/status/locale; canViewAny doar pentru admin

Custom Filament page "Setări" (tenant):
- Brand & contact (display_name, city, phone, email)
- Localizare (limba RO/RU/EN, currency, theme color picker)
- Servicii & tarif (labor_rate)
- Liste configurabile (services, cars) — păstrate în companies.settings JSON

Widgets dashboard:
- Tenant: StatsOverview (Clienți, Mașini, Cereri noi, Deal-uri active, Programări azi)
- Central: PlatformStats (Companii total/active/trial, Expiră în 7 zile)

Seed extins demo PSauto:
- 3 posturi (Pod 1/2/3 cu culori)
- 2 lead-uri demo (Alex Grosu Telegram, Irina Cojocaru WhatsApp)
- 3 deal-uri demo (BMW done, Audi in_work, Porsche agree)
- 2 programări (azi + mâine)

Filament v5 fixes:
- $navigationGroup type → string|UnitEnum|null (parent stricter signature)
- Toate resources noi au tipurile corecte
2026-05-06 17:36:32 +00:00