chore: initial commit of existing codebase

This commit is contained in:
ghassan committed 2026-05-19 12:40:08 +03:00
commit 11e94889b2
292 files changed
+38586

No files matched your search

@@ -0,0 +1,180 @@
@extends('layouts.app')
@section('title', 'New Sales Order')
@section('content')
<div class="mb-6">
<h1 class="page-title">New Sales Order</h1>
<p class="page-subtitle"><a href="{{ route('sales.orders.index') }}" class="text-blue-600 hover:underline">Sales Orders</a> / New</p>
</div>
@if($errors->any())
<div class="mb-4 px-4 py-3 bg-red-50 border border-red-200 rounded-lg text-sm text-red-700">
<ul class="list-disc list-inside space-y-1">
@foreach($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif
<form action="{{ route('sales.orders.store') }}" method="POST">
@csrf
<!-- Header -->
<div class="card card-body mb-4">
<h2 class="text-base font-semibold text-gray-700 mb-4">Order Details</h2>
<div class="grid grid-cols-1 gap-5 sm:grid-cols-2 lg:grid-cols-3">
<div>
<label class="form-label">Customer <span class="text-red-500">*</span></label>
<select name="customer_id" required class="form-select">
<option value="">-- Select Customer --</option>
@foreach($customers as $customer)
<option value="{{ $customer->id }}" {{ old('customer_id') == $customer->id ? 'selected' : '' }}>
{{ $customer->name }}
</option>
@endforeach
</select>
</div>
<div>
<label class="form-label">Order Date <span class="text-red-500">*</span></label>
<input type="date" name="order_date" value="{{ old('order_date', date('Y-m-d')) }}" required class="form-input">
</div>
<div>
<label class="form-label">Delivery Date</label>
<input type="date" name="delivery_date" value="{{ old('delivery_date') }}" class="form-input">
</div>
<div class="sm:col-span-2 lg:col-span-3">
<label class="form-label">Notes</label>
<textarea name="notes" rows="2" class="form-textarea">{{ old('notes') }}</textarea>
</div>
</div>
</div>
<!-- Items -->
<div class="card card-body mb-4">
<div class="flex items-center justify-between mb-4">
<h2 class="text-base font-semibold text-gray-700">Order Items</h2>
<button type="button" onclick="addSORow()" class="btn-primary btn-sm">+ Add Row</button>
</div>
<div class="overflow-x-auto">
<table class="min-w-full text-sm">
<thead>
<tr class="border-b border-gray-200">
<th class="pb-2 text-left font-semibold text-gray-600">Product</th>
<th class="pb-2 text-left font-semibold text-gray-600 w-28">Quantity</th>
<th class="pb-2 text-left font-semibold text-gray-600 w-28">Unit Price</th>
<th class="pb-2 text-left font-semibold text-gray-600 w-28">Total</th>
<th class="pb-2 w-10"></th>
</tr>
</thead>
<tbody id="so-items-body">
<tr class="so-item-row">
<td class="py-1 pr-2">
<select name="items[0][item_id]"
class="w-full border border-gray-300 rounded px-2 py-1.5 text-sm focus:outline-none focus:ring-1 focus:ring-blue-400">
<option value="">-- Select Product --</option>
@foreach($products as $product)
<option value="{{ $product->id }}">{{ $product->item_code }} - {{ $product->item_name }}</option>
@endforeach
</select>
</td>
<td class="py-1 pr-2">
<input type="number" name="items[0][quantity]" placeholder="0" min="0" step="0.01"
onchange="calcSORowTotal(this)"
class="w-full border border-gray-300 rounded px-2 py-1.5 text-sm so-qty">
</td>
<td class="py-1 pr-2">
<input type="number" name="items[0][price]" placeholder="0.00" min="0" step="0.01"
onchange="calcSORowTotal(this)"
class="w-full border border-gray-300 rounded px-2 py-1.5 text-sm so-price">
</td>
<td class="py-1 pr-2">
<input type="number" name="items[0][total]" placeholder="0.00" readonly
class="w-full border border-gray-200 bg-gray-50 rounded px-2 py-1.5 text-sm so-total">
</td>
<td class="py-1">
<button type="button" onclick="removeSORow(this)" class="text-red-400 hover:text-red-600 text-lg leading-none">&times;</button>
</td>
</tr>
</tbody>
</table>
</div>
<div class="mt-3 flex justify-end">
<div class="text-sm font-semibold text-gray-700">
Grand Total: <span id="so-grand-total" class="text-blue-700">0.00</span>
</div>
</div>
</div>
<div class="flex items-center gap-3">
<button type="submit" class="btn-primary">Create Sales Order</button>
<a href="{{ route('sales.orders.index') }}" class="btn-secondary">Cancel</a>
</div>
</form>
<script>
var soRowIndex = 1;
var soItemsHtml = `@foreach($products as $product)<option value="{{ $product->id }}">{{ $product->item_code }} - {{ $product->item_name }}</option>@endforeach`;
function addSORow() {
var tbody = document.getElementById('so-items-body');
var row = document.createElement('tr');
row.className = 'so-item-row';
row.innerHTML = `
<td class="py-1 pr-2">
<select name="items[${soRowIndex}][item_id]" class="w-full border border-gray-300 rounded px-2 py-1.5 text-sm">
<option value="">-- Select Product --</option>
${soItemsHtml}
</select>
</td>
<td class="py-1 pr-2">
<input type="number" name="items[${soRowIndex}][quantity]" placeholder="0" min="0" step="0.01"
onchange="calcSORowTotal(this)" class="w-full border border-gray-300 rounded px-2 py-1.5 text-sm so-qty">
</td>
<td class="py-1 pr-2">
<input type="number" name="items[${soRowIndex}][price]" placeholder="0.00" min="0" step="0.01"
onchange="calcSORowTotal(this)" class="w-full border border-gray-300 rounded px-2 py-1.5 text-sm so-price">
</td>
<td class="py-1 pr-2">
<input type="number" name="items[${soRowIndex}][total]" placeholder="0.00" readonly
class="w-full border border-gray-200 bg-gray-50 rounded px-2 py-1.5 text-sm so-total">
</td>
<td class="py-1">
<button type="button" onclick="removeSORow(this)" class="text-red-400 hover:text-red-600 text-lg leading-none">&times;</button>
</td>`;
tbody.appendChild(row);
soRowIndex++;
}
function removeSORow(btn) {
var rows = document.querySelectorAll('.so-item-row');
if (rows.length > 1) {
btn.closest('tr').remove();
updateSOGrandTotal();
}
}
function calcSORowTotal(input) {
var row = input.closest('tr');
var qty = parseFloat(row.querySelector('.so-qty').value) || 0;
var price = parseFloat(row.querySelector('.so-price').value) || 0;
row.querySelector('.so-total').value = (qty * price).toFixed(2);
updateSOGrandTotal();
}
function updateSOGrandTotal() {
var totals = document.querySelectorAll('.so-total');
var grand = 0;
totals.forEach(function(t) { grand += parseFloat(t.value) || 0; });
document.getElementById('so-grand-total').textContent = grand.toFixed(2);
}
</script>
@endsection
@@ -0,0 +1,62 @@
@extends('layouts.app')
@section('title', 'Edit Sales Order')
@section('content')
<div class="mb-6">
<h1 class="page-title">Edit Sales Order</h1>
<p class="page-subtitle"><a href="{{ route('sales.orders.index') }}" class="text-blue-600 hover:underline">Sales Orders</a> / Edit</p>
</div>
@if($errors->any())
<div class="mb-4 px-4 py-3 bg-red-50 border border-red-200 rounded-lg text-sm text-red-700">
<ul class="list-disc list-inside space-y-1">
@foreach($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif
<div class="card card-body max-w-2xl">
<form action="{{ route('sales.orders.update', $order) }}" method="POST">
@csrf
@method('PUT')
<div class="grid grid-cols-1 gap-5 sm:grid-cols-2">
<div>
<label class="form-label">Customer <span class="text-red-500">*</span></label>
<select name="customer_id" required class="form-select">
<option value="">-- Select Customer --</option>
@foreach($customers as $customer)
<option value="{{ $customer->id }}" {{ old('customer_id', $order->customer_id) == $customer->id ? 'selected' : '' }}>
{{ $customer->name }}
</option>
@endforeach
</select>
</div>
<div>
<label class="form-label">Order Date <span class="text-red-500">*</span></label>
<input type="date" name="order_date" value="{{ old('order_date', $order->order_date ? \Carbon\Carbon::parse($order->order_date)->format('Y-m-d') : '') }}" required class="form-input">
</div>
<div>
<label class="form-label">Delivery Date</label>
<input type="date" name="delivery_date" value="{{ old('delivery_date', $order->delivery_date ? \Carbon\Carbon::parse($order->delivery_date)->format('Y-m-d') : '') }}" class="form-input">
</div>
<div class="sm:col-span-2">
<label class="form-label">Notes</label>
<textarea name="notes" rows="3" class="form-textarea">{{ old('notes', $order->notes) }}</textarea>
</div>
</div>
<div class="mt-6 flex items-center gap-3">
<button type="submit" class="btn-primary">Update Order</button>
<a href="{{ route('sales.orders.show', $order) }}" class="btn-secondary">Cancel</a>
</div>
</form>
</div>
@endsection
@@ -0,0 +1,78 @@
@extends('layouts.app')
@section('title', 'Sales Orders')
@section('content')
<div class="page-header">
<div>
<h1 class="page-title">Sales Orders</h1>
<p class="page-subtitle">Manage customer orders</p>
</div>
<a href="{{ route('sales.orders.create') }}" class="btn-primary">
+ New Order
</a>
</div>
<div class="table-wrapper overflow-x-auto">
<table class="table-base">
<thead>
<tr>
<th>Order #</th>
<th>Customer</th>
<th>Date</th>
<th class="text-right">Total</th>
<th>Status</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
@forelse($orders as $order)
<tr>
<td class="font-mono text-gray-700">{{ $order->order_number ?? 'SO-' . str_pad($order->id, 5, '0', STR_PAD_LEFT) }}</td>
<td class="font-medium text-gray-800">{{ $order->customer->name ?? '' }}</td>
<td>{{ $order->order_date ? \Carbon\Carbon::parse($order->order_date)->format('d M Y') : '' }}</td>
<td class="text-right font-medium text-gray-800">{{ number_format($order->total_amount, 2) }}</td>
<td>
@php
$badgeClass = match($order->status ?? 'draft') {
'draft' => 'badge-gray',
'confirmed' => 'badge-blue',
'dispatched' => 'badge-violet',
'invoiced' => 'badge-green',
'cancelled' => 'badge-red',
default => 'badge-gray',
};
@endphp
<span class="{{ $badgeClass }}">{{ ucfirst($order->status ?? 'draft') }}</span>
</td>
<td>
<div class="flex items-center gap-2 flex-wrap">
<a href="{{ route('sales.orders.show', $order) }}" class="btn-primary btn-sm">View</a>
@if($order->status === 'draft')
<form action="{{ route('sales.orders.confirm', $order) }}" method="POST">
@csrf @method('PATCH')
<button type="submit" class="btn-primary btn-sm">Confirm</button>
</form>
@endif
<a href="{{ route('sales.orders.edit', $order) }}" class="btn-secondary btn-sm">Edit</a>
<form action="{{ route('sales.orders.destroy', $order) }}" method="POST"
onsubmit="confirmDelete(this,'Delete this sales order?','This order and its items will be permanently removed.'); return false;">
@csrf @method('DELETE')
<button type="submit" class="btn-danger btn-sm">Delete</button>
</form>
</div>
</td>
</tr>
@empty
<tr>
<td colspan="6" class="px-4 py-8 text-center text-gray-400">No sales orders found.</td>
</tr>
@endforelse
</tbody>
</table>
</div>
@if($orders->hasPages())
<div class="mt-4">{{ $orders->links() }}</div>
@endif
@endsection
+175
View File
@@ -0,0 +1,175 @@
@extends('layouts.app')
@section('title', 'Sales Order')
@section('content')
<div class="page-header">
<div>
<h1 class="page-title">Sales Order</h1>
<p class="page-subtitle">
<a href="{{ route('sales.orders.index') }}" class="text-blue-600 hover:underline">Sales Orders</a>
/ {{ $order->order_number ?? 'SO-' . str_pad($order->id, 5, '0', STR_PAD_LEFT) }}
</p>
</div>
<div class="flex gap-2">
@if($order->status === 'draft')
<form action="{{ route('sales.orders.confirm', $order) }}" method="POST">
@csrf @method('PATCH')
<button type="submit" class="btn-primary">Confirm Order</button>
</form>
@endif
@if($order->status === 'confirmed')
<a href="{{ route('sales.delivery-notes.create', ['sales_order_id' => $order->id]) }}" class="btn-primary">Create Delivery Note</a>
@endif
<a href="{{ route('sales.orders.edit', $order) }}" class="btn-secondary">Edit</a>
</div>
</div>
<div class="grid grid-cols-1 lg:grid-cols-3 gap-4 mb-6">
<!-- Order Info -->
<div class="lg:col-span-2 card card-body">
<h2 class="text-sm font-semibold text-gray-500 uppercase tracking-wider mb-4">Order Details</h2>
<dl class="grid grid-cols-2 gap-4 text-sm">
<div>
<dt class="text-gray-500">Order Number</dt>
<dd class="font-mono font-semibold text-gray-800">{{ $order->order_number ?? 'SO-' . str_pad($order->id, 5, '0', STR_PAD_LEFT) }}</dd>
</div>
<div>
<dt class="text-gray-500">Status</dt>
<dd>
@php
$badgeClass = match($order->status ?? 'draft') {
'draft' => 'badge-gray',
'confirmed' => 'badge-blue',
'dispatched'=> 'badge-violet',
'invoiced' => 'badge-green',
default => 'badge-gray',
};
@endphp
<span class="{{ $badgeClass }}">{{ ucfirst($order->status ?? 'draft') }}</span>
</dd>
</div>
<div>
<dt class="text-gray-500">Order Date</dt>
<dd class="font-medium text-gray-800">{{ $order->order_date ? \Carbon\Carbon::parse($order->order_date)->format('d M Y') : '-' }}</dd>
</div>
<div>
<dt class="text-gray-500">Delivery Date</dt>
<dd class="font-medium text-gray-800">{{ $order->delivery_date ? \Carbon\Carbon::parse($order->delivery_date)->format('d M Y') : '-' }}</dd>
</div>
</dl>
</div>
<!-- Customer Info -->
<div class="card card-body">
<h2 class="text-sm font-semibold text-gray-500 uppercase tracking-wider mb-4">Customer</h2>
@if($order->customer)
<dl class="space-y-2 text-sm">
<div><dt class="text-gray-500">Name</dt><dd class="font-semibold text-gray-800">{{ $order->customer->name }}</dd></div>
<div><dt class="text-gray-500">Contact</dt><dd class="text-gray-700">{{ $order->customer->contact_person }}</dd></div>
<div><dt class="text-gray-500">Email</dt><dd class="text-gray-700">{{ $order->customer->email }}</dd></div>
<div><dt class="text-gray-500">Phone</dt><dd class="text-gray-700">{{ $order->customer->phone }}</dd></div>
</dl>
@endif
</div>
</div>
<!-- Items -->
<div class="card mb-6 overflow-hidden">
<div class="card-header">
<h2 class="text-base font-semibold text-gray-700">Order Items</h2>
</div>
<div class="overflow-x-auto">
<table class="table-base">
<thead>
<tr>
<th>Product</th>
<th class="text-right">Quantity</th>
<th class="text-right">Unit Price</th>
<th class="text-right">Total</th>
</tr>
</thead>
<tbody>
@forelse($order->items as $item)
<tr>
<td class="text-gray-800">{{ $item->item->item_name ?? '' }}</td>
<td class="text-right text-gray-600">{{ number_format($item->quantity, 2) }}</td>
<td class="text-right text-gray-600">{{ number_format($item->price, 2) }}</td>
<td class="text-right font-medium text-gray-800">{{ number_format($item->total, 2) }}</td>
</tr>
@empty
<tr><td colspan="4" class="px-4 py-6 text-center text-gray-400">No items.</td></tr>
@endforelse
</tbody>
<tfoot class="bg-gray-50">
<tr>
<td colspan="3" class="px-4 py-3 text-right font-semibold text-gray-700">Total</td>
<td class="px-4 py-3 text-right font-bold text-gray-900">{{ number_format($order->total_amount, 2) }}</td>
</tr>
</tfoot>
</table>
</div>
</div>
<!-- Delivery Notes -->
@if(isset($deliveryNotes) && $deliveryNotes->count())
<div class="card mb-6 overflow-hidden">
<div class="card-header">
<h2 class="text-base font-semibold text-gray-700">Delivery Notes</h2>
</div>
<div class="overflow-x-auto">
<table class="table-base">
<thead>
<tr>
<th>DN #</th>
<th>Warehouse</th>
<th>Delivery Date</th>
<th>Status</th>
</tr>
</thead>
<tbody>
@foreach($deliveryNotes as $dn)
<tr>
<td class="font-mono text-gray-700">{{ $dn->dn_number ?? 'DN-' . str_pad($dn->id, 5, '0', STR_PAD_LEFT) }}</td>
<td class="text-gray-700">{{ $dn->warehouse->name ?? '' }}</td>
<td class="text-gray-600">{{ $dn->delivery_date ? \Carbon\Carbon::parse($dn->delivery_date)->format('d M Y') : '' }}</td>
<td><span class="badge-blue">{{ ucfirst($dn->status ?? 'pending') }}</span></td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
@endif
<!-- Invoices -->
@if(isset($invoices) && $invoices->count())
<div class="card overflow-hidden">
<div class="card-header">
<h2 class="text-base font-semibold text-gray-700">Invoices</h2>
</div>
<div class="overflow-x-auto">
<table class="table-base">
<thead>
<tr>
<th>Invoice #</th>
<th>Date</th>
<th class="text-right">Total</th>
<th>Status</th>
</tr>
</thead>
<tbody>
@foreach($invoices as $invoice)
<tr>
<td class="font-mono text-gray-700">{{ $invoice->invoice_number }}</td>
<td class="text-gray-600">{{ $invoice->invoice_date ? \Carbon\Carbon::parse($invoice->invoice_date)->format('d M Y') : '' }}</td>
<td class="text-right font-medium text-gray-800">{{ number_format($invoice->total_amount, 2) }}</td>
<td><span class="badge-green">{{ ucfirst($invoice->status ?? 'unpaid') }}</span></td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
@endif
@endsection