Commit 2a44403d authored by Mahmoud Aglan's avatar Mahmoud Aglan

Fix broken features found in sweep

- CreateProductWizard: fix permission mismatch (products.create→inventory.create),
  add SKU uniqueness validation, redirect to list after success instead of dead-end
- ProductForm: set branch_id on new products
- ProductList: fix dynamic Tailwind classes (use full class strings for JIT)
- GroupList: fix dynamic Tailwind status badge classes
- POSTerminal: remove dead addProgram() method and unused imports
- CSS: add safelist comment for dynamic color classes used via Blade interpolation
Co-Authored-By: 's avatarClaude Opus 4.6 <noreply@anthropic.com>
parent fdd13245
...@@ -42,7 +42,7 @@ class CreateProductWizard extends Component ...@@ -42,7 +42,7 @@ class CreateProductWizard extends Component
public function mount(): void public function mount(): void
{ {
$this->authorize('products.create'); $this->authorize('inventory.create');
$this->categories = ProductCategory::active() $this->categories = ProductCategory::active()
->orderBy('name_ar') ->orderBy('name_ar')
->get(['id', 'name_ar', 'name']) ->get(['id', 'name_ar', 'name'])
...@@ -65,7 +65,7 @@ public function rulesForStep(): array ...@@ -65,7 +65,7 @@ public function rulesForStep(): array
1 => [ 1 => [
'nameAr' => 'required|string|max:255', 'nameAr' => 'required|string|max:255',
'name' => 'nullable|string|max:255', 'name' => 'nullable|string|max:255',
'sku' => 'nullable|string|max:50', 'sku' => 'nullable|string|max:50|unique:products,sku',
'barcode' => 'nullable|string|max:50', 'barcode' => 'nullable|string|max:50',
'categoryId' => 'nullable|exists:product_categories,id', 'categoryId' => 'nullable|exists:product_categories,id',
'type' => 'required|in:physical,digital,service', 'type' => 'required|in:physical,digital,service',
...@@ -143,8 +143,8 @@ public function confirm(): void ...@@ -143,8 +143,8 @@ public function confirm(): void
'created_by' => auth()->id(), 'created_by' => auth()->id(),
]); ]);
$this->completed = true;
session()->flash('success', 'تم إنشاء المنتج بنجاح'); session()->flash('success', 'تم إنشاء المنتج بنجاح');
$this->redirect(route('inventory.products'), navigate: true);
} catch (DomainException $e) { } catch (DomainException $e) {
session()->flash('error', $e->getMessage()); session()->flash('error', $e->getMessage());
} }
......
...@@ -8,9 +8,7 @@ ...@@ -8,9 +8,7 @@
use App\Domain\POS\Enums\POSItemType; use App\Domain\POS\Enums\POSItemType;
use App\Domain\POS\Enums\POSPaymentMethod; use App\Domain\POS\Enums\POSPaymentMethod;
use App\Domain\POS\Services\POSService; use App\Domain\POS\Services\POSService;
use App\Domain\Pricing\Services\PricingService;
use App\Domain\Shared\Exceptions\DomainException; use App\Domain\Shared\Exceptions\DomainException;
use App\Domain\Training\Models\TrainingProgram;
use Livewire\Attributes\Layout; use Livewire\Attributes\Layout;
use Livewire\Attributes\Title; use Livewire\Attributes\Title;
use Livewire\Component; use Livewire\Component;
...@@ -89,36 +87,6 @@ public function clearParticipant(): void ...@@ -89,36 +87,6 @@ public function clearParticipant(): void
$this->searchResults = []; $this->searchResults = [];
} }
public function addProgram(int $programId, PricingService $pricingService): void
{
$program = TrainingProgram::findOrFail($programId);
$participant = $this->participantId ? Participant::find($this->participantId) : null;
try {
$priceResult = $pricingService->calculate(
priceable: $program,
participant: $participant,
branchId: auth()->user()->branch_id ?? null,
couponCode: $this->couponCode ?: null,
);
$this->cart[] = [
'item_type' => POSItemType::Program->value,
'item_id' => $program->id,
'item_name_ar' => $program->name_ar,
'quantity' => 1,
'unit_price' => $priceResult->finalAmount,
'base_price' => $priceResult->baseAmount,
'discount_amount' => $priceResult->totalDiscount,
'line_total' => $priceResult->finalAmount,
'pricing_snapshot' => $priceResult->toArray(),
'metadata' => ['program_id' => $program->id],
];
} catch (DomainException $e) {
session()->flash('error', $e->getMessage());
}
}
public function addProduct(int $productId): void public function addProduct(int $productId): void
{ {
$product = Product::findOrFail($productId); $product = Product::findOrFail($productId);
......
@import 'tailwindcss'; @import 'tailwindcss';
/*
Dynamic status badge colors used via Blade interpolation — Tailwind scanner needs
these as complete strings to include them in the build output:
bg-red-100 bg-red-50 text-red-600 text-red-700 border-red-200
bg-green-100 bg-green-50 text-green-600 text-green-700 border-green-200
bg-blue-100 bg-blue-50 text-blue-600 text-blue-700 border-blue-200
bg-amber-100 bg-amber-50 text-amber-600 text-amber-700 border-amber-200
bg-yellow-100 bg-yellow-50 text-yellow-600 text-yellow-700 border-yellow-200
bg-purple-100 bg-purple-50 text-purple-600 text-purple-700 border-purple-200
bg-gray-100 bg-gray-50 text-gray-600 text-gray-700 border-gray-200
bg-emerald-100 bg-emerald-50 text-emerald-600 text-emerald-700 border-emerald-200
bg-orange-100 bg-orange-50 text-orange-600 text-orange-700 border-orange-200
bg-indigo-100 bg-indigo-50 text-indigo-600 text-indigo-700 border-indigo-200
bg-teal-100 bg-teal-50 text-teal-600 text-teal-700 border-teal-200
bg-red-500 bg-green-500 bg-blue-500 bg-amber-500 bg-purple-500
h-full
*/
@theme { @theme {
--color-surface-field: oklch(0.98 0.008 145); --color-surface-field: oklch(0.98 0.008 145);
--color-surface-pool: oklch(0.97 0.012 220); --color-surface-pool: oklch(0.97 0.012 220);
......
...@@ -77,10 +77,17 @@ class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:rin ...@@ -77,10 +77,17 @@ class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:rin
<td class="px-4 py-3 text-center"> <td class="px-4 py-3 text-center">
@php @php
$sv = $group->status->value ?? $group->status; $sv = $group->status->value ?? $group->status;
$sc = ['forming'=>'blue','active'=>'green','full'=>'amber','on_hold'=>'gray','completed'=>'purple','cancelled'=>'red']; $statusClass = match($sv) {
$c = $sc[$sv] ?? 'gray'; 'forming' => 'bg-blue-100 text-blue-700',
'active' => 'bg-green-100 text-green-700',
'full' => 'bg-amber-100 text-amber-700',
'on_hold' => 'bg-gray-100 text-gray-700',
'completed' => 'bg-purple-100 text-purple-700',
'cancelled' => 'bg-red-100 text-red-700',
default => 'bg-gray-100 text-gray-700',
};
@endphp @endphp
<span class="px-2 py-0.5 text-xs bg-{{ $c }}-100 text-{{ $c }}-700 rounded-full"> <span class="px-2 py-0.5 text-xs {{ $statusClass }} rounded-full">
{{ __($statusOptions[$sv] ?? $sv) }} {{ __($statusOptions[$sv] ?? $sv) }}
</span> </span>
</td> </td>
......
...@@ -76,9 +76,9 @@ class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:rin ...@@ -76,9 +76,9 @@ class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:rin
@if($product->track_inventory) @if($product->track_inventory)
@php @php
$totalStock = $product->inventoryLevels->sum('quantity_on_hand'); $totalStock = $product->inventoryLevels->sum('quantity_on_hand');
$stockColor = $totalStock <= 0 ? 'red' : ($totalStock <= ($product->min_stock_level ?? 5) ? 'amber' : 'green'); $stockClass = $totalStock <= 0 ? 'bg-red-100 text-red-700' : ($totalStock <= ($product->min_stock_level ?? 5) ? 'bg-amber-100 text-amber-700' : 'bg-green-100 text-green-700');
@endphp @endphp
<span class="px-2 py-0.5 text-xs bg-{{ $stockColor }}-100 text-{{ $stockColor }}-700 rounded-full" dir="ltr"> <span class="px-2 py-0.5 text-xs {{ $stockClass }} rounded-full" dir="ltr">
{{ $totalStock }} {{ $totalStock }}
</span> </span>
@else @else
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment