7077fff3b3
- Convert all remaining static \$modelLabel/\$pluralModelLabel to getter methods with __() (AppointmentResource, WorkOrderResource, MasterResource, ServiceTemplateResource, LaborResource, and 33 others) - Safe blade wrap v2 (37 files, 201 wraps): fixed the regex to not match `->`, `=>`, `!<` — previous aggressive pass broke @if directives - Bulk-translate lowercase model-label keys (programări, fișe lucru, tehnicieni, șabloane servicii, norme-ore, etc.) — these were the RO strings appearing in page titles/breadcrumbs - Add human RU/EN for mechanic-kpi (Mecanic/Lucrări/Norma ore/etc.) and calendar-board words Tests 306 green. Blade compiles cleanly. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
229 lines
12 KiB
PHP
229 lines
12 KiB
PHP
<x-filament-panels::page>
|
|
<style>
|
|
.wiz { max-width: 1000px; margin: 0 auto; }
|
|
.wiz-steps { display: flex; gap: 8px; margin-bottom: 24px; align-items: center; }
|
|
.wiz-step { display: flex; align-items: center; gap: 8px; padding: 6px 14px; border-radius: 20px; font-size: 13px; font-weight: 500; color: #94a3b8; }
|
|
.wiz-step.active { background: #ebf5ff; color: #1d4ed8; }
|
|
.wiz-step.done { background: #dcfce7; color: #15803d; }
|
|
.wiz-step-sep { width: 24px; height: 2px; background: #e2e8f0; }
|
|
.wiz-card { background: white; border: 1px solid #e2e8f0; border-radius: 12px; padding: 24px; }
|
|
.dark .wiz-card { background: #1f2937; border-color: #374151; }
|
|
.wiz-field { margin-bottom: 16px; }
|
|
.wiz-field label { display: block; font-size: 11px; font-weight: 600; color: #718096; text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 6px; }
|
|
.wiz-field input, .wiz-field select { width: 100%; padding: 10px 12px; border: 1px solid #cbd5e1; border-radius: 6px; font-size: 14px; }
|
|
.dark .wiz-field input, .dark .wiz-field select { background: #111827; color: #f1f5f9; border-color: #374151; }
|
|
.wiz-grid { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 12px; }
|
|
.wiz-actions { display: flex; gap: 8px; justify-content: flex-end; margin-top: 24px; }
|
|
.wiz-btn { padding: 8px 16px; border-radius: 6px; font-weight: 500; cursor: pointer; border: 1px solid #cbd5e1; background: white; color: #1a202c; }
|
|
.wiz-btn:hover { background: #f7fafc; }
|
|
.wiz-btn-primary { background: #3b82f6; color: white; border-color: #3b82f6; }
|
|
.wiz-btn-primary:hover { background: #2563eb; }
|
|
.wiz-preview-table { width: 100%; border-collapse: collapse; font-size: 13px; }
|
|
.wiz-preview-table th, .wiz-preview-table td { padding: 6px 8px; border: 1px solid #e2e8f0; text-align: left; }
|
|
.wiz-preview-table th { background: #f7fafc; font-weight: 600; font-size: 11px; color: #4a5568; }
|
|
.dark .wiz-preview-table th { background: #111827; color: #cbd5e1; }
|
|
.wiz-badge { padding: 2px 8px; border-radius: 4px; font-size: 11px; font-weight: 500; }
|
|
.wiz-badge.found { background: #dcfce7; color: #15803d; }
|
|
.wiz-badge.new { background: #fef3c7; color: #92400e; }
|
|
.wiz-badge.no_article { background: #fee2e2; color: #991b1b; }
|
|
.wiz-summary { display: flex; gap: 16px; margin-bottom: 16px; padding: 12px; background: #f7fafc; border-radius: 6px; }
|
|
.dark .wiz-summary { background: #111827; }
|
|
.wiz-summary-item { font-size: 13px; }
|
|
.wiz-summary-item strong { font-size: 20px; display: block; font-weight: 700; }
|
|
.wiz-upload { border: 2px dashed #cbd5e1; border-radius: 8px; padding: 32px; text-align: center; cursor: pointer; }
|
|
.wiz-upload:hover { border-color: #3b82f6; background: #ebf5ff; }
|
|
.dark .wiz-upload:hover { background: #1e293b; }
|
|
</style>
|
|
|
|
<div class="wiz">
|
|
<div class="wiz-steps">
|
|
<div class="wiz-step {{ $step >= 1 ? 'active' : '' }} {{ $step > 1 ? 'done' : '' }}">1. Upload</div>
|
|
<div class="wiz-step-sep"></div>
|
|
<div class="wiz-step {{ $step == 2 ? 'active' : '' }} {{ $step > 2 ? 'done' : '' }}">2. Mapare coloane</div>
|
|
<div class="wiz-step-sep"></div>
|
|
<div class="wiz-step {{ $step == 3 ? 'active' : '' }} {{ $step > 3 ? 'done' : '' }}">3. Previzualizare</div>
|
|
<div class="wiz-step-sep"></div>
|
|
<div class="wiz-step {{ $step == 4 ? 'active' : '' }}">4. Confirmare</div>
|
|
</div>
|
|
|
|
<div class="wiz-card">
|
|
{{-- STEP 1: Upload --}}
|
|
@if ($step === 1)
|
|
<h2 style="font-size:18px;font-weight:600;margin-bottom:16px;">{{ __('Pasul 1: Încarcă fișierul') }}</h2>
|
|
|
|
<div class="wiz-field">
|
|
<label>{{ __('Furnizor *') }}</label>
|
|
<select wire:model="supplierId">
|
|
<option value="">{{ __('— alege —') }}</option>
|
|
@foreach ($this->getSupplierOptions() as $id => $name)
|
|
<option value="{{ $id }}">{{ $name }}</option>
|
|
@endforeach
|
|
</select>
|
|
</div>
|
|
|
|
<div class="wiz-field">
|
|
<label>{{ __('Fișier Excel (.xlsx) sau CSV *') }}</label>
|
|
<input type="file" wire:model="upload" accept=".xlsx,.xls,.csv" />
|
|
@if ($upload)
|
|
<div style="margin-top:6px;font-size:12px;color:#4a5568;">Selectat: {{ $upload->getClientOriginalName() }}</div>
|
|
@endif
|
|
</div>
|
|
|
|
<div class="wiz-actions">
|
|
<button class="wiz-btn wiz-btn-primary" wire:click="goToStep2" wire:loading.attr="disabled">{{ __('Următorul →') }}</button>
|
|
</div>
|
|
@endif
|
|
|
|
{{-- STEP 2: Mapping --}}
|
|
@if ($step === 2)
|
|
<h2 style="font-size:18px;font-weight:600;margin-bottom:16px;">{{ __('Pasul 2: Mapează coloanele') }}</h2>
|
|
<p style="color:#4a5568;font-size:13px;margin-bottom:16px;">{{ __('Spune-i sistemului ce reprezintă fiecare coloană din fișier. Vom salva configurația pentru imporțările viitoare.') }}</p>
|
|
|
|
<div style="overflow-x:auto;margin-bottom:20px;">
|
|
<table class="wiz-preview-table">
|
|
<thead>
|
|
<tr>
|
|
@foreach ($headersPreview['columns'] as $col)
|
|
<th>{{ $col }}</th>
|
|
@endforeach
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
@foreach ($headersPreview['rows'] as $row)
|
|
<tr>
|
|
@foreach ($headersPreview['columns'] as $col)
|
|
<td>{{ $row[$col] ?? '' }}</td>
|
|
@endforeach
|
|
</tr>
|
|
@endforeach
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
<div class="wiz-grid">
|
|
<div class="wiz-field">
|
|
<label>{{ __('Coloana Articol *') }}</label>
|
|
<select wire:model="mapping.article_col">
|
|
<option value="">—</option>
|
|
@foreach ($headersPreview['columns'] as $c)<option value="{{ $c }}">{{ $c }}</option>@endforeach
|
|
</select>
|
|
</div>
|
|
<div class="wiz-field">
|
|
<label>{{ __('Coloana Denumire *') }}</label>
|
|
<select wire:model="mapping.name_col">
|
|
<option value="">—</option>
|
|
@foreach ($headersPreview['columns'] as $c)<option value="{{ $c }}">{{ $c }}</option>@endforeach
|
|
</select>
|
|
</div>
|
|
<div class="wiz-field">
|
|
<label>{{ __('Coloana Cantitate *') }}</label>
|
|
<select wire:model="mapping.qty_col">
|
|
<option value="">—</option>
|
|
@foreach ($headersPreview['columns'] as $c)<option value="{{ $c }}">{{ $c }}</option>@endforeach
|
|
</select>
|
|
</div>
|
|
<div class="wiz-field">
|
|
<label>{{ __('Coloana Preț *') }}</label>
|
|
<select wire:model="mapping.price_col">
|
|
<option value="">—</option>
|
|
@foreach ($headersPreview['columns'] as $c)<option value="{{ $c }}">{{ $c }}</option>@endforeach
|
|
</select>
|
|
</div>
|
|
<div class="wiz-field">
|
|
<label>{{ __('Coloana Brand (opțional)') }}</label>
|
|
<select wire:model="mapping.brand_col">
|
|
<option value="">—</option>
|
|
@foreach ($headersPreview['columns'] as $c)<option value="{{ $c }}">{{ $c }}</option>@endforeach
|
|
</select>
|
|
</div>
|
|
<div class="wiz-field">
|
|
<label>{{ __('De la rândul (sărim header) *') }}</label>
|
|
<input type="number" min="1" wire:model="mapping.header_row" />
|
|
</div>
|
|
</div>
|
|
|
|
<div class="wiz-field">
|
|
<label style="display:flex;align-items:center;gap:8px;text-transform:none;">
|
|
<input type="checkbox" wire:model="rememberMapping" />
|
|
<span>{{ __('Salvează configurația pentru acest furnizor') }}</span>
|
|
</label>
|
|
</div>
|
|
|
|
<div class="wiz-actions">
|
|
<button class="wiz-btn" wire:click="$set('step', 1)">{{ __('← Înapoi') }}</button>
|
|
<button class="wiz-btn wiz-btn-primary" wire:click="goToStep3">{{ __('Previzualizare →') }}</button>
|
|
</div>
|
|
@endif
|
|
|
|
{{-- STEP 3: Preview --}}
|
|
@if ($step === 3)
|
|
<h2 style="font-size:18px;font-weight:600;margin-bottom:16px;">Pasul 3: Previzualizare — {{ $previewSummary['total'] }} poziții</h2>
|
|
|
|
<div class="wiz-summary">
|
|
<div class="wiz-summary-item">
|
|
<strong style="color:#15803d;">{{ $previewSummary['found'] }}</strong>
|
|
{{ __('Găsite (cu articol existent)') }}
|
|
</div>
|
|
<div class="wiz-summary-item">
|
|
<strong style="color:#92400e;">{{ $previewSummary['new'] }}</strong>
|
|
Articole noi (se vor crea)
|
|
</div>
|
|
<div class="wiz-summary-item">
|
|
<strong style="color:#991b1b;">{{ $previewSummary['no_article'] }}</strong>
|
|
{{ __('Fără articol (manual)') }}
|
|
</div>
|
|
</div>
|
|
|
|
<div style="max-height:400px;overflow-y:auto;margin-bottom:16px;">
|
|
<table class="wiz-preview-table">
|
|
<thead>
|
|
<tr>
|
|
<th>{{ __('Articol') }}</th><th>{{ __('Denumire') }}</th><th>{{ __('Brand') }}</th><th>{{ __('Cant.') }}</th><th>{{ __('Preț') }}</th><th>{{ __('Status') }}</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
@foreach ($previewRows as $row)
|
|
<tr>
|
|
<td style="font-family:monospace;">{{ $row['article'] }}</td>
|
|
<td>{{ $row['name'] }}</td>
|
|
<td>{{ $row['brand'] }}</td>
|
|
<td>{{ rtrim(rtrim(number_format($row['qty'], 2), '0'), '.') }}</td>
|
|
<td>{{ number_format($row['price'], 2) }}</td>
|
|
<td><span class="wiz-badge {{ $row['status'] }}">{{ ['found' => '✅ Găsit', 'new' => '⚠️ Nou', 'no_article' => '❓ Nu găsit'][$row['status']] ?? $row['status'] }}</span></td>
|
|
</tr>
|
|
@endforeach
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
<div class="wiz-field">
|
|
<label style="display:flex;align-items:center;gap:8px;text-transform:none;">
|
|
<input type="checkbox" wire:model="createNew" />
|
|
<span>{{ __('Creează automat articolele noi în nomenclatură') }}</span>
|
|
</label>
|
|
</div>
|
|
|
|
<div class="wiz-actions">
|
|
<button class="wiz-btn" wire:click="$set('step', 2)">{{ __('← Înapoi') }}</button>
|
|
<button class="wiz-btn wiz-btn-primary" wire:click="confirmImport">{{ __('Confirmă și importă') }}</button>
|
|
</div>
|
|
@endif
|
|
|
|
{{-- STEP 4: Done --}}
|
|
@if ($step === 4)
|
|
<div style="text-align:center;padding:32px;">
|
|
<div style="font-size:48px;margin-bottom:16px;">✅</div>
|
|
<h2 style="font-size:20px;font-weight:600;margin-bottom:8px;">{{ __('Import finalizat cu succes') }}</h2>
|
|
<p style="color:#4a5568;">{{ $previewSummary['total'] }} poziții importate în Purchase nouă</p>
|
|
<div style="margin-top:24px;">
|
|
@if (session('purchase_id'))
|
|
<a class="wiz-btn wiz-btn-primary" href="{{ route('filament.tenant.resources.purchases.edit', ['record' => session('purchase_id')]) }}">{{ __('↗ Deschide Purchase') }}</a>
|
|
@endif
|
|
<button class="wiz-btn" wire:click="reset_">{{ __('Import nou') }}</button>
|
|
</div>
|
|
</div>
|
|
@endif
|
|
</div>
|
|
</div>
|
|
</x-filament-panels::page>
|