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,27 @@
<x-guest-layout>
<div class="mb-4 text-sm text-gray-600">
{{ __('This is a secure area of the application. Please confirm your password before continuing.') }}
</div>
<form method="POST" action="{{ route('password.confirm') }}">
@csrf
<!-- Password -->
<div>
<x-input-label for="password" :value="__('Password')" />
<x-text-input id="password" class="block mt-1 w-full"
type="password"
name="password"
required autocomplete="current-password" />
<x-input-error :messages="$errors->get('password')" class="mt-2" />
</div>
<div class="flex justify-end mt-4">
<x-primary-button>
{{ __('Confirm') }}
</x-primary-button>
</div>
</form>
</x-guest-layout>
@@ -0,0 +1,25 @@
<x-guest-layout>
<div class="mb-4 text-sm text-gray-600">
{{ __('Forgot your password? No problem. Just let us know your email address and we will email you a password reset link that will allow you to choose a new one.') }}
</div>
<!-- Session Status -->
<x-auth-session-status class="mb-4" :status="session('status')" />
<form method="POST" action="{{ route('password.email') }}">
@csrf
<!-- Email Address -->
<div>
<x-input-label for="email" :value="__('Email')" />
<x-text-input id="email" class="block mt-1 w-full" type="email" name="email" :value="old('email')" required autofocus />
<x-input-error :messages="$errors->get('email')" class="mt-2" />
</div>
<div class="flex items-center justify-end mt-4">
<x-primary-button>
{{ __('Email Password Reset Link') }}
</x-primary-button>
</div>
</form>
</x-guest-layout>
+47
View File
@@ -0,0 +1,47 @@
<x-guest-layout>
<!-- Session Status -->
<x-auth-session-status class="mb-4" :status="session('status')" />
<form method="POST" action="{{ route('login') }}">
@csrf
<!-- Email Address -->
<div>
<x-input-label for="email" :value="__('Email')" />
<x-text-input id="email" class="block mt-1 w-full" type="email" name="email" :value="old('email')" required autofocus autocomplete="username" />
<x-input-error :messages="$errors->get('email')" class="mt-2" />
</div>
<!-- Password -->
<div class="mt-4">
<x-input-label for="password" :value="__('Password')" />
<x-text-input id="password" class="block mt-1 w-full"
type="password"
name="password"
required autocomplete="current-password" />
<x-input-error :messages="$errors->get('password')" class="mt-2" />
</div>
<!-- Remember Me -->
<div class="block mt-4">
<label for="remember_me" class="inline-flex items-center">
<input id="remember_me" type="checkbox" class="rounded border-gray-300 text-indigo-600 shadow-sm focus:ring-indigo-500" name="remember">
<span class="ms-2 text-sm text-gray-600">{{ __('Remember me') }}</span>
</label>
</div>
<div class="flex items-center justify-end mt-4">
@if (Route::has('password.request'))
<a class="underline text-sm text-gray-600 hover:text-gray-900 rounded-md focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500" href="{{ route('password.request') }}">
{{ __('Forgot your password?') }}
</a>
@endif
<x-primary-button class="ms-3">
{{ __('Log in') }}
</x-primary-button>
</div>
</form>
</x-guest-layout>
+52
View File
@@ -0,0 +1,52 @@
<x-guest-layout>
<form method="POST" action="{{ route('register') }}">
@csrf
<!-- Name -->
<div>
<x-input-label for="name" :value="__('Name')" />
<x-text-input id="name" class="block mt-1 w-full" type="text" name="name" :value="old('name')" required autofocus autocomplete="name" />
<x-input-error :messages="$errors->get('name')" class="mt-2" />
</div>
<!-- Email Address -->
<div class="mt-4">
<x-input-label for="email" :value="__('Email')" />
<x-text-input id="email" class="block mt-1 w-full" type="email" name="email" :value="old('email')" required autocomplete="username" />
<x-input-error :messages="$errors->get('email')" class="mt-2" />
</div>
<!-- Password -->
<div class="mt-4">
<x-input-label for="password" :value="__('Password')" />
<x-text-input id="password" class="block mt-1 w-full"
type="password"
name="password"
required autocomplete="new-password" />
<x-input-error :messages="$errors->get('password')" class="mt-2" />
</div>
<!-- Confirm Password -->
<div class="mt-4">
<x-input-label for="password_confirmation" :value="__('Confirm Password')" />
<x-text-input id="password_confirmation" class="block mt-1 w-full"
type="password"
name="password_confirmation" required autocomplete="new-password" />
<x-input-error :messages="$errors->get('password_confirmation')" class="mt-2" />
</div>
<div class="flex items-center justify-end mt-4">
<a class="underline text-sm text-gray-600 hover:text-gray-900 rounded-md focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500" href="{{ route('login') }}">
{{ __('Already registered?') }}
</a>
<x-primary-button class="ms-4">
{{ __('Register') }}
</x-primary-button>
</div>
</form>
</x-guest-layout>
@@ -0,0 +1,39 @@
<x-guest-layout>
<form method="POST" action="{{ route('password.store') }}">
@csrf
<!-- Password Reset Token -->
<input type="hidden" name="token" value="{{ $request->route('token') }}">
<!-- Email Address -->
<div>
<x-input-label for="email" :value="__('Email')" />
<x-text-input id="email" class="block mt-1 w-full" type="email" name="email" :value="old('email', $request->email)" required autofocus autocomplete="username" />
<x-input-error :messages="$errors->get('email')" class="mt-2" />
</div>
<!-- Password -->
<div class="mt-4">
<x-input-label for="password" :value="__('Password')" />
<x-text-input id="password" class="block mt-1 w-full" type="password" name="password" required autocomplete="new-password" />
<x-input-error :messages="$errors->get('password')" class="mt-2" />
</div>
<!-- Confirm Password -->
<div class="mt-4">
<x-input-label for="password_confirmation" :value="__('Confirm Password')" />
<x-text-input id="password_confirmation" class="block mt-1 w-full"
type="password"
name="password_confirmation" required autocomplete="new-password" />
<x-input-error :messages="$errors->get('password_confirmation')" class="mt-2" />
</div>
<div class="flex items-center justify-end mt-4">
<x-primary-button>
{{ __('Reset Password') }}
</x-primary-button>
</div>
</form>
</x-guest-layout>
@@ -0,0 +1,31 @@
<x-guest-layout>
<div class="mb-4 text-sm text-gray-600">
{{ __('Thanks for signing up! Before getting started, could you verify your email address by clicking on the link we just emailed to you? If you didn\'t receive the email, we will gladly send you another.') }}
</div>
@if (session('status') == 'verification-link-sent')
<div class="mb-4 font-medium text-sm text-green-600">
{{ __('A new verification link has been sent to the email address you provided during registration.') }}
</div>
@endif
<div class="mt-4 flex items-center justify-between">
<form method="POST" action="{{ route('verification.send') }}">
@csrf
<div>
<x-primary-button>
{{ __('Resend Verification Email') }}
</x-primary-button>
</div>
</form>
<form method="POST" action="{{ route('logout') }}">
@csrf
<button type="submit" class="underline text-sm text-gray-600 hover:text-gray-900 rounded-md focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">
{{ __('Log Out') }}
</button>
</form>
</div>
</x-guest-layout>
@@ -0,0 +1,3 @@
<svg viewBox="0 0 316 316" xmlns="http://www.w3.org/2000/svg" {{ $attributes }}>
<path d="M305.8 81.125C305.77 80.995 305.69 80.885 305.65 80.755C305.56 80.525 305.49 80.285 305.37 80.075C305.29 79.935 305.17 79.815 305.07 79.685C304.94 79.515 304.83 79.325 304.68 79.175C304.55 79.045 304.39 78.955 304.25 78.845C304.09 78.715 303.95 78.575 303.77 78.475L251.32 48.275C249.97 47.495 248.31 47.495 246.96 48.275L194.51 78.475C194.33 78.575 194.19 78.725 194.03 78.845C193.89 78.955 193.73 79.045 193.6 79.175C193.45 79.325 193.34 79.515 193.21 79.685C193.11 79.815 192.99 79.935 192.91 80.075C192.79 80.285 192.71 80.525 192.63 80.755C192.58 80.875 192.51 80.995 192.48 81.125C192.38 81.495 192.33 81.875 192.33 82.265V139.625L148.62 164.795V52.575C148.62 52.185 148.57 51.805 148.47 51.435C148.44 51.305 148.36 51.195 148.32 51.065C148.23 50.835 148.16 50.595 148.04 50.385C147.96 50.245 147.84 50.125 147.74 49.995C147.61 49.825 147.5 49.635 147.35 49.485C147.22 49.355 147.06 49.265 146.92 49.155C146.76 49.025 146.62 48.885 146.44 48.785L93.99 18.585C92.64 17.805 90.98 17.805 89.63 18.585L37.18 48.785C37 48.885 36.86 49.035 36.7 49.155C36.56 49.265 36.4 49.355 36.27 49.485C36.12 49.635 36.01 49.825 35.88 49.995C35.78 50.125 35.66 50.245 35.58 50.385C35.46 50.595 35.38 50.835 35.3 51.065C35.25 51.185 35.18 51.305 35.15 51.435C35.05 51.805 35 52.185 35 52.575V232.235C35 233.795 35.84 235.245 37.19 236.025L142.1 296.425C142.33 296.555 142.58 296.635 142.82 296.725C142.93 296.765 143.04 296.835 143.16 296.865C143.53 296.965 143.9 297.015 144.28 297.015C144.66 297.015 145.03 296.965 145.4 296.865C145.5 296.835 145.59 296.775 145.69 296.745C145.95 296.655 146.21 296.565 146.45 296.435L251.36 236.035C252.72 235.255 253.55 233.815 253.55 232.245V174.885L303.81 145.945C305.17 145.165 306 143.725 306 142.155V82.265C305.95 81.875 305.89 81.495 305.8 81.125ZM144.2 227.205L100.57 202.515L146.39 176.135L196.66 147.195L240.33 172.335L208.29 190.625L144.2 227.205ZM244.75 114.995V164.795L226.39 154.225L201.03 139.625V89.825L219.39 100.395L244.75 114.995ZM249.12 57.105L292.81 82.265L249.12 107.425L205.43 82.265L249.12 57.105ZM114.49 184.425L96.13 194.995V85.305L121.49 70.705L139.85 60.135V169.815L114.49 184.425ZM91.76 27.425L135.45 52.585L91.76 77.745L48.07 52.585L91.76 27.425ZM43.67 60.135L62.03 70.705L87.39 85.305V202.545V202.555V202.565C87.39 202.735 87.44 202.895 87.46 203.055C87.49 203.265 87.49 203.485 87.55 203.695V203.705C87.6 203.875 87.69 204.035 87.76 204.195C87.84 204.375 87.89 204.575 87.99 204.745C87.99 204.745 87.99 204.755 88 204.755C88.09 204.905 88.22 205.035 88.33 205.175C88.45 205.335 88.55 205.495 88.69 205.635L88.7 205.645C88.82 205.765 88.98 205.855 89.12 205.965C89.28 206.085 89.42 206.225 89.59 206.325C89.6 206.325 89.6 206.325 89.61 206.335C89.62 206.335 89.62 206.345 89.63 206.345L139.87 234.775V285.065L43.67 229.705V60.135ZM244.75 229.705L148.58 285.075V234.775L219.8 194.115L244.75 179.875V229.705ZM297.2 139.625L253.49 164.795V114.995L278.85 100.395L297.21 89.825V139.625H297.2Z"/>
</svg>

After

Width:  |  Height:  |  Size: 3.0 KiB

@@ -0,0 +1,7 @@
@props(['status'])
@if ($status)
<div {{ $attributes->merge(['class' => 'font-medium text-sm text-green-600']) }}>
{{ $status }}
</div>
@endif
@@ -0,0 +1,3 @@
<button {{ $attributes->merge(['type' => 'submit', 'class' => 'inline-flex items-center px-4 py-2 bg-red-600 border border-transparent rounded-md font-semibold text-xs text-white uppercase tracking-widest hover:bg-red-500 active:bg-red-700 focus:outline-none focus:ring-2 focus:ring-red-500 focus:ring-offset-2 transition ease-in-out duration-150']) }}>
{{ $slot }}
</button>
@@ -0,0 +1 @@
<a {{ $attributes->merge(['class' => 'block w-full px-4 py-2 text-start text-sm leading-5 text-gray-700 hover:bg-gray-100 focus:outline-none focus:bg-gray-100 transition duration-150 ease-in-out']) }}>{{ $slot }}</a>
@@ -0,0 +1,35 @@
@props(['align' => 'right', 'width' => '48', 'contentClasses' => 'py-1 bg-white'])
@php
$alignmentClasses = match ($align) {
'left' => 'ltr:origin-top-left rtl:origin-top-right start-0',
'top' => 'origin-top',
default => 'ltr:origin-top-right rtl:origin-top-left end-0',
};
$width = match ($width) {
'48' => 'w-48',
default => $width,
};
@endphp
<div class="relative" x-data="{ open: false }" @click.outside="open = false" @close.stop="open = false">
<div @click="open = ! open">
{{ $trigger }}
</div>
<div x-show="open"
x-transition:enter="transition ease-out duration-200"
x-transition:enter-start="opacity-0 scale-95"
x-transition:enter-end="opacity-100 scale-100"
x-transition:leave="transition ease-in duration-75"
x-transition:leave-start="opacity-100 scale-100"
x-transition:leave-end="opacity-0 scale-95"
class="absolute z-50 mt-2 {{ $width }} rounded-md shadow-lg {{ $alignmentClasses }}"
style="display: none;"
@click="open = false">
<div class="rounded-md ring-1 ring-black ring-opacity-5 {{ $contentClasses }}">
{{ $content }}
</div>
</div>
</div>
@@ -0,0 +1,9 @@
@props(['messages'])
@if ($messages)
<ul {{ $attributes->merge(['class' => 'text-sm text-red-600 space-y-1']) }}>
@foreach ((array) $messages as $message)
<li>{{ $message }}</li>
@endforeach
</ul>
@endif
@@ -0,0 +1,5 @@
@props(['value'])
<label {{ $attributes->merge(['class' => 'block font-medium text-sm text-gray-700']) }}>
{{ $value ?? $slot }}
</label>
@@ -0,0 +1,78 @@
@props([
'name',
'show' => false,
'maxWidth' => '2xl'
])
@php
$maxWidth = [
'sm' => 'sm:max-w-sm',
'md' => 'sm:max-w-md',
'lg' => 'sm:max-w-lg',
'xl' => 'sm:max-w-xl',
'2xl' => 'sm:max-w-2xl',
][$maxWidth];
@endphp
<div
x-data="{
show: @js($show),
focusables() {
// All focusable element types...
let selector = 'a, button, input:not([type=\'hidden\']), textarea, select, details, [tabindex]:not([tabindex=\'-1\'])'
return [...$el.querySelectorAll(selector)]
// All non-disabled elements...
.filter(el => ! el.hasAttribute('disabled'))
},
firstFocusable() { return this.focusables()[0] },
lastFocusable() { return this.focusables().slice(-1)[0] },
nextFocusable() { return this.focusables()[this.nextFocusableIndex()] || this.firstFocusable() },
prevFocusable() { return this.focusables()[this.prevFocusableIndex()] || this.lastFocusable() },
nextFocusableIndex() { return (this.focusables().indexOf(document.activeElement) + 1) % (this.focusables().length + 1) },
prevFocusableIndex() { return Math.max(0, this.focusables().indexOf(document.activeElement)) -1 },
}"
x-init="$watch('show', value => {
if (value) {
document.body.classList.add('overflow-y-hidden');
{{ $attributes->has('focusable') ? 'setTimeout(() => firstFocusable().focus(), 100)' : '' }}
} else {
document.body.classList.remove('overflow-y-hidden');
}
})"
x-on:open-modal.window="$event.detail == '{{ $name }}' ? show = true : null"
x-on:close-modal.window="$event.detail == '{{ $name }}' ? show = false : null"
x-on:close.stop="show = false"
x-on:keydown.escape.window="show = false"
x-on:keydown.tab.prevent="$event.shiftKey || nextFocusable().focus()"
x-on:keydown.shift.tab.prevent="prevFocusable().focus()"
x-show="show"
class="fixed inset-0 overflow-y-auto px-4 py-6 sm:px-0 z-50"
style="display: {{ $show ? 'block' : 'none' }};"
>
<div
x-show="show"
class="fixed inset-0 transform transition-all"
x-on:click="show = false"
x-transition:enter="ease-out duration-300"
x-transition:enter-start="opacity-0"
x-transition:enter-end="opacity-100"
x-transition:leave="ease-in duration-200"
x-transition:leave-start="opacity-100"
x-transition:leave-end="opacity-0"
>
<div class="absolute inset-0 bg-gray-500 opacity-75"></div>
</div>
<div
x-show="show"
class="mb-6 bg-white rounded-lg overflow-hidden shadow-xl transform transition-all sm:w-full {{ $maxWidth }} sm:mx-auto"
x-transition:enter="ease-out duration-300"
x-transition:enter-start="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
x-transition:enter-end="opacity-100 translate-y-0 sm:scale-100"
x-transition:leave="ease-in duration-200"
x-transition:leave-start="opacity-100 translate-y-0 sm:scale-100"
x-transition:leave-end="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
>
{{ $slot }}
</div>
</div>
@@ -0,0 +1,11 @@
@props(['active'])
@php
$classes = ($active ?? false)
? 'inline-flex items-center px-1 pt-1 border-b-2 border-indigo-400 text-sm font-medium leading-5 text-gray-900 focus:outline-none focus:border-indigo-700 transition duration-150 ease-in-out'
: 'inline-flex items-center px-1 pt-1 border-b-2 border-transparent text-sm font-medium leading-5 text-gray-500 hover:text-gray-700 hover:border-gray-300 focus:outline-none focus:text-gray-700 focus:border-gray-300 transition duration-150 ease-in-out';
@endphp
<a {{ $attributes->merge(['class' => $classes]) }}>
{{ $slot }}
</a>
@@ -0,0 +1,3 @@
<button {{ $attributes->merge(['type' => 'submit', 'class' => 'inline-flex items-center px-4 py-2 bg-gray-800 border border-transparent rounded-md font-semibold text-xs text-white uppercase tracking-widest hover:bg-gray-700 focus:bg-gray-700 active:bg-gray-900 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 transition ease-in-out duration-150']) }}>
{{ $slot }}
</button>
@@ -0,0 +1,11 @@
@props(['active'])
@php
$classes = ($active ?? false)
? 'block w-full ps-3 pe-4 py-2 border-l-4 border-indigo-400 text-start text-base font-medium text-indigo-700 bg-indigo-50 focus:outline-none focus:text-indigo-800 focus:bg-indigo-100 focus:border-indigo-700 transition duration-150 ease-in-out'
: 'block w-full ps-3 pe-4 py-2 border-l-4 border-transparent text-start text-base font-medium text-gray-600 hover:text-gray-800 hover:bg-gray-50 hover:border-gray-300 focus:outline-none focus:text-gray-800 focus:bg-gray-50 focus:border-gray-300 transition duration-150 ease-in-out';
@endphp
<a {{ $attributes->merge(['class' => $classes]) }}>
{{ $slot }}
</a>
@@ -0,0 +1,3 @@
<button {{ $attributes->merge(['type' => 'button', 'class' => 'inline-flex items-center px-4 py-2 bg-white border border-gray-300 rounded-md font-semibold text-xs text-gray-700 uppercase tracking-widest shadow-sm hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 disabled:opacity-25 transition ease-in-out duration-150']) }}>
{{ $slot }}
</button>
@@ -0,0 +1,3 @@
@props(['disabled' => false])
<input @disabled($disabled) {{ $attributes->merge(['class' => 'border-gray-300 focus:border-indigo-500 focus:ring-indigo-500 rounded-md shadow-sm']) }}>
+232
View File
@@ -0,0 +1,232 @@
@extends('layouts.app')
@section('title', 'Dashboard')
@section('content')
{{-- Page header --}}
<div class="mb-6">
<h1 class="text-2xl font-bold text-slate-800">Dashboard</h1>
<p class="text-slate-500 text-sm mt-1">Welcome back, {{ Auth::user()->name }}. Here's what's happening today.</p>
</div>
{{-- KPI Cards --}}
<div class="grid grid-cols-1 sm:grid-cols-2 xl:grid-cols-5 gap-4 mb-8">
<div class="bg-white rounded-2xl p-5 shadow-sm border border-slate-200 flex items-start gap-4">
<div class="w-11 h-11 rounded-xl bg-blue-100 flex items-center justify-center flex-shrink-0">
<svg class="w-5 h-5 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8c-1.657 0-3 .895-3 2s1.343 2 3 2 3 .895 3 2-1.343 2-3 2m0-8c1.11 0 2.08.402 2.599 1M12 8V7m0 1v8m0 0v1m0-1c-1.11 0-2.08-.402-2.599-1M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/>
</svg>
</div>
<div class="min-w-0">
<p class="text-xs font-semibold text-slate-400 uppercase tracking-wider">Total Sales</p>
<p class="text-2xl font-bold text-slate-800 mt-1 truncate">{{ number_format($totalSales ?? 0, 0) }}</p>
<p class="text-xs text-slate-400 mt-0.5">All time invoiced</p>
</div>
</div>
<div class="bg-white rounded-2xl p-5 shadow-sm border border-slate-200 flex items-start gap-4">
<div class="w-11 h-11 rounded-xl bg-emerald-100 flex items-center justify-center flex-shrink-0">
<svg class="w-5 h-5 text-emerald-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 8h14M5 8a2 2 0 110-4h14a2 2 0 110 4M5 8v10a2 2 0 002 2h10a2 2 0 002-2V8m-9 4h4"/>
</svg>
</div>
<div class="min-w-0">
<p class="text-xs font-semibold text-slate-400 uppercase tracking-wider">Inventory Value</p>
<p class="text-2xl font-bold text-slate-800 mt-1 truncate">{{ number_format($inventoryValue ?? 0, 0) }}</p>
<p class="text-xs text-slate-400 mt-0.5">All warehouses</p>
</div>
</div>
<div class="bg-white rounded-2xl p-5 shadow-sm border border-slate-200 flex items-start gap-4">
<div class="w-11 h-11 rounded-xl bg-orange-100 flex items-center justify-center flex-shrink-0">
<svg class="w-5 h-5 text-orange-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z"/><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/>
</svg>
</div>
<div>
<p class="text-xs font-semibold text-slate-400 uppercase tracking-wider">Production Active</p>
<p class="text-2xl font-bold text-slate-800 mt-1">{{ $productionInProgress ?? 0 }}</p>
<p class="text-xs text-slate-400 mt-0.5">Orders in progress</p>
</div>
</div>
<a href="{{ route('purchase.pipeline.index') }}"
class="bg-white rounded-2xl p-5 shadow-sm border border-slate-200 flex items-start gap-4 hover:border-amber-300 hover:shadow-md transition-all duration-200 group">
<div class="w-11 h-11 rounded-xl bg-amber-100 group-hover:bg-amber-200 flex items-center justify-center flex-shrink-0 transition-colors">
<svg class="w-5 h-5 text-amber-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"/>
</svg>
</div>
<div>
<p class="text-xs font-semibold text-slate-400 uppercase tracking-wider">Purchase Pipeline</p>
<p class="text-2xl font-bold text-slate-800 mt-1">{{ $purchasePending ?? 0 }}</p>
<p class="text-xs text-slate-400 mt-0.5">Active pipelines</p>
</div>
</a>
<div class="bg-white rounded-2xl p-5 shadow-sm border border-slate-200 flex items-start gap-4">
<div class="w-11 h-11 rounded-xl bg-red-100 flex items-center justify-center flex-shrink-0">
<svg class="w-5 h-5 text-red-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"/>
</svg>
</div>
<div class="min-w-0">
<p class="text-xs font-semibold text-slate-400 uppercase tracking-wider">Receivables</p>
<p class="text-2xl font-bold text-red-600 mt-1 truncate">{{ number_format($outstandingReceivables ?? 0, 0) }}</p>
<p class="text-xs text-slate-400 mt-0.5">Outstanding</p>
</div>
</div>
</div>
{{-- Quick Actions --}}
<div class="mb-6">
<h2 class="text-sm font-semibold text-slate-500 uppercase tracking-wider mb-3">Quick Actions</h2>
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-3">
<a href="{{ route('purchase.requests.create') }}"
class="group bg-white rounded-xl p-4 border border-slate-200 hover:border-amber-300 hover:shadow-md transition-all duration-200 flex items-center gap-4">
<div class="w-10 h-10 rounded-lg bg-amber-50 group-hover:bg-amber-100 flex items-center justify-center flex-shrink-0 transition-colors">
<svg class="w-5 h-5 text-amber-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4"/>
</svg>
</div>
<div>
<p class="text-sm font-semibold text-slate-700 group-hover:text-amber-700">New Purchase Request</p>
<p class="text-xs text-slate-400 mt-0.5">Raise a PR for approval</p>
</div>
</a>
<a href="{{ route('sales.orders.create') }}"
class="group bg-white rounded-xl p-4 border border-slate-200 hover:border-violet-300 hover:shadow-md transition-all duration-200 flex items-center gap-4">
<div class="w-10 h-10 rounded-lg bg-violet-50 group-hover:bg-violet-100 flex items-center justify-center flex-shrink-0 transition-colors">
<svg class="w-5 h-5 text-violet-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4"/>
</svg>
</div>
<div>
<p class="text-sm font-semibold text-slate-700 group-hover:text-violet-700">New Sales Order</p>
<p class="text-xs text-slate-400 mt-0.5">Create order for a customer</p>
</div>
</a>
<a href="{{ route('production.orders.create') }}"
class="group bg-white rounded-xl p-4 border border-slate-200 hover:border-orange-300 hover:shadow-md transition-all duration-200 flex items-center gap-4">
<div class="w-10 h-10 rounded-lg bg-orange-50 group-hover:bg-orange-100 flex items-center justify-center flex-shrink-0 transition-colors">
<svg class="w-5 h-5 text-orange-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4"/>
</svg>
</div>
<div>
<p class="text-sm font-semibold text-slate-700 group-hover:text-orange-700">New Production Order</p>
<p class="text-xs text-slate-400 mt-0.5">Schedule a production run</p>
</div>
</a>
<a href="{{ route('inventory.reports.low-stock') }}"
class="group bg-white rounded-xl p-4 border border-slate-200 hover:border-red-300 hover:shadow-md transition-all duration-200 flex items-center gap-4">
<div class="w-10 h-10 rounded-lg bg-red-50 group-hover:bg-red-100 flex items-center justify-center flex-shrink-0 transition-colors">
<svg class="w-5 h-5 text-red-500" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"/>
</svg>
</div>
<div>
<p class="text-sm font-semibold text-slate-700 group-hover:text-red-600">Low Stock Alert</p>
<p class="text-xs text-slate-400 mt-0.5">Items below minimum level</p>
</div>
</a>
</div>
</div>
{{-- Module Shortcuts --}}
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
<div class="bg-white rounded-2xl border border-slate-200 shadow-sm overflow-hidden">
<div class="bg-gradient-to-r from-amber-500 to-amber-400 px-5 py-4">
<h3 class="text-white font-semibold text-sm">Purchase</h3>
<p class="text-amber-100 text-xs mt-0.5">Procurement workflow</p>
</div>
<div class="p-4 space-y-1">
<a href="{{ route('purchase.requests.index') }}" class="flex items-center justify-between py-1.5 text-sm text-slate-600 hover:text-amber-600 transition-colors">
<span>Purchase Requests</span>
<svg class="w-3.5 h-3.5 text-slate-300" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"/></svg>
</a>
<a href="{{ route('purchase.orders.index') }}" class="flex items-center justify-between py-1.5 text-sm text-slate-600 hover:text-amber-600 transition-colors">
<span>Purchase Orders</span>
<svg class="w-3.5 h-3.5 text-slate-300" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"/></svg>
</a>
<a href="{{ route('purchase.grns.index') }}" class="flex items-center justify-between py-1.5 text-sm text-slate-600 hover:text-amber-600 transition-colors">
<span>Goods Receipt (GRN)</span>
<svg class="w-3.5 h-3.5 text-slate-300" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"/></svg>
</a>
</div>
</div>
<div class="bg-white rounded-2xl border border-slate-200 shadow-sm overflow-hidden">
<div class="bg-gradient-to-r from-emerald-500 to-emerald-400 px-5 py-4">
<h3 class="text-white font-semibold text-sm">Inventory</h3>
<p class="text-emerald-100 text-xs mt-0.5">Stock management</p>
</div>
<div class="p-4 space-y-1">
<a href="{{ route('inventory.items.index') }}" class="flex items-center justify-between py-1.5 text-sm text-slate-600 hover:text-emerald-600 transition-colors">
<span>Item Master</span>
<svg class="w-3.5 h-3.5 text-slate-300" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"/></svg>
</a>
<a href="{{ route('inventory.reports.summary') }}" class="flex items-center justify-between py-1.5 text-sm text-slate-600 hover:text-emerald-600 transition-colors">
<span>Stock Summary</span>
<svg class="w-3.5 h-3.5 text-slate-300" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"/></svg>
</a>
<a href="{{ route('inventory.reports.valuation') }}" class="flex items-center justify-between py-1.5 text-sm text-slate-600 hover:text-emerald-600 transition-colors">
<span>Inventory Valuation</span>
<svg class="w-3.5 h-3.5 text-slate-300" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"/></svg>
</a>
</div>
</div>
<div class="bg-white rounded-2xl border border-slate-200 shadow-sm overflow-hidden">
<div class="bg-gradient-to-r from-orange-500 to-orange-400 px-5 py-4">
<h3 class="text-white font-semibold text-sm">Production</h3>
<p class="text-orange-100 text-xs mt-0.5">Manufacturing operations</p>
</div>
<div class="p-4 space-y-1">
<a href="{{ route('production.orders.index') }}" class="flex items-center justify-between py-1.5 text-sm text-slate-600 hover:text-orange-600 transition-colors">
<span>Production Orders</span>
<svg class="w-3.5 h-3.5 text-slate-300" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"/></svg>
</a>
<a href="{{ route('production.bom.index') }}" class="flex items-center justify-between py-1.5 text-sm text-slate-600 hover:text-orange-600 transition-colors">
<span>Bill of Materials</span>
<svg class="w-3.5 h-3.5 text-slate-300" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"/></svg>
</a>
<a href="{{ route('production.material-issues.index') }}" class="flex items-center justify-between py-1.5 text-sm text-slate-600 hover:text-orange-600 transition-colors">
<span>Material Issues</span>
<svg class="w-3.5 h-3.5 text-slate-300" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"/></svg>
</a>
</div>
</div>
<div class="bg-white rounded-2xl border border-slate-200 shadow-sm overflow-hidden">
<div class="bg-gradient-to-r from-violet-500 to-violet-400 px-5 py-4">
<h3 class="text-white font-semibold text-sm">Sales</h3>
<p class="text-violet-100 text-xs mt-0.5">Customer & revenue</p>
</div>
<div class="p-4 space-y-1">
<a href="{{ route('sales.orders.index') }}" class="flex items-center justify-between py-1.5 text-sm text-slate-600 hover:text-violet-600 transition-colors">
<span>Sales Orders</span>
<svg class="w-3.5 h-3.5 text-slate-300" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"/></svg>
</a>
<a href="{{ route('sales.invoices.index') }}" class="flex items-center justify-between py-1.5 text-sm text-slate-600 hover:text-violet-600 transition-colors">
<span>Sales Invoices</span>
<svg class="w-3.5 h-3.5 text-slate-300" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"/></svg>
</a>
<a href="{{ route('sales.customers.index') }}" class="flex items-center justify-between py-1.5 text-sm text-slate-600 hover:text-violet-600 transition-colors">
<span>Customers</span>
<svg class="w-3.5 h-3.5 text-slate-300" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"/></svg>
</a>
</div>
</div>
</div>
@endsection
@@ -0,0 +1,84 @@
@extends('layouts.app')
@section('title', 'Add Item')
@section('content')
<div class="mb-6">
<h1 class="page-title">Add Item</h1>
<p class="page-subtitle"><a href="{{ route('inventory.items.index') }}" class="text-blue-600 hover:underline">Items</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
<div class="card card-body max-w-2xl">
<form action="{{ route('inventory.items.store') }}" method="POST">
@csrf
<div class="grid grid-cols-1 gap-5 sm:grid-cols-2">
<div>
<label class="form-label">Item Code <span class="text-red-500">*</span></label>
<input type="text" name="item_code" value="{{ old('item_code') }}" required
placeholder="e.g. RAW-001" class="form-input">
<p class="text-xs text-gray-400 mt-1">Unique code for this item</p>
</div>
<div>
<label class="form-label">Item Name <span class="text-red-500">*</span></label>
<input type="text" name="item_name" value="{{ old('item_name') }}" required class="form-input">
</div>
<div>
<label class="form-label">Category <span class="text-red-500">*</span></label>
<select name="category" required class="form-select">
<option value="">-- Select Category --</option>
<option value="raw_material" {{ old('category') === 'raw_material' ? 'selected' : '' }}>Raw Material</option>
<option value="wip" {{ old('category') === 'wip' ? 'selected' : '' }}>Work In Progress (WIP)</option>
<option value="finished_good" {{ old('category') === 'finished_good' ? 'selected' : '' }}>Finished Good</option>
</select>
</div>
<div>
<label class="form-label">Unit of Measure <span class="text-red-500">*</span></label>
<input type="text" name="unit_of_measure" value="{{ old('unit_of_measure') }}" required
placeholder="e.g. KG, PCS, Ltr" class="form-input">
</div>
<div>
<label class="form-label">Minimum Stock Level</label>
<input type="number" name="minimum_stock_level" value="{{ old('minimum_stock_level', 0) }}" min="0" step="0.01" class="form-input">
</div>
<div>
<label class="form-label">Cost Price</label>
<input type="number" name="cost_price" value="{{ old('cost_price', 0) }}" min="0" step="0.01" class="form-input">
</div>
<div class="sm:col-span-2">
<label class="form-label">Description</label>
<textarea name="description" rows="3" class="form-textarea">{{ old('description') }}</textarea>
</div>
<div class="sm:col-span-2 flex items-center gap-2">
<input type="checkbox" name="is_active" id="is_active" value="1"
{{ old('is_active', '1') ? 'checked' : '' }}
class="h-4 w-4 text-blue-600 border-gray-300 rounded">
<label for="is_active" class="form-label mb-0">Active</label>
</div>
</div>
<div class="mt-6 flex items-center gap-3">
<button type="submit" class="btn-primary">Save Item</button>
<a href="{{ route('inventory.items.index') }}" class="btn-secondary">Cancel</a>
</div>
</form>
</div>
@endsection
@@ -0,0 +1,82 @@
@extends('layouts.app')
@section('title', 'Edit Item')
@section('content')
<div class="mb-6">
<h1 class="page-title">Edit Item</h1>
<p class="page-subtitle"><a href="{{ route('inventory.items.index') }}" class="text-blue-600 hover:underline">Items</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('inventory.items.update', $item) }}" method="POST">
@csrf
@method('PUT')
<div class="grid grid-cols-1 gap-5 sm:grid-cols-2">
<div>
<label class="form-label">Item Code <span class="text-red-500">*</span></label>
<input type="text" name="item_code" value="{{ old('item_code', $item->item_code) }}" required class="form-input">
</div>
<div>
<label class="form-label">Item Name <span class="text-red-500">*</span></label>
<input type="text" name="item_name" value="{{ old('item_name', $item->item_name) }}" required class="form-input">
</div>
<div>
<label class="form-label">Category <span class="text-red-500">*</span></label>
<select name="category" required class="form-select">
<option value="">-- Select Category --</option>
<option value="raw_material" {{ old('category', $item->category) === 'raw_material' ? 'selected' : '' }}>Raw Material</option>
<option value="wip" {{ old('category', $item->category) === 'wip' ? 'selected' : '' }}>Work In Progress (WIP)</option>
<option value="finished_good" {{ old('category', $item->category) === 'finished_good' ? 'selected' : '' }}>Finished Good</option>
</select>
</div>
<div>
<label class="form-label">Unit of Measure <span class="text-red-500">*</span></label>
<input type="text" name="unit_of_measure" value="{{ old('unit_of_measure', $item->unit_of_measure) }}" required class="form-input">
</div>
<div>
<label class="form-label">Minimum Stock Level</label>
<input type="number" name="minimum_stock_level" value="{{ old('minimum_stock_level', $item->minimum_stock_level) }}" min="0" step="0.01" class="form-input">
</div>
<div>
<label class="form-label">Cost Price</label>
<input type="number" name="cost_price" value="{{ old('cost_price', $item->cost_price) }}" min="0" step="0.01" class="form-input">
</div>
<div class="sm:col-span-2">
<label class="form-label">Description</label>
<textarea name="description" rows="3" class="form-textarea">{{ old('description', $item->description) }}</textarea>
</div>
<div class="sm:col-span-2 flex items-center gap-2">
<input type="checkbox" name="is_active" id="is_active" value="1"
{{ old('is_active', $item->is_active) ? 'checked' : '' }}
class="h-4 w-4 text-blue-600 border-gray-300 rounded">
<label for="is_active" class="form-label mb-0">Active</label>
</div>
</div>
<div class="mt-6 flex items-center gap-3">
<button type="submit" class="btn-primary">Update Item</button>
<a href="{{ route('inventory.items.index') }}" class="btn-secondary">Cancel</a>
</div>
</form>
</div>
@endsection
@@ -0,0 +1,204 @@
@extends('layouts.app')
@section('title', 'Inventory Items')
@section('content')
<div class="page-header">
<div>
<h1 class="page-title">Inventory Items</h1>
<p class="page-subtitle">Manage all stock items</p>
</div>
<div class="flex items-center gap-2 flex-wrap">
<a href="{{ route('inventory.items.export-pdf') }}" class="btn btn-secondary" title="Export items as PDF">
<svg width="15" height="15" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M7 21h10a2 2 0 002-2V9.414a1 1 0 00-.293-.707l-5.414-5.414A1 1 0 0012.586 3H7a2 2 0 00-2 2v14a2 2 0 002 2z"/>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 13h6M9 17h4"/>
</svg>
Export PDF
</a>
<a href="{{ route('inventory.items.template') }}" class="btn btn-secondary" title="Download Excel import template">
<svg width="15" height="15" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-4l-4 4m0 0l-4-4m4 4V4"/>
</svg>
Template
</a>
<button onclick="document.getElementById('import-modal').classList.remove('hidden')"
class="btn btn-success" title="Import items from Excel">
<svg width="15" height="15" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-8l-4-4m0 0L8 8m4-4v12"/>
</svg>
Import Excel
</button>
<a href="{{ route('inventory.items.create') }}" class="btn-primary">
+ Add Item
</a>
</div>
</div>
<div class="table-wrapper overflow-x-auto">
<table class="table-base">
<thead>
<tr>
<th>Code</th>
<th>Name</th>
<th>Category</th>
<th>UOM</th>
<th class="text-right">Min Stock</th>
<th class="text-right">Cost Price</th>
<th>Status</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
@forelse($items as $item)
<tr>
<td class="font-mono text-gray-700">{{ $item->item_code }}</td>
<td class="font-medium text-gray-800">{{ $item->item_name }}</td>
<td>
@php
$catBadgeClass = match($item->category) {
'raw_material' => 'badge-blue',
'wip' => 'badge-yellow',
'finished_good' => 'badge-green',
default => 'badge-gray',
};
$catLabels = ['raw_material' => 'Raw Material', 'wip' => 'WIP', 'finished_good' => 'Finished Good'];
@endphp
<span class="{{ $catBadgeClass }}">
{{ $catLabels[$item->category] ?? ucfirst($item->category) }}
</span>
</td>
<td>{{ $item->unit_of_measure }}</td>
<td class="text-right">{{ number_format($item->minimum_stock_level, 2) }}</td>
<td class="text-right text-gray-800">{{ number_format($item->cost_price, 2) }}</td>
<td>
@if($item->is_active)
<span class="badge-green">Active</span>
@else
<span class="badge-gray">Inactive</span>
@endif
</td>
<td>
<div class="flex items-center gap-2">
<a href="{{ route('inventory.items.edit', $item) }}" class="btn-secondary btn-sm">Edit</a>
<form action="{{ route('inventory.items.destroy', $item) }}" method="POST"
onsubmit="confirmDelete(this,'Delete this item?','This inventory item 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="8" class="px-4 py-8 text-center text-gray-400">No items found.</td>
</tr>
@endforelse
</tbody>
</table>
</div>
@if($items->hasPages())
<div class="mt-4">{{ $items->links() }}</div>
@endif
{{-- ═══════════ Import Modal ═══════════ --}}
<div id="import-modal"
class="hidden fixed inset-0 z-50 flex items-center justify-center"
style="background:rgba(0,0,0,.45);">
<div class="bg-white rounded-2xl shadow-xl w-full max-w-md mx-4" style="animation:fadeInUp .2s ease;">
<div class="flex items-center justify-between px-6 py-4 border-b border-slate-100">
<h2 class="text-base font-semibold text-slate-800">Import Items from Excel</h2>
<button onclick="document.getElementById('import-modal').classList.add('hidden')"
class="text-slate-400 hover:text-slate-600 transition-colors">
<svg width="18" height="18" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"/>
</svg>
</button>
</div>
<form action="{{ route('inventory.items.import') }}" method="POST" enctype="multipart/form-data" class="p-6">
@csrf
<label for="import-file"
id="drop-zone"
class="flex flex-col items-center justify-center gap-3 w-full border-2 border-dashed border-slate-300 rounded-xl p-8 cursor-pointer transition-colors hover:border-blue-400 hover:bg-blue-50"
ondragover="event.preventDefault(); this.classList.add('border-blue-500','bg-blue-50')"
ondragleave="this.classList.remove('border-blue-500','bg-blue-50')"
ondrop="itemHandleDrop(event)">
<svg width="36" height="36" fill="none" stroke="#94a3b8" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5"
d="M9 13h6M12 10v6m-7 4h14a2 2 0 002-2V7a2 2 0 00-2-2h-5.586a1 1 0 01-.707-.293l-1.414-1.414A1 1 0 0010.586 3H5a2 2 0 00-2 2v14a2 2 0 002 2z"/>
</svg>
<div class="text-center">
<p id="item-drop-label" class="text-sm font-medium text-slate-700">
Click to choose or drag & drop your file
</p>
<p class="text-xs text-slate-400 mt-1">Accepts: .xlsx, .xls — max 10 MB</p>
</div>
<input id="import-file" type="file" name="file" accept=".xlsx,.xls"
class="sr-only" onchange="itemUpdateLabel(this)">
</label>
<div class="mt-4 p-3 rounded-lg bg-amber-50 border border-amber-200 text-xs text-amber-700 flex gap-2">
<svg width="14" height="14" fill="none" stroke="currentColor" viewBox="0 0 24 24" class="flex-shrink-0 mt-0.5">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/>
</svg>
<span>
Supports the <strong>Forkoll inventory format</strong> and the
<strong>standard item template</strong>. Categories are auto-detected.
Items marked "Not in Use" are imported as inactive. Duplicate names are skipped.
</span>
</div>
<div class="flex items-center justify-end gap-3 mt-5">
<button type="button"
onclick="document.getElementById('import-modal').classList.add('hidden')"
class="btn-secondary">
Cancel
</button>
<button type="submit" class="btn-primary">
<svg width="14" height="14" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-8l-4-4m0 0L8 8m4-4v12"/>
</svg>
Import
</button>
</div>
</form>
</div>
</div>
<style>
@keyframes fadeInUp {
from { opacity:0; transform:translateY(12px); }
to { opacity:1; transform:translateY(0); }
}
</style>
<script>
function itemUpdateLabel(input) {
document.getElementById('item-drop-label').textContent =
input.files.length ? input.files[0].name : 'Click to choose or drag & drop your file';
}
function itemHandleDrop(e) {
e.preventDefault();
document.getElementById('drop-zone').classList.remove('border-blue-500', 'bg-blue-50');
const file = e.dataTransfer.files[0];
if (!file) return;
const input = document.getElementById('import-file');
const dt = new DataTransfer();
dt.items.add(file);
input.files = dt.files;
itemUpdateLabel(input);
}
document.getElementById('import-modal').addEventListener('click', function(e) {
if (e.target === this) this.classList.add('hidden');
});
</script>
@endsection
@@ -0,0 +1,149 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Inventory Items</title>
<style>
* { box-sizing: border-box; margin: 0; padding: 0; }
body { font-family: 'DejaVu Sans', Arial, sans-serif; font-size: 10.5px; color: #1e293b; background: #fff; }
.header {
display: flex; justify-content: space-between; align-items: flex-start;
padding: 22px 28px 14px; border-bottom: 2px solid #2563eb; margin-bottom: 18px;
}
.brand { display: flex; align-items: center; gap: 10px; }
.brand-box { width:34px;height:34px;border-radius:8px;background:#2563eb;display:flex;align-items:center;justify-content:center; }
.brand-name { font-size:16px;font-weight:700;color:#0f172a; }
.brand-sub { font-size:9px;color:#64748b;margin-top:1px; }
.report-info { text-align:right; }
.report-title { font-size:14px;font-weight:700;color:#1e293b; }
.report-meta { font-size:9px;color:#64748b;margin-top:2px; }
.summary { display:flex;gap:10px;margin:0 28px 16px; }
.sc { flex:1;padding:9px 12px;background:#f1f5f9;border-radius:8px;border-left:3px solid #2563eb; }
.sc .val { font-size:18px;font-weight:700;color:#2563eb; }
.sc .lbl { font-size:9px;color:#64748b;margin-top:1px; }
.sc.blue { border-color:#3b82f6; } .sc.blue .val { color:#3b82f6; }
.sc.green { border-color:#10b981; } .sc.green .val { color:#10b981; }
.sc.amber { border-color:#f59e0b; } .sc.amber .val { color:#f59e0b; }
.sc.gray { border-color:#94a3b8; } .sc.gray .val { color:#94a3b8; }
.table-wrap { padding: 0 28px; }
table { width:100%;border-collapse:collapse;font-size:10px; }
thead th {
background:#1e293b;color:#fff;font-weight:600;font-size:8.5px;
letter-spacing:.06em;text-transform:uppercase;padding:7px 9px;text-align:left;
}
tbody tr:nth-child(even) { background:#f8fafc; }
tbody td { padding:6px 9px;color:#334155;border-bottom:1px solid #e2e8f0;vertical-align:middle; }
tbody td:nth-child(2) { font-weight:600;color:#0f172a; }
.badge { display:inline-block;padding:2px 6px;border-radius:20px;font-size:8px;font-weight:700; }
.fg { background:#d1fae5;color:#065f46; }
.rm { background:#dbeafe;color:#1e40af; }
.wip { background:#fef3c7;color:#92400e; }
.act { background:#d1fae5;color:#065f46; }
.ina { background:#f1f5f9;color:#64748b; }
.section-row td {
background:#f8fafc;font-weight:700;font-size:9px;
color:#64748b;letter-spacing:.06em;text-transform:uppercase;
padding:5px 9px;border-bottom:1px solid #e2e8f0;
}
.footer { margin-top:20px;padding:10px 28px 0;border-top:1px solid #e2e8f0;display:flex;justify-content:space-between;font-size:9px;color:#94a3b8; }
</style>
</head>
<body>
<div class="header">
<div class="brand">
<div class="brand-box">
<svg width="17" height="17" fill="none" stroke="#fff" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M5 8h14M5 8a2 2 0 110-4h14a2 2 0 110 4M5 8v10a2 2 0 002 2h10a2 2 0 002-2V8m-9 4h4"/>
</svg>
</div>
<div>
<div class="brand-name">SteelERP</div>
<div class="brand-sub">Manufacturing &amp; Trading</div>
</div>
</div>
<div class="report-info">
<div class="report-title">Inventory Items List</div>
<div class="report-meta">Generated: {{ now()->format('d M Y, H:i') }}</div>
<div class="report-meta">Total Records: {{ $items->count() }}</div>
</div>
</div>
@php
$fg = $items->where('category', 'finished_good')->count();
$rm = $items->where('category', 'raw_material')->count();
$wip = $items->where('category', 'wip')->count();
$active = $items->where('is_active', true)->count();
$inactive= $items->where('is_active', false)->count();
$grouped = $items->groupBy('category');
$catLabels = ['finished_good' => 'Finished Goods', 'raw_material' => 'Raw Materials', 'wip' => 'Work In Progress'];
@endphp
<div class="summary">
<div class="sc"><div class="val">{{ $items->count() }}</div><div class="lbl">Total Items</div></div>
<div class="sc green"><div class="val">{{ $fg }}</div><div class="lbl">Finished Goods</div></div>
<div class="sc blue"><div class="val">{{ $rm }}</div><div class="lbl">Raw Materials</div></div>
<div class="sc amber"><div class="val">{{ $wip }}</div><div class="lbl">WIP</div></div>
<div class="sc gray"><div class="val">{{ $inactive }}</div><div class="lbl">Inactive</div></div>
</div>
<div class="table-wrap">
<table>
<thead>
<tr>
<th style="width:9%">Code</th>
<th style="width:32%">Item Name</th>
<th style="width:12%">Category</th>
<th style="width:7%">UOM</th>
<th style="width:10%;text-align:right">Min Stock</th>
<th style="width:10%;text-align:right">Cost Price</th>
<th style="width:8%">Status</th>
</tr>
</thead>
<tbody>
@foreach($grouped as $category => $catItems)
<tr class="section-row">
<td colspan="7">{{ $catLabels[$category] ?? ucfirst(str_replace('_', ' ', $category)) }}</td>
</tr>
@foreach($catItems as $i => $item)
<tr>
<td style="font-family:monospace;font-size:9px;color:#94a3b8;">{{ $item->item_code }}</td>
<td>{{ $item->item_name }}</td>
<td>
@if($item->category === 'finished_good') <span class="badge fg">Fin. Good</span>
@elseif($item->category === 'raw_material') <span class="badge rm">Raw Mat.</span>
@else <span class="badge wip">WIP</span>
@endif
</td>
<td>{{ $item->unit_of_measure }}</td>
<td style="text-align:right">{{ number_format($item->minimum_stock_level, 2) }}</td>
<td style="text-align:right">{{ number_format($item->cost_price, 4) }}</td>
<td>
@if($item->is_active) <span class="badge act">Active</span>
@else <span class="badge ina">Inactive</span>
@endif
</td>
</tr>
@endforeach
@endforeach
@if($items->isEmpty())
<tr><td colspan="7" style="text-align:center;padding:20px;color:#94a3b8;">No items found.</td></tr>
@endif
</tbody>
</table>
</div>
<div class="footer">
<span>SteelERP — Confidential</span>
<span>Exported by: {{ Auth::user()->name ?? '—' }}</span>
</div>
</body>
</html>
@@ -0,0 +1,77 @@
@extends('layouts.app')
@section('title', 'Manual Stock Adjustment')
@section('content')
<div class="mb-6">
<h1 class="page-title">Manual Stock Adjustment</h1>
<p class="page-subtitle"><a href="{{ route('inventory.movements.index') }}" class="text-blue-600 hover:underline">Stock Movements</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
<div class="card card-body max-w-xl">
<form action="{{ route('inventory.movements.store') }}" method="POST">
@csrf
<div class="grid grid-cols-1 gap-5 sm:grid-cols-2">
<div>
<label class="form-label">Item <span class="text-red-500">*</span></label>
<select name="item_id" required class="form-select">
<option value="">-- Select Item --</option>
@foreach($items as $item)
<option value="{{ $item->id }}" {{ old('item_id') == $item->id ? 'selected' : '' }}>
{{ $item->item_code }} - {{ $item->item_name }}
</option>
@endforeach
</select>
</div>
<div>
<label class="form-label">Warehouse <span class="text-red-500">*</span></label>
<select name="warehouse_id" required class="form-select">
<option value="">-- Select Warehouse --</option>
@foreach($warehouses as $warehouse)
<option value="{{ $warehouse->id }}" {{ old('warehouse_id') == $warehouse->id ? 'selected' : '' }}>
{{ $warehouse->name }}
</option>
@endforeach
</select>
</div>
<div>
<label class="form-label">Type <span class="text-red-500">*</span></label>
<select name="type" required class="form-select">
<option value="">-- Select Type --</option>
<option value="in" {{ old('type') === 'in' ? 'selected' : '' }}>IN (Stock In)</option>
<option value="out" {{ old('type') === 'out' ? 'selected' : '' }}>OUT (Stock Out)</option>
</select>
</div>
<div>
<label class="form-label">Quantity <span class="text-red-500">*</span></label>
<input type="number" name="quantity" value="{{ old('quantity') }}" min="0.01" step="0.01" required class="form-input">
</div>
<div class="sm:col-span-2">
<label class="form-label">Notes</label>
<textarea name="notes" rows="2" class="form-textarea">{{ old('notes') }}</textarea>
</div>
</div>
<div class="mt-6 flex items-center gap-3">
<button type="submit" class="btn-primary">Save Adjustment</button>
<a href="{{ route('inventory.movements.index') }}" class="btn-secondary">Cancel</a>
</div>
</form>
</div>
@endsection
@@ -0,0 +1,59 @@
@extends('layouts.app')
@section('title', 'Stock Movements')
@section('content')
<div class="page-header">
<div>
<h1 class="page-title">Stock Movements</h1>
<p class="page-subtitle">Track all inventory movements</p>
</div>
<a href="{{ route('inventory.movements.create') }}" class="btn-primary">
+ Manual Adjustment
</a>
</div>
<div class="table-wrapper overflow-x-auto">
<table class="table-base">
<thead>
<tr>
<th>Item</th>
<th>Warehouse</th>
<th>Type</th>
<th class="text-right">Quantity</th>
<th>Reference</th>
<th>Date</th>
</tr>
</thead>
<tbody>
@forelse($movements as $movement)
<tr>
<td class="text-gray-800">
<span class="font-mono text-xs text-gray-500">{{ $movement->item->item_code ?? '' }}</span>
<span class="ml-1">{{ $movement->item->item_name ?? '' }}</span>
</td>
<td class="text-gray-700">{{ $movement->warehouse->name ?? '' }}</td>
<td>
@if(strtolower($movement->type) === 'in')
<span class="badge-green">IN</span>
@else
<span class="badge-red">OUT</span>
@endif
</td>
<td class="text-right font-medium text-gray-800">{{ number_format($movement->quantity, 2) }}</td>
<td class="text-gray-500 text-xs">{{ $movement->reference ?? '-' }}</td>
<td>{{ $movement->created_at ? $movement->created_at->format('d M Y') : '' }}</td>
</tr>
@empty
<tr>
<td colspan="6" class="px-4 py-8 text-center text-gray-400">No movements recorded.</td>
</tr>
@endforelse
</tbody>
</table>
</div>
@if($movements->hasPages())
<div class="mt-4">{{ $movements->links() }}</div>
@endif
@endsection
@@ -0,0 +1,61 @@
@extends('layouts.app')
@section('title', 'Low Stock Report')
@section('content')
<div class="mb-6">
<h1 class="page-title">Low Stock Report</h1>
<p class="page-subtitle">Items currently below their minimum stock level</p>
</div>
@if(isset($stocks) && $stocks->count() > 0)
<div class="mb-4 px-4 py-3 bg-red-50 border border-red-200 rounded-lg text-sm text-red-700 font-medium">
{{ $stocks->count() }} item(s) are below minimum stock level. Immediate action may be required.
</div>
@endif
<div class="table-wrapper overflow-x-auto">
<table class="table-base">
<thead>
<tr>
<th>Item Code</th>
<th>Item Name</th>
<th>Category</th>
<th>Warehouse</th>
<th class="text-right">Current Qty</th>
<th class="text-right">Min Stock</th>
<th class="text-right">Shortage</th>
<th>Status</th>
</tr>
</thead>
<tbody>
@forelse($stocks as $stock)
@php $shortage = $stock->item->minimum_stock_level - $stock->quantity; @endphp
<tr class="bg-red-50">
<td class="font-mono text-red-700">{{ $stock->item->item_code ?? '' }}</td>
<td class="font-medium text-red-800">{{ $stock->item->item_name ?? '' }}</td>
<td>
@php
$catLabels = ['raw_material'=>'Raw Material','wip'=>'WIP','finished_good'=>'Finished Good'];
@endphp
<span class="text-gray-600">{{ $catLabels[$stock->item->category ?? ''] ?? ucfirst($stock->item->category ?? '') }}</span>
</td>
<td class="text-gray-600">{{ $stock->warehouse->name ?? '' }}</td>
<td class="text-right font-bold text-red-700">{{ number_format($stock->quantity, 2) }}</td>
<td class="text-right text-gray-600">{{ number_format($stock->item->minimum_stock_level ?? 0, 2) }}</td>
<td class="text-right font-semibold text-red-600">{{ number_format($shortage, 2) }}</td>
<td>
<span class="badge-red">LOW STOCK</span>
</td>
</tr>
@empty
<tr>
<td colspan="8" class="px-4 py-8 text-center text-green-600 font-medium">
All items are above minimum stock levels.
</td>
</tr>
@endforelse
</tbody>
</table>
</div>
@endsection
@@ -0,0 +1,80 @@
@extends('layouts.app')
@section('title', 'Movement Report')
@section('content')
<div class="mb-6">
<h1 class="page-title">Movement Report</h1>
<p class="page-subtitle">View stock movements within a date range</p>
</div>
<!-- Date Range Filter -->
<div class="card card-body mb-6">
<form method="GET" action="{{ route('inventory.reports.movement') }}" class="flex flex-wrap items-end gap-4">
<div>
<label class="form-label">From Date</label>
<input type="date" name="from_date" value="{{ request('from_date') }}" class="form-input">
</div>
<div>
<label class="form-label">To Date</label>
<input type="date" name="to_date" value="{{ request('to_date') }}" class="form-input">
</div>
<div>
<label class="form-label">Item (optional)</label>
<select name="item_id" class="form-select">
<option value="">All Items</option>
@foreach($items as $item)
<option value="{{ $item->id }}" {{ request('item_id') == $item->id ? 'selected' : '' }}>
{{ $item->item_code }} - {{ $item->item_name }}
</option>
@endforeach
</select>
</div>
<div>
<button type="submit" class="btn-primary">Filter</button>
</div>
</form>
</div>
<div class="table-wrapper overflow-x-auto">
<table class="table-base">
<thead>
<tr>
<th>Item</th>
<th>Warehouse</th>
<th>Type</th>
<th class="text-right">Quantity</th>
<th>Date</th>
</tr>
</thead>
<tbody>
@forelse($movements as $movement)
<tr>
<td class="text-gray-800">
<span class="font-mono text-xs text-gray-500">{{ $movement->item->item_code ?? '' }}</span>
<span class="ml-1">{{ $movement->item->item_name ?? '' }}</span>
</td>
<td class="text-gray-700">{{ $movement->warehouse->name ?? '' }}</td>
<td>
@if(strtolower($movement->type) === 'in')
<span class="badge-green">IN</span>
@else
<span class="badge-red">OUT</span>
@endif
</td>
<td class="text-right font-medium text-gray-800">{{ number_format($movement->quantity, 2) }}</td>
<td>{{ $movement->created_at ? $movement->created_at->format('d M Y') : '' }}</td>
</tr>
@empty
<tr>
<td colspan="5" class="px-4 py-8 text-center text-gray-400">No movements found for selected filters.</td>
</tr>
@endforelse
</tbody>
</table>
</div>
@if(isset($movements) && $movements->hasPages())
<div class="mt-4">{{ $movements->links() }}</div>
@endif
@endsection
@@ -0,0 +1,45 @@
@extends('layouts.app')
@section('title', 'Inventory Summary')
@section('content')
<div class="mb-6">
<h1 class="page-title">Inventory Summary</h1>
<p class="page-subtitle">Current stock levels across all warehouses</p>
</div>
<div class="table-wrapper overflow-x-auto">
<table class="table-base">
<thead>
<tr>
<th>Item Code</th>
<th>Item Name</th>
<th>Warehouse</th>
<th class="text-right">Quantity</th>
<th class="text-right">Min Stock</th>
</tr>
</thead>
<tbody>
@forelse($stocks as $stock)
@php $isLow = $stock->quantity < $stock->item->minimum_stock_level; @endphp
<tr class="{{ $isLow ? 'bg-red-50' : '' }}">
<td class="font-mono {{ $isLow ? 'text-red-700' : 'text-gray-700' }}">{{ $stock->item->item_code ?? '' }}</td>
<td class="font-medium {{ $isLow ? 'text-red-800' : 'text-gray-800' }}">
{{ $stock->item->item_name ?? '' }}
@if($isLow)
<span class="ml-2 px-1.5 py-0.5 text-xs font-bold rounded bg-red-200 text-red-800">LOW</span>
@endif
</td>
<td>{{ $stock->warehouse->name ?? '' }}</td>
<td class="text-right font-semibold {{ $isLow ? 'text-red-700' : 'text-gray-800' }}">{{ number_format($stock->quantity, 2) }}</td>
<td class="text-right text-gray-500">{{ number_format($stock->item->minimum_stock_level ?? 0, 2) }}</td>
</tr>
@empty
<tr>
<td colspan="5" class="px-4 py-8 text-center text-gray-400">No stock data available.</td>
</tr>
@endforelse
</tbody>
</table>
</div>
@endsection
@@ -0,0 +1,62 @@
@extends('layouts.app')
@section('title', 'Inventory Valuation')
@section('content')
<div class="mb-6">
<h1 class="page-title">Inventory Valuation</h1>
<p class="page-subtitle">Total value of current stock</p>
</div>
<div class="table-wrapper overflow-x-auto">
<table class="table-base">
<thead>
<tr>
<th>Item Code</th>
<th>Item Name</th>
<th>Category</th>
<th class="text-right">Total Qty</th>
<th class="text-right">Cost Price</th>
<th class="text-right">Total Value</th>
</tr>
</thead>
<tbody>
@forelse($valuations as $row)
<tr>
<td class="font-mono text-gray-700">{{ $row->item_code }}</td>
<td class="font-medium text-gray-800">{{ $row->item_name }}</td>
<td>
@php
$catBadgeClass = match($row->category) {
'raw_material' => 'badge-blue',
'wip' => 'badge-yellow',
'finished_good' => 'badge-green',
default => 'badge-gray',
};
$catLabels = ['raw_material'=>'Raw Material','wip'=>'WIP','finished_good'=>'Finished Good'];
@endphp
<span class="{{ $catBadgeClass }}">
{{ $catLabels[$row->category] ?? ucfirst($row->category) }}
</span>
</td>
<td class="text-right text-gray-700">{{ number_format($row->total_qty, 2) }}</td>
<td class="text-right text-gray-600">{{ number_format($row->cost_price, 2) }}</td>
<td class="text-right font-semibold text-gray-800">{{ number_format($row->total_value, 2) }}</td>
</tr>
@empty
<tr>
<td colspan="6" class="px-4 py-8 text-center text-gray-400">No valuation data available.</td>
</tr>
@endforelse
</tbody>
@if(isset($grandTotal))
<tfoot class="bg-blue-50 border-t-2 border-blue-200">
<tr>
<td colspan="5" class="px-4 py-3 text-right font-bold text-gray-800 text-sm">Grand Total</td>
<td class="px-4 py-3 text-right font-bold text-blue-700 text-base">{{ number_format($grandTotal, 2) }}</td>
</tr>
</tfoot>
@endif
</table>
</div>
@endsection
@@ -0,0 +1,62 @@
@extends('layouts.app')
@section('title', 'Add Warehouse')
@section('content')
<div class="mb-6">
<h1 class="page-title">Add Warehouse</h1>
<p class="page-subtitle"><a href="{{ route('inventory.warehouses.index') }}" class="text-blue-600 hover:underline">Warehouses</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
<div class="card card-body max-w-xl">
<form action="{{ route('inventory.warehouses.store') }}" method="POST">
@csrf
<div class="grid grid-cols-1 gap-5 sm:grid-cols-2">
<div>
<label class="form-label">Code <span class="text-red-500">*</span></label>
<input type="text" name="code" value="{{ old('code') }}" required
placeholder="e.g. WH-01" class="form-input">
</div>
<div>
<label class="form-label">Name <span class="text-red-500">*</span></label>
<input type="text" name="name" value="{{ old('name') }}" required class="form-input">
</div>
<div class="sm:col-span-2">
<label class="form-label">Location</label>
<input type="text" name="location" value="{{ old('location') }}" class="form-input">
</div>
<div class="sm:col-span-2">
<label class="form-label">Description</label>
<textarea name="description" rows="3" class="form-textarea">{{ old('description') }}</textarea>
</div>
<div class="sm:col-span-2 flex items-center gap-2">
<input type="checkbox" name="is_active" id="is_active" value="1"
{{ old('is_active', '1') ? 'checked' : '' }}
class="h-4 w-4 text-blue-600 border-gray-300 rounded">
<label for="is_active" class="form-label mb-0">Active</label>
</div>
</div>
<div class="mt-6 flex items-center gap-3">
<button type="submit" class="btn-primary">Save Warehouse</button>
<a href="{{ route('inventory.warehouses.index') }}" class="btn-secondary">Cancel</a>
</div>
</form>
</div>
@endsection
@@ -0,0 +1,62 @@
@extends('layouts.app')
@section('title', 'Edit Warehouse')
@section('content')
<div class="mb-6">
<h1 class="page-title">Edit Warehouse</h1>
<p class="page-subtitle"><a href="{{ route('inventory.warehouses.index') }}" class="text-blue-600 hover:underline">Warehouses</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-xl">
<form action="{{ route('inventory.warehouses.update', $warehouse) }}" method="POST">
@csrf
@method('PUT')
<div class="grid grid-cols-1 gap-5 sm:grid-cols-2">
<div>
<label class="form-label">Code <span class="text-red-500">*</span></label>
<input type="text" name="code" value="{{ old('code', $warehouse->code) }}" required class="form-input">
</div>
<div>
<label class="form-label">Name <span class="text-red-500">*</span></label>
<input type="text" name="name" value="{{ old('name', $warehouse->name) }}" required class="form-input">
</div>
<div class="sm:col-span-2">
<label class="form-label">Location</label>
<input type="text" name="location" value="{{ old('location', $warehouse->location) }}" class="form-input">
</div>
<div class="sm:col-span-2">
<label class="form-label">Description</label>
<textarea name="description" rows="3" class="form-textarea">{{ old('description', $warehouse->description) }}</textarea>
</div>
<div class="sm:col-span-2 flex items-center gap-2">
<input type="checkbox" name="is_active" id="is_active" value="1"
{{ old('is_active', $warehouse->is_active) ? 'checked' : '' }}
class="h-4 w-4 text-blue-600 border-gray-300 rounded">
<label for="is_active" class="form-label mb-0">Active</label>
</div>
</div>
<div class="mt-6 flex items-center gap-3">
<button type="submit" class="btn-primary">Update Warehouse</button>
<a href="{{ route('inventory.warehouses.index') }}" class="btn-secondary">Cancel</a>
</div>
</form>
</div>
@endsection
@@ -0,0 +1,64 @@
@extends('layouts.app')
@section('title', 'Warehouses')
@section('content')
<div class="page-header">
<div>
<h1 class="page-title">Warehouses</h1>
<p class="page-subtitle">Manage storage locations</p>
</div>
<a href="{{ route('inventory.warehouses.create') }}" class="btn-primary">
+ Add Warehouse
</a>
</div>
<div class="table-wrapper overflow-x-auto">
<table class="table-base">
<thead>
<tr>
<th>Code</th>
<th>Name</th>
<th>Location</th>
<th>Status</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
@forelse($warehouses as $warehouse)
<tr>
<td class="font-mono text-gray-700">{{ $warehouse->code }}</td>
<td class="font-medium text-gray-800">{{ $warehouse->name }}</td>
<td>{{ $warehouse->location }}</td>
<td>
@if($warehouse->is_active)
<span class="badge-green">Active</span>
@else
<span class="badge-gray">Inactive</span>
@endif
</td>
<td>
<div class="flex items-center gap-2">
<a href="{{ route('inventory.warehouses.edit', $warehouse) }}" class="btn-secondary btn-sm">Edit</a>
<form action="{{ route('inventory.warehouses.destroy', $warehouse) }}" method="POST"
onsubmit="confirmDelete(this,'Delete this warehouse?','This warehouse 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="5" class="px-4 py-8 text-center text-gray-400">No warehouses found.</td>
</tr>
@endforelse
</tbody>
</table>
</div>
@if($warehouses->hasPages())
<div class="mt-4">{{ $warehouses->links() }}</div>
@endif
@endsection
+440
View File
@@ -0,0 +1,440 @@
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="csrf-token" content="{{ csrf_token() }}">
<title>@yield('title', 'Dashboard') — SteelERP</title>
<link rel="preconnect" href="https://fonts.bunny.net">
<link href="https://fonts.bunny.net/css?family=inter:400,500,600,700&display=swap" rel="stylesheet"/>
@vite(['resources/css/app.css', 'resources/js/app.js'])
</head>
<body class="bg-slate-100 antialiased" style="font-family:'Inter',sans-serif;">
<div style="display:flex; min-height:100vh;">
{{-- ════════════════ SIDEBAR ════════════════ --}}
<aside id="sidebar" style="
width: 260px;
min-width: 260px;
background: #0f172a;
display: flex;
flex-direction: column;
position: fixed;
top: 0; left: 0; bottom: 0;
z-index: 50;
overflow-y: auto;
transition: transform .2s ease;
">
{{-- Logo --}}
<div style="padding:20px 20px 16px; border-bottom:1px solid #1e293b; flex-shrink:0;">
<div style="display:flex; align-items:center; gap:12px;">
<div style="width:36px;height:36px;border-radius:10px;background:#2563eb;display:flex;align-items:center;justify-content:center;flex-shrink:0;">
<svg width="20" height="20" fill="none" stroke="#fff" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-5m-9 0H3m2 0h5M9 7h1m-1 4h1m4-4h1m-1 4h1m-5 10v-5a1 1 0 011-1h2a1 1 0 011 1v5m-4 0h4"/>
</svg>
</div>
<div>
<div style="color:#fff;font-weight:700;font-size:15px;line-height:1.2;">SteelERP</div>
<div style="color:#64748b;font-size:11px;margin-top:1px;">Manufacturing & Trading</div>
</div>
</div>
</div>
{{-- Nav --}}
<nav style="padding:12px 12px; flex:1;">
{{-- Dashboard --}}
<a href="{{ route('dashboard') }}" style="
display:flex; align-items:center; gap:10px;
padding:8px 12px; border-radius:8px; margin-bottom:2px;
font-size:13.5px; font-weight:500; text-decoration:none;
{{ request()->routeIs('dashboard') ? 'background:#2563eb;color:#fff;' : 'color:#94a3b8;' }}
transition:background .15s,color .15s;
" onmouseover="if(!this.style.background.includes('2563eb'))this.style.background='#1e293b'" onmouseout="if(!this.style.background.includes('2563eb'))this.style.background=''">
<svg width="16" height="16" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 5a1 1 0 011-1h4a1 1 0 011 1v5a1 1 0 01-1 1H5a1 1 0 01-1-1V5zM4 13a1 1 0 011-1h4a1 1 0 011 1v6a1 1 0 01-1 1H5a1 1 0 01-1-1v-6zM14 5a1 1 0 011-1h4a1 1 0 011 1v2a1 1 0 01-1 1h-4a1 1 0 01-1-1V5zM14 11a1 1 0 011-1h4a1 1 0 011 1v8a1 1 0 01-1 1h-4a1 1 0 01-1-1v-8z"/>
</svg>
Dashboard
</a>
{{-- ── PURCHASE ── --}}
<div style="margin-top:16px; margin-bottom:4px; padding:0 12px;">
<span style="font-size:10px;font-weight:600;letter-spacing:.08em;text-transform:uppercase;color:#f59e0b;display:flex;align-items:center;gap:6px;">
<svg width="12" height="12" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M16 11V7a4 4 0 00-8 0v4M5 9h14l1 12H4L5 9z"/></svg>
Purchase
</span>
</div>
@php
$navLink = function(string $route, string $label, string $icon = '') use (&$navLink) { return ''; };
@endphp
{{-- Pipeline top-level link --}}
<a href="{{ route('purchase.pipeline.index') }}" style="
display:flex; align-items:center; gap:8px;
padding:7px 12px 7px 14px; border-radius:7px; margin-bottom:4px;
font-size:13px; text-decoration:none; font-weight:600;
{{ request()->routeIs('purchase.pipeline.*') ? 'background:#2563eb;color:#fff;' : 'color:#fbbf24;' }}
" onmouseover="if(!this.style.background.includes('2563eb'))this.style.background='#1e293b'" onmouseout="if(!this.style.background.includes('2563eb'))this.style.background=''">
<svg width="14" height="14" fill="none" stroke="currentColor" viewBox="0 0 24 24" style="flex-shrink:0;">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2m-6 9l2 2 4-4"/>
</svg>
Pipeline
</a>
@foreach([
['purchase.suppliers.index', 'Suppliers'],
['purchase.requests.index', 'Purchase Requests'],
['purchase.orders.index', 'Purchase Orders'],
['purchase.grns.index', 'Goods Receipt (GRN)'],
['purchase.invoices.index', 'Supplier Invoices'],
['purchase.payments.index', 'Payments'],
] as [$routeName, $label])
<a href="{{ route($routeName) }}" style="
display:block; padding:7px 12px 7px 24px; border-radius:7px; margin-bottom:1px;
font-size:13px; text-decoration:none;
{{ request()->routeIs(str_replace('.index','',$routeName).'*') ? 'background:#1e293b;color:#fff;font-weight:500;' : 'color:#94a3b8;' }}
" onmouseover="if(!this.style.color.includes('fff'))this.style.color='#e2e8f0'" onmouseout="if(!this.style.background.includes('1e293b'))this.style.color='#94a3b8'">
{{ $label }}
</a>
@endforeach
{{-- ── INVENTORY ── --}}
<div style="margin-top:16px; margin-bottom:4px; padding:0 12px;">
<span style="font-size:10px;font-weight:600;letter-spacing:.08em;text-transform:uppercase;color:#10b981;display:flex;align-items:center;gap:6px;">
<svg width="12" height="12" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 8h14M5 8a2 2 0 110-4h14a2 2 0 110 4M5 8v10a2 2 0 002 2h10a2 2 0 002-2V8m-9 4h4"/></svg>
Inventory
</span>
</div>
@foreach([
['inventory.items.index', 'Items'],
['inventory.warehouses.index', 'Warehouses'],
['inventory.movements.index', 'Stock Movements'],
['inventory.reports.summary', 'Stock Summary'],
['inventory.reports.movement', 'Movement Report'],
['inventory.reports.low-stock', 'Low Stock Alert'],
['inventory.reports.valuation', 'Valuation'],
] as [$routeName, $label])
<a href="{{ route($routeName) }}" style="
display:block; padding:7px 12px 7px 24px; border-radius:7px; margin-bottom:1px;
font-size:13px; text-decoration:none;
{{ request()->routeIs(rtrim($routeName,'y').'*') || request()->routeIs($routeName) ? 'background:#1e293b;color:#fff;font-weight:500;' : 'color:#94a3b8;' }}
" onmouseover="if(!this.style.color.includes('fff'))this.style.color='#e2e8f0'" onmouseout="if(!this.style.background.includes('1e293b'))this.style.color='#94a3b8'">
{{ $label }}
</a>
@endforeach
{{-- ── PRODUCTION ── --}}
<div style="margin-top:16px; margin-bottom:4px; padding:0 12px;">
<span style="font-size:10px;font-weight:600;letter-spacing:.08em;text-transform:uppercase;color:#f97316;display:flex;align-items:center;gap:6px;">
<svg width="12" height="12" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z"/><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/></svg>
Production
</span>
</div>
@foreach([
['production.orders.index', 'Production Orders'],
['production.bom.index', 'Bill of Materials'],
['production.material-issues.index', 'Material Issues'],
['production.outputs.index', 'Production Output'],
] as [$routeName, $label])
<a href="{{ route($routeName) }}" style="
display:block; padding:7px 12px 7px 24px; border-radius:7px; margin-bottom:1px;
font-size:13px; text-decoration:none;
{{ request()->routeIs(str_replace('.index','',$routeName).'*') ? 'background:#1e293b;color:#fff;font-weight:500;' : 'color:#94a3b8;' }}
" onmouseover="if(!this.style.color.includes('fff'))this.style.color='#e2e8f0'" onmouseout="if(!this.style.background.includes('1e293b'))this.style.color='#94a3b8'">
{{ $label }}
</a>
@endforeach
{{-- ── SALES ── --}}
<div style="margin-top:16px; margin-bottom:4px; padding:0 12px;">
<span style="font-size:10px;font-weight:600;letter-spacing:.08em;text-transform:uppercase;color:#a78bfa;display:flex;align-items:center;gap:6px;">
<svg width="12" height="12" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"/></svg>
Sales
</span>
</div>
@foreach([
['sales.customers.index', 'Customers'],
['sales.orders.index', 'Sales Orders'],
['sales.delivery-notes.index', 'Delivery Notes'],
['sales.invoices.index', 'Sales Invoices'],
['sales.payments.index', 'Payment Receipts'],
] as [$routeName, $label])
<a href="{{ route($routeName) }}" style="
display:block; padding:7px 12px 7px 24px; border-radius:7px; margin-bottom:1px;
font-size:13px; text-decoration:none;
{{ request()->routeIs(str_replace('.index','',$routeName).'*') ? 'background:#1e293b;color:#fff;font-weight:500;' : 'color:#94a3b8;' }}
" onmouseover="if(!this.style.color.includes('fff'))this.style.color='#e2e8f0'" onmouseout="if(!this.style.background.includes('1e293b'))this.style.color='#94a3b8'">
{{ $label }}
</a>
@endforeach
<div style="height:16px;"></div>
</nav>
{{-- User footer --}}
<div style="padding:12px; border-top:1px solid #1e293b; flex-shrink:0;">
<div style="display:flex; align-items:center; gap:10px; padding:8px 10px; border-radius:8px; background:#1e293b;">
<div style="width:32px;height:32px;border-radius:50%;background:#2563eb;display:flex;align-items:center;justify-content:center;flex-shrink:0;font-size:13px;font-weight:700;color:#fff;">
{{ strtoupper(substr(Auth::user()->name ?? 'U', 0, 1)) }}
</div>
<div style="flex:1;min-width:0;">
<div style="color:#e2e8f0;font-size:12.5px;font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;">{{ Auth::user()->name ?? 'User' }}</div>
<div style="color:#64748b;font-size:11px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;">{{ Auth::user()->email ?? '' }}</div>
</div>
<form method="POST" action="{{ route('logout') }}">
@csrf
<button type="submit" title="Sign out" style="background:none;border:none;cursor:pointer;color:#64748b;padding:4px;" onmouseover="this.style.color='#f87171'" onmouseout="this.style.color='#64748b'">
<svg width="15" height="15" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 16l4-4m0 0l-4-4m4 4H7m6 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h4a3 3 0 013 3v1"/>
</svg>
</button>
</form>
</div>
</div>
</aside>
{{-- Mobile overlay --}}
<div id="sidebar-overlay" style="display:none;position:fixed;inset:0;background:rgba(0,0,0,.5);z-index:40;"></div>
{{-- ════════════════ MAIN AREA ════════════════ --}}
<div style="flex:1; margin-left:260px; display:flex; flex-direction:column; min-height:100vh;">
{{-- Top bar --}}
<header style="
height:60px; background:#fff; border-bottom:1px solid #e2e8f0;
display:flex; align-items:center; gap:16px; padding:0 28px;
position:sticky; top:0; z-index:30; flex-shrink:0;
box-shadow:0 1px 3px rgba(0,0,0,.06);
">
<button id="sidebar-toggle" style="display:none; background:none; border:none; cursor:pointer; color:#64748b; padding:4px;" onclick="toggleSidebar()">
<svg width="20" height="20" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16"/>
</svg>
</button>
<div style="flex:1;">
<span style="font-size:14px;font-weight:600;color:#1e293b;">@yield('title', 'Dashboard')</span>
</div>
<div style="display:flex;align-items:center;gap:16px;">
<span style="font-size:12px;color:#94a3b8;">{{ now()->format('l, d M Y') }}</span>
<div style="width:1px;height:20px;background:#e2e8f0;"></div>
<div style="display:flex;align-items:center;gap:8px;">
<div style="width:32px;height:32px;border-radius:50%;background:#2563eb;display:flex;align-items:center;justify-content:center;font-size:13px;font-weight:700;color:#fff;">
{{ strtoupper(substr(Auth::user()->name ?? 'U', 0, 1)) }}
</div>
<span style="font-size:13px;color:#475569;font-weight:500;">{{ Auth::user()->name ?? 'User' }}</span>
</div>
</div>
</header>
{{-- Content --}}
<main style="flex:1; padding:28px; background:#f1f5f9;">
@yield('content')
</main>
</div>
</div>
{{-- ── Toast notification container ── --}}
<div id="toast-stack" style="position:fixed;bottom:24px;right:24px;z-index:9999;
display:flex;flex-direction:column;gap:10px;pointer-events:none;"></div>
<style>
@keyframes toastIn {
from { opacity:0; transform:translateX(40px) scale(.95); }
to { opacity:1; transform:translateX(0) scale(1); }
}
@keyframes toastOut {
from { opacity:1; transform:translateX(0) scale(1); max-height:80px; margin:0; }
to { opacity:0; transform:translateX(40px) scale(.95); max-height:0; margin:0; }
}
.toast {
display:flex;align-items:flex-start;gap:10px;
padding:13px 16px;border-radius:12px;
font-size:13px;font-weight:500;line-height:1.4;
box-shadow:0 8px 24px rgba(0,0,0,.12),0 2px 6px rgba(0,0,0,.06);
pointer-events:all;cursor:pointer;max-width:340px;min-width:240px;
animation:toastIn .3s cubic-bezier(.34,1.56,.64,1) forwards;
border:1px solid transparent;
position:relative;overflow:hidden;
}
.toast::after {
content:'';position:absolute;bottom:0;left:0;height:3px;
background:currentColor;opacity:.25;
animation:toastProgress linear forwards;
}
@keyframes toastProgress {
from { width:100%; }
to { width:0%; }
}
.toast-success { background:#f0fdf4;border-color:#bbf7d0;color:#15803d; }
.toast-error { background:#fef2f2;border-color:#fecaca;color:#dc2626; }
.toast-info { background:#eff6ff;border-color:#bfdbfe;color:#1d4ed8; }
.toast-warn { background:#fffbeb;border-color:#fde68a;color:#92400e; }
</style>
<script>
// ── Toast system ─────────────────────────────────────────
function showToast(message, type, duration) {
type = type || 'success';
duration = duration || 4000;
var stack = document.getElementById('toast-stack');
var icons = {
success: '<svg width="16" height="16" fill="currentColor" viewBox="0 0 20 20" style="flex-shrink:0;margin-top:1px;"><path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"/></svg>',
error: '<svg width="16" height="16" fill="currentColor" viewBox="0 0 20 20" style="flex-shrink:0;margin-top:1px;"><path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7 4a1 1 0 11-2 0 1 1 0 012 0zm-1-9a1 1 0 00-1 1v4a1 1 0 102 0V6a1 1 0 00-1-1z" clip-rule="evenodd"/></svg>',
info: '<svg width="16" height="16" fill="currentColor" viewBox="0 0 20 20" style="flex-shrink:0;margin-top:1px;"><path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z" clip-rule="evenodd"/></svg>',
warn: '<svg width="16" height="16" fill="currentColor" viewBox="0 0 20 20" style="flex-shrink:0;margin-top:1px;"><path fill-rule="evenodd" d="M8.257 3.099c.765-1.36 2.722-1.36 3.486 0l5.58 9.92c.75 1.334-.213 2.98-1.742 2.98H4.42c-1.53 0-2.493-1.646-1.743-2.98l5.58-9.92zM11 13a1 1 0 11-2 0 1 1 0 012 0zm-1-8a1 1 0 00-1 1v3a1 1 0 002 0V6a1 1 0 00-1-1z" clip-rule="evenodd"/></svg>',
};
var t = document.createElement('div');
t.className = 'toast toast-' + type;
t.style.setProperty('--dur', duration + 'ms');
t.innerHTML = icons[type] + '<span style="flex:1;">' + message + '</span>'
+ '<button onclick="dismissToast(this.parentNode)" style="background:none;border:none;cursor:pointer;'
+ 'color:currentColor;opacity:.5;padding:0;margin-left:4px;font-size:16px;line-height:1;'
+ 'flex-shrink:0;" onmouseover="this.style.opacity=1" onmouseout="this.style.opacity=.5">×</button>';
t.querySelector('button').addEventListener('click', function(e) { e.stopPropagation(); });
t.style.setProperty('--dur', duration + 'ms');
t.querySelector('::after'); // trigger
t.style.cssText += ';--dur:' + duration + 'ms;';
// Apply progress bar duration via inline style on ::after via a workaround:
t.addEventListener('animationend', function(e) {
if (e.animationName === 'toastIn') {
t.style.setProperty('animation', 'none'); // freeze in place
}
});
// Progress bar via a child div
var bar = document.createElement('div');
bar.style.cssText = 'position:absolute;bottom:0;left:0;height:3px;background:currentColor;'
+ 'opacity:.25;width:100%;transition:width ' + duration + 'ms linear;border-radius:0 0 12px 12px;';
t.appendChild(bar);
stack.appendChild(t);
// Trigger progress bar shrink
requestAnimationFrame(function() { requestAnimationFrame(function() { bar.style.width = '0%'; }); });
var timer = setTimeout(function() { dismissToast(t); }, duration);
t.addEventListener('click', function() { clearTimeout(timer); dismissToast(t); });
return t;
}
function dismissToast(el) {
if (!el || el._dismissing) return;
el._dismissing = true;
el.style.animation = 'toastOut .25s ease forwards';
setTimeout(function() { if (el.parentNode) el.parentNode.removeChild(el); }, 240);
}
// ── Fire session flash messages as toasts ───────────────
(function() {
@if(session('success'))
showToast(@json(session('success')), 'success');
@endif
@if(session('error'))
showToast(@json(session('error')), 'error');
@endif
@if(session('info'))
showToast(@json(session('info')), 'info');
@endif
@if(session('warning'))
showToast(@json(session('warning')), 'warn');
@endif
})();
</script>
{{-- ── Global delete confirmation modal ── --}}
<div id="global-delete-modal" style="display:none;position:fixed;inset:0;z-index:9000;
background:rgba(15,23,42,.6);backdrop-filter:blur(5px);
align-items:center;justify-content:center;padding:20px;">
<div style="background:#fff;border-radius:18px;width:100%;max-width:380px;overflow:hidden;
box-shadow:0 24px 60px rgba(0,0,0,.25);animation:toastIn .22s cubic-bezier(.34,1.56,.64,1);">
<div style="height:3px;background:linear-gradient(90deg,#dc2626,#ef4444,#f97316);"></div>
<div style="padding:22px 22px 8px;display:flex;align-items:flex-start;gap:14px;">
<div style="width:40px;height:40px;border-radius:12px;background:#fef2f2;display:flex;
align-items:center;justify-content:center;flex-shrink:0;">
<svg width="20" height="20" fill="none" stroke="#dc2626" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6M1 7h22M8 7V4a1 1 0 011-1h6a1 1 0 011 1v3"/>
</svg>
</div>
<div>
<div style="font-size:15px;font-weight:700;color:#0f172a;margin-bottom:4px;" id="gdm-title">Delete?</div>
<div style="font-size:13px;color:#64748b;line-height:1.5;" id="gdm-body">This action cannot be undone.</div>
</div>
</div>
<div style="padding:16px 22px 20px;display:flex;gap:8px;justify-content:flex-end;">
<button onclick="closeGlobalDeleteModal()"
style="padding:9px 18px;border-radius:9px;border:1.5px solid #e2e8f0;background:#fff;
font-size:13px;font-weight:600;color:#475569;cursor:pointer;">
Cancel
</button>
<button id="gdm-confirm-btn"
style="padding:9px 18px;border-radius:9px;border:none;background:#dc2626;
font-size:13px;font-weight:600;color:#fff;cursor:pointer;
box-shadow:0 3px 10px rgba(220,38,38,.35);">
Delete
</button>
</div>
</div>
</div>
<script>
// ── Global delete confirmation modal ────────────────────
var _gdmForm = null;
function confirmDelete(form, title, body) {
_gdmForm = form;
document.getElementById('gdm-title').textContent = title || 'Delete this record?';
document.getElementById('gdm-body').textContent = body || 'This action cannot be undone.';
var modal = document.getElementById('global-delete-modal');
modal.style.display = 'flex';
document.getElementById('gdm-confirm-btn').onclick = function() {
closeGlobalDeleteModal();
_gdmForm.submit();
};
}
function closeGlobalDeleteModal() {
document.getElementById('global-delete-modal').style.display = 'none';
_gdmForm = null;
}
document.getElementById('global-delete-modal').addEventListener('click', function(e) {
if (e.target === this) closeGlobalDeleteModal();
});
document.addEventListener('keydown', function(e) {
if (e.key === 'Escape') closeGlobalDeleteModal();
});
</script>
<script>
function toggleSidebar() {
var s = document.getElementById('sidebar');
var o = document.getElementById('sidebar-overlay');
var open = s.style.transform === '';
s.style.transform = open ? 'translateX(-260px)' : '';
o.style.display = open ? 'none' : 'block';
}
document.getElementById('sidebar-overlay').addEventListener('click', function() {
document.getElementById('sidebar').style.transform = 'translateX(-260px)';
this.style.display = 'none';
});
// Show mobile toggle on small screens
if (window.innerWidth < 1024) {
document.getElementById('sidebar').style.transform = 'translateX(-260px)';
document.getElementById('sidebar-toggle').style.display = 'block';
}
window.addEventListener('resize', function() {
if (window.innerWidth >= 1024) {
document.getElementById('sidebar').style.transform = '';
document.getElementById('sidebar').style.position = 'fixed';
document.getElementById('sidebar-toggle').style.display = 'none';
document.getElementById('sidebar-overlay').style.display = 'none';
} else {
document.getElementById('sidebar-toggle').style.display = 'block';
}
});
</script>
</body>
</html>
+30
View File
@@ -0,0 +1,30 @@
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="csrf-token" content="{{ csrf_token() }}">
<title>{{ config('app.name', 'Laravel') }}</title>
<!-- Fonts -->
<link rel="preconnect" href="https://fonts.bunny.net">
<link href="https://fonts.bunny.net/css?family=figtree:400,500,600&display=swap" rel="stylesheet" />
<!-- Scripts -->
@vite(['resources/css/app.css', 'resources/js/app.js'])
</head>
<body class="font-sans text-gray-900 antialiased">
<div class="min-h-screen flex flex-col sm:justify-center items-center pt-6 sm:pt-0 bg-gray-100">
<div>
<a href="/">
<x-application-logo class="w-20 h-20 fill-current text-gray-500" />
</a>
</div>
<div class="w-full sm:max-w-md mt-6 px-6 py-4 bg-white shadow-md overflow-hidden sm:rounded-lg">
{{ $slot }}
</div>
</div>
</body>
</html>
@@ -0,0 +1,100 @@
<nav x-data="{ open: false }" class="bg-white border-b border-gray-100">
<!-- Primary Navigation Menu -->
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="flex justify-between h-16">
<div class="flex">
<!-- Logo -->
<div class="shrink-0 flex items-center">
<a href="{{ route('dashboard') }}">
<x-application-logo class="block h-9 w-auto fill-current text-gray-800" />
</a>
</div>
<!-- Navigation Links -->
<div class="hidden space-x-8 sm:-my-px sm:ms-10 sm:flex">
<x-nav-link :href="route('dashboard')" :active="request()->routeIs('dashboard')">
{{ __('Dashboard') }}
</x-nav-link>
</div>
</div>
<!-- Settings Dropdown -->
<div class="hidden sm:flex sm:items-center sm:ms-6">
<x-dropdown align="right" width="48">
<x-slot name="trigger">
<button class="inline-flex items-center px-3 py-2 border border-transparent text-sm leading-4 font-medium rounded-md text-gray-500 bg-white hover:text-gray-700 focus:outline-none transition ease-in-out duration-150">
<div>{{ Auth::user()->name }}</div>
<div class="ms-1">
<svg class="fill-current h-4 w-4" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" clip-rule="evenodd" />
</svg>
</div>
</button>
</x-slot>
<x-slot name="content">
<x-dropdown-link :href="route('profile.edit')">
{{ __('Profile') }}
</x-dropdown-link>
<!-- Authentication -->
<form method="POST" action="{{ route('logout') }}">
@csrf
<x-dropdown-link :href="route('logout')"
onclick="event.preventDefault();
this.closest('form').submit();">
{{ __('Log Out') }}
</x-dropdown-link>
</form>
</x-slot>
</x-dropdown>
</div>
<!-- Hamburger -->
<div class="-me-2 flex items-center sm:hidden">
<button @click="open = ! open" class="inline-flex items-center justify-center p-2 rounded-md text-gray-400 hover:text-gray-500 hover:bg-gray-100 focus:outline-none focus:bg-gray-100 focus:text-gray-500 transition duration-150 ease-in-out">
<svg class="h-6 w-6" stroke="currentColor" fill="none" viewBox="0 0 24 24">
<path :class="{'hidden': open, 'inline-flex': ! open }" class="inline-flex" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16" />
<path :class="{'hidden': ! open, 'inline-flex': open }" class="hidden" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
</div>
</div>
</div>
<!-- Responsive Navigation Menu -->
<div :class="{'block': open, 'hidden': ! open}" class="hidden sm:hidden">
<div class="pt-2 pb-3 space-y-1">
<x-responsive-nav-link :href="route('dashboard')" :active="request()->routeIs('dashboard')">
{{ __('Dashboard') }}
</x-responsive-nav-link>
</div>
<!-- Responsive Settings Options -->
<div class="pt-4 pb-1 border-t border-gray-200">
<div class="px-4">
<div class="font-medium text-base text-gray-800">{{ Auth::user()->name }}</div>
<div class="font-medium text-sm text-gray-500">{{ Auth::user()->email }}</div>
</div>
<div class="mt-3 space-y-1">
<x-responsive-nav-link :href="route('profile.edit')">
{{ __('Profile') }}
</x-responsive-nav-link>
<!-- Authentication -->
<form method="POST" action="{{ route('logout') }}">
@csrf
<x-responsive-nav-link :href="route('logout')"
onclick="event.preventDefault();
this.closest('form').submit();">
{{ __('Log Out') }}
</x-responsive-nav-link>
</form>
</div>
</div>
</div>
</nav>
@@ -0,0 +1,40 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Quote Request</title>
</head>
<body style="margin:0;padding:0;background:#f1f5f9;font-family:system-ui,-apple-system,sans-serif;">
<div style="max-width:520px;margin:32px auto;background:#fff;border-radius:14px;overflow:hidden;box-shadow:0 4px 20px rgba(0,0,0,.08);">
<!-- Header bar -->
<div style="background:linear-gradient(135deg,#2563eb,#1d4ed8);padding:28px 32px;">
<div style="font-size:12px;font-weight:600;color:rgba(255,255,255,.7);text-transform:uppercase;letter-spacing:.06em;margin-bottom:6px;">Quote Request</div>
<div style="font-size:22px;font-weight:700;color:#fff;">{{ $invitation->purchaseRequest->request_number }}</div>
@if($invitation->purchaseRequest->project_name)
<div style="font-size:13px;color:rgba(255,255,255,.8);margin-top:4px;">{{ $invitation->purchaseRequest->project_name }}</div>
@endif
</div>
<!-- Body -->
<div style="padding:32px;">
<p style="font-size:15px;color:#334155;margin:0 0 12px;">Dear <strong>{{ $invitation->supplier->name }}</strong>,</p>
<p style="font-size:14px;color:#475569;line-height:1.65;margin:0 0 28px;">
You have been invited to submit a price quotation. Please click the button below to view the required items and submit your quote. The link is private to your company and expires in 7 days.
</p>
<a href="{{ route('rfq.show', $invitation->token) }}"
style="display:inline-block;background:#2563eb;color:#fff;text-decoration:none;padding:13px 32px;border-radius:9px;font-size:14px;font-weight:700;letter-spacing:.02em;">
Submit My Quote →
</a>
<p style="font-size:12px;color:#94a3b8;margin:28px 0 0;">
This link can only be submitted once and expires on <strong>{{ $invitation->expires_at->format('d M Y') }}</strong>.
If the button doesn't work, copy this URL into your browser:<br>
<span style="color:#2563eb;word-break:break-all;">{{ route('rfq.show', $invitation->token) }}</span>
</p>
</div>
</div>
</body>
</html>
@@ -0,0 +1,69 @@
@extends('layouts.app')
@section('title', 'Add BOM Entry')
@section('content')
<div class="mb-6">
<h1 class="page-title">Add BOM Entry</h1>
<p class="page-subtitle"><a href="{{ route('production.bom.index') }}" class="text-blue-600 hover:underline">Bill of Materials</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
<div class="card card-body max-w-xl">
<form action="{{ route('production.bom.store') }}" method="POST">
@csrf
<div class="grid grid-cols-1 gap-5 sm:grid-cols-2">
<div class="sm:col-span-2">
<label class="form-label">Product (Finished Good) <span class="text-red-500">*</span></label>
<select name="product_id" required class="form-select">
<option value="">-- Select Product --</option>
@foreach($products as $product)
<option value="{{ $product->id }}" {{ old('product_id') == $product->id ? 'selected' : '' }}>
{{ $product->item_code }} - {{ $product->item_name }}
</option>
@endforeach
</select>
</div>
<div class="sm:col-span-2">
<label class="form-label">Raw Material <span class="text-red-500">*</span></label>
<select name="raw_material_id" required class="form-select">
<option value="">-- Select Raw Material --</option>
@foreach($rawMaterials as $material)
<option value="{{ $material->id }}" {{ old('raw_material_id') == $material->id ? 'selected' : '' }}>
{{ $material->item_code }} - {{ $material->item_name }}
</option>
@endforeach
</select>
</div>
<div>
<label class="form-label">Quantity Required <span class="text-red-500">*</span></label>
<input type="number" name="quantity_required" value="{{ old('quantity_required') }}" min="0.001" step="0.001" required class="form-input">
</div>
<div>
<label class="form-label">Unit of Measure <span class="text-red-500">*</span></label>
<input type="text" name="unit_of_measure" value="{{ old('unit_of_measure') }}" required
placeholder="e.g. KG, PCS" class="form-input">
</div>
</div>
<div class="mt-6 flex items-center gap-3">
<button type="submit" class="btn-primary">Save BOM Entry</button>
<a href="{{ route('production.bom.index') }}" class="btn-secondary">Cancel</a>
</div>
</form>
</div>
@endsection
@@ -0,0 +1,69 @@
@extends('layouts.app')
@section('title', 'Edit BOM Entry')
@section('content')
<div class="mb-6">
<h1 class="page-title">Edit BOM Entry</h1>
<p class="page-subtitle"><a href="{{ route('production.bom.index') }}" class="text-blue-600 hover:underline">Bill of Materials</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-xl">
<form action="{{ route('production.bom.update', $bom) }}" method="POST">
@csrf
@method('PUT')
<div class="grid grid-cols-1 gap-5 sm:grid-cols-2">
<div class="sm:col-span-2">
<label class="form-label">Product (Finished Good) <span class="text-red-500">*</span></label>
<select name="product_id" required class="form-select">
<option value="">-- Select Product --</option>
@foreach($products as $product)
<option value="{{ $product->id }}" {{ old('product_id', $bom->product_id) == $product->id ? 'selected' : '' }}>
{{ $product->item_code }} - {{ $product->item_name }}
</option>
@endforeach
</select>
</div>
<div class="sm:col-span-2">
<label class="form-label">Raw Material <span class="text-red-500">*</span></label>
<select name="raw_material_id" required class="form-select">
<option value="">-- Select Raw Material --</option>
@foreach($rawMaterials as $material)
<option value="{{ $material->id }}" {{ old('raw_material_id', $bom->raw_material_id) == $material->id ? 'selected' : '' }}>
{{ $material->item_code }} - {{ $material->item_name }}
</option>
@endforeach
</select>
</div>
<div>
<label class="form-label">Quantity Required <span class="text-red-500">*</span></label>
<input type="number" name="quantity_required" value="{{ old('quantity_required', $bom->quantity_required) }}" min="0.001" step="0.001" required class="form-input">
</div>
<div>
<label class="form-label">Unit of Measure <span class="text-red-500">*</span></label>
<input type="text" name="unit_of_measure" value="{{ old('unit_of_measure', $bom->unit_of_measure) }}" required class="form-input">
</div>
</div>
<div class="mt-6 flex items-center gap-3">
<button type="submit" class="btn-primary">Update BOM Entry</button>
<a href="{{ route('production.bom.index') }}" class="btn-secondary">Cancel</a>
</div>
</form>
</div>
@endsection
@@ -0,0 +1,65 @@
@extends('layouts.app')
@section('title', 'Bill of Materials')
@section('content')
<div class="page-header">
<div>
<h1 class="page-title">Bill of Materials</h1>
<p class="page-subtitle">Define material requirements for each product</p>
</div>
<a href="{{ route('production.bom.create') }}" class="btn-primary">
+ Add BOM Entry
</a>
</div>
@php $currentProduct = null; @endphp
@forelse($bomEntries as $entry)
@if($currentProduct !== ($entry->product->item_name ?? ''))
@if($currentProduct !== null)
</tbody></table></div>
@endif
@php $currentProduct = $entry->product->item_name ?? ''; @endphp
<div class="table-wrapper overflow-hidden mb-4">
<div class="px-6 py-3 bg-blue-50 border-b border-blue-100 flex items-center justify-between">
<h3 class="font-semibold text-blue-800 text-sm">{{ $currentProduct }}</h3>
<span class="text-xs text-blue-500">{{ $entry->product->item_code ?? '' }}</span>
</div>
<table class="table-base">
<thead>
<tr>
<th>Raw Material</th>
<th class="text-right">Qty Required</th>
<th>UOM</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
@endif
<tr>
<td class="text-gray-800">{{ $entry->rawMaterial->item_name ?? '' }}</td>
<td class="text-right text-gray-700">{{ number_format($entry->quantity_required, 2) }}</td>
<td class="text-gray-500">{{ $entry->unit_of_measure }}</td>
<td>
<div class="flex items-center gap-2">
<a href="{{ route('production.bom.edit', $entry) }}" class="btn-secondary btn-sm">Edit</a>
<form action="{{ route('production.bom.destroy', $entry) }}" method="POST"
onsubmit="confirmDelete(this,'Delete this BOM entry?','This bill of materials entry will be permanently removed.'); return false;">
@csrf
@method('DELETE')
<button type="submit" class="btn-danger btn-sm">Delete</button>
</form>
</div>
</td>
</tr>
@empty
<div class="card card-body text-center text-gray-400">
No BOM entries found. <a href="{{ route('production.bom.create') }}" class="text-blue-600 hover:underline">Add the first one</a>.
</div>
@endforelse
@if($currentProduct !== null && $bomEntries->count())
</tbody>
</table>
</div>
@endif
@endsection
@@ -0,0 +1,133 @@
@extends('layouts.app')
@section('title', 'Material Issues')
@section('content')
<div class="page-header">
<div>
<h1 class="page-title">Material Issues</h1>
<p class="page-subtitle">Record materials issued for production</p>
</div>
<a href="{{ route('production.material-issues.create') }}" class="btn-primary">
+ Issue Material
</a>
</div>
<div class="table-wrapper overflow-x-auto">
<table class="table-base">
<thead>
<tr>
<th>Production Order</th>
<th>Item</th>
<th>Warehouse</th>
<th class="text-right">Quantity</th>
<th>Issue Date</th>
<th>Notes</th>
</tr>
</thead>
<tbody>
@forelse($materialIssues as $issue)
<tr>
<td class="font-mono text-gray-700">
<a href="{{ route('production.orders.show', $issue->productionOrder) }}" class="text-blue-600 hover:underline">
{{ $issue->productionOrder->order_number ?? 'PRD-' . str_pad($issue->production_order_id, 5, '0', STR_PAD_LEFT) }}
</a>
</td>
<td class="text-gray-800">{{ $issue->item->item_name ?? '' }}</td>
<td>{{ $issue->warehouse->name ?? '' }}</td>
<td class="text-right text-gray-700">{{ number_format($issue->quantity, 2) }}</td>
<td>{{ $issue->issue_date ? \Carbon\Carbon::parse($issue->issue_date)->format('d M Y') : '' }}</td>
<td class="text-gray-500 text-xs">{{ $issue->notes ?? '-' }}</td>
</tr>
@empty
<tr>
<td colspan="6" class="px-4 py-8 text-center text-gray-400">No material issues found.</td>
</tr>
@endforelse
</tbody>
</table>
</div>
@if($materialIssues->hasPages())
<div class="mt-4">{{ $materialIssues->links() }}</div>
@endif
<!-- Inline Create Form -->
<div class="mt-8">
<h2 class="text-lg font-bold text-gray-800 mb-4">Issue New Material</h2>
@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('production.material-issues.store') }}" method="POST">
@csrf
<div class="grid grid-cols-1 gap-5 sm:grid-cols-2">
<div>
<label class="form-label">Production Order <span class="text-red-500">*</span></label>
<select name="production_order_id" required class="form-select">
<option value="">-- Select Order --</option>
@foreach($productionOrders as $po)
<option value="{{ $po->id }}" {{ (old('production_order_id', request('production_order_id'))) == $po->id ? 'selected' : '' }}>
{{ $po->order_number ?? 'PRD-' . str_pad($po->id, 5, '0', STR_PAD_LEFT) }} - {{ $po->product->item_name ?? '' }}
</option>
@endforeach
</select>
</div>
<div>
<label class="form-label">Item <span class="text-red-500">*</span></label>
<select name="item_id" required class="form-select">
<option value="">-- Select Item --</option>
@foreach($items as $item)
<option value="{{ $item->id }}" {{ old('item_id') == $item->id ? 'selected' : '' }}>
{{ $item->item_code }} - {{ $item->item_name }}
</option>
@endforeach
</select>
</div>
<div>
<label class="form-label">Warehouse <span class="text-red-500">*</span></label>
<select name="warehouse_id" required class="form-select">
<option value="">-- Select Warehouse --</option>
@foreach($warehouses as $warehouse)
<option value="{{ $warehouse->id }}" {{ old('warehouse_id') == $warehouse->id ? 'selected' : '' }}>
{{ $warehouse->name }}
</option>
@endforeach
</select>
</div>
<div>
<label class="form-label">Quantity <span class="text-red-500">*</span></label>
<input type="number" name="quantity" value="{{ old('quantity') }}" min="0.001" step="0.001" required class="form-input">
</div>
<div>
<label class="form-label">Issue Date <span class="text-red-500">*</span></label>
<input type="date" name="issue_date" value="{{ old('issue_date', date('Y-m-d')) }}" required class="form-input">
</div>
<div>
<label class="form-label">Notes</label>
<input type="text" name="notes" value="{{ old('notes') }}" class="form-input">
</div>
</div>
<div class="mt-6">
<button type="submit" class="btn-primary">Issue Material</button>
</div>
</form>
</div>
</div>
@endsection
@@ -0,0 +1,61 @@
@extends('layouts.app')
@section('title', 'New Production Order')
@section('content')
<div class="mb-6">
<h1 class="page-title">New Production Order</h1>
<p class="page-subtitle"><a href="{{ route('production.orders.index') }}" class="text-blue-600 hover:underline">Production 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
<div class="card card-body max-w-xl">
<form action="{{ route('production.orders.store') }}" method="POST">
@csrf
<div class="grid grid-cols-1 gap-5 sm:grid-cols-2">
<div class="sm:col-span-2">
<label class="form-label">Product (Finished Good) <span class="text-red-500">*</span></label>
<select name="product_id" required class="form-select">
<option value="">-- Select Product --</option>
@foreach($products as $product)
<option value="{{ $product->id }}" {{ old('product_id') == $product->id ? 'selected' : '' }}>
{{ $product->item_code }} - {{ $product->item_name }}
</option>
@endforeach
</select>
</div>
<div>
<label class="form-label">Quantity to Produce <span class="text-red-500">*</span></label>
<input type="number" name="quantity_to_produce" value="{{ old('quantity_to_produce') }}" min="0.01" step="0.01" required class="form-input">
</div>
<div>
<label class="form-label">Production Date <span class="text-red-500">*</span></label>
<input type="date" name="production_date" value="{{ old('production_date', date('Y-m-d')) }}" required 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') }}</textarea>
</div>
</div>
<div class="mt-6 flex items-center gap-3">
<button type="submit" class="btn-primary">Create Order</button>
<a href="{{ route('production.orders.index') }}" class="btn-secondary">Cancel</a>
</div>
</form>
</div>
@endsection
@@ -0,0 +1,62 @@
@extends('layouts.app')
@section('title', 'Edit Production Order')
@section('content')
<div class="mb-6">
<h1 class="page-title">Edit Production Order</h1>
<p class="page-subtitle"><a href="{{ route('production.orders.index') }}" class="text-blue-600 hover:underline">Production 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-xl">
<form action="{{ route('production.orders.update', $order) }}" method="POST">
@csrf
@method('PUT')
<div class="grid grid-cols-1 gap-5 sm:grid-cols-2">
<div class="sm:col-span-2">
<label class="form-label">Product (Finished Good) <span class="text-red-500">*</span></label>
<select name="product_id" required class="form-select">
<option value="">-- Select Product --</option>
@foreach($products as $product)
<option value="{{ $product->id }}" {{ old('product_id', $order->product_id) == $product->id ? 'selected' : '' }}>
{{ $product->item_code }} - {{ $product->item_name }}
</option>
@endforeach
</select>
</div>
<div>
<label class="form-label">Quantity to Produce <span class="text-red-500">*</span></label>
<input type="number" name="quantity_to_produce" value="{{ old('quantity_to_produce', $order->quantity_to_produce) }}" min="0.01" step="0.01" required class="form-input">
</div>
<div>
<label class="form-label">Production Date <span class="text-red-500">*</span></label>
<input type="date" name="production_date" value="{{ old('production_date', $order->production_date ? \Carbon\Carbon::parse($order->production_date)->format('Y-m-d') : '') }}" required 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('production.orders.show', $order) }}" class="btn-secondary">Cancel</a>
</div>
</form>
</div>
@endsection
@@ -0,0 +1,82 @@
@extends('layouts.app')
@section('title', 'Production Orders')
@section('content')
<div class="page-header">
<div>
<h1 class="page-title">Production Orders</h1>
<p class="page-subtitle">Manage manufacturing orders</p>
</div>
<a href="{{ route('production.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>Product</th>
<th class="text-right">Qty to Produce</th>
<th class="text-right">Produced</th>
<th>Date</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 ?? 'PRD-' . str_pad($order->id, 5, '0', STR_PAD_LEFT) }}</td>
<td class="font-medium text-gray-800">{{ $order->product->item_name ?? '' }}</td>
<td class="text-right text-gray-700">{{ number_format($order->quantity_to_produce, 2) }}</td>
<td class="text-right text-gray-700">{{ number_format($order->quantity_produced ?? 0, 2) }}</td>
<td>{{ $order->production_date ? \Carbon\Carbon::parse($order->production_date)->format('d M Y') : '' }}</td>
<td>
@php
$badgeClass = match($order->status ?? 'pending') {
'pending' => 'badge-yellow',
'in_progress' => 'badge-blue',
'completed' => 'badge-green',
'cancelled' => 'badge-red',
default => 'badge-gray',
};
@endphp
<span class="{{ $badgeClass }}">{{ ucwords(str_replace('_', ' ', $order->status ?? 'pending')) }}</span>
</td>
<td>
<div class="flex items-center gap-2 flex-wrap">
<a href="{{ route('production.orders.show', $order) }}" class="btn-primary btn-sm">View</a>
@if($order->status === 'pending')
<form action="{{ route('production.orders.start', $order) }}" method="POST">
@csrf
@method('PATCH')
<button type="submit" class="btn-primary btn-sm">Start</button>
</form>
@endif
@if($order->status === 'in_progress')
<form action="{{ route('production.orders.complete', $order) }}" method="POST">
@csrf
@method('PATCH')
<button type="submit" class="btn-success btn-sm">Complete</button>
</form>
@endif
<a href="{{ route('production.orders.edit', $order) }}" class="btn-secondary btn-sm">Edit</a>
</div>
</td>
</tr>
@empty
<tr>
<td colspan="7" class="px-4 py-8 text-center text-gray-400">No production orders found.</td>
</tr>
@endforelse
</tbody>
</table>
</div>
@if($orders->hasPages())
<div class="mt-4">{{ $orders->links() }}</div>
@endif
@endsection
@@ -0,0 +1,168 @@
@extends('layouts.app')
@section('title', 'Production Order')
@section('content')
<div class="page-header">
<div>
<h1 class="page-title">Production Order</h1>
<p class="page-subtitle">
<a href="{{ route('production.orders.index') }}" class="text-blue-600 hover:underline">Production Orders</a>
/ {{ $order->order_number ?? 'PRD-' . str_pad($order->id, 5, '0', STR_PAD_LEFT) }}
</p>
</div>
<div class="flex gap-2">
<a href="{{ route('production.orders.edit', $order) }}" class="btn-secondary">Edit</a>
@if($order->status === 'pending')
<form action="{{ route('production.orders.start', $order) }}" method="POST">
@csrf @method('PATCH')
<button type="submit" class="btn-primary">Start Production</button>
</form>
@endif
@if($order->status === 'in_progress')
<form action="{{ route('production.orders.complete', $order) }}" method="POST">
@csrf @method('PATCH')
<button type="submit" class="btn-success">Mark Complete</button>
</form>
@endif
</div>
</div>
<!-- Order Details -->
<div class="grid grid-cols-1 lg:grid-cols-2 gap-4 mb-6">
<div class="bg-white rounded-xl border border-gray-200 p-6 shadow-sm">
<h2 class="text-sm font-semibold text-gray-500 uppercase tracking-wider mb-4">Order Details</h2>
<dl class="space-y-3 text-sm">
<div class="flex justify-between">
<dt class="text-gray-500">Order Number</dt>
<dd class="font-mono font-semibold text-gray-800">{{ $order->order_number ?? 'PRD-' . str_pad($order->id, 5, '0', STR_PAD_LEFT) }}</dd>
</div>
<div class="flex justify-between">
<dt class="text-gray-500">Product</dt>
<dd class="font-medium text-gray-800">{{ $order->product->item_name ?? '' }}</dd>
</div>
<div class="flex justify-between">
<dt class="text-gray-500">Qty to Produce</dt>
<dd class="text-gray-800">{{ number_format($order->quantity_to_produce, 2) }}</dd>
</div>
<div class="flex justify-between">
<dt class="text-gray-500">Qty Produced</dt>
<dd class="text-gray-800">{{ number_format($order->quantity_produced ?? 0, 2) }}</dd>
</div>
<div class="flex justify-between">
<dt class="text-gray-500">Production Date</dt>
<dd class="text-gray-800">{{ $order->production_date ? \Carbon\Carbon::parse($order->production_date)->format('d M Y') : '-' }}</dd>
</div>
<div class="flex justify-between">
<dt class="text-gray-500">Status</dt>
<dd>
@php
$badgeClass = match($order->status ?? 'pending') {
'pending' => 'badge-yellow',
'in_progress' => 'badge-blue',
'completed' => 'badge-green',
default => 'badge-gray',
};
@endphp
<span class="{{ $badgeClass }}">{{ ucwords(str_replace('_', ' ', $order->status ?? 'pending')) }}</span>
</dd>
</div>
@if($order->notes)
<div>
<dt class="text-gray-500">Notes</dt>
<dd class="text-gray-700 mt-1">{{ $order->notes }}</dd>
</div>
@endif
</dl>
</div>
<!-- BOM for product -->
<div class="bg-white rounded-xl border border-gray-200 p-6 shadow-sm">
<h2 class="text-sm font-semibold text-gray-500 uppercase tracking-wider mb-4">Bill of Materials</h2>
@if(isset($bom) && $bom->count())
<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">Material</th>
<th class="pb-2 text-right font-semibold text-gray-600">Qty Required</th>
<th class="pb-2 text-left font-semibold text-gray-600">UOM</th>
</tr>
</thead>
<tbody class="divide-y divide-gray-100">
@foreach($bom as $bomItem)
<tr>
<td class="py-2 text-gray-800">{{ $bomItem->rawMaterial->item_name ?? '' }}</td>
<td class="py-2 text-right text-gray-700">{{ number_format($bomItem->quantity_required, 2) }}</td>
<td class="py-2 text-gray-500 pl-2">{{ $bomItem->unit_of_measure }}</td>
</tr>
@endforeach
</tbody>
</table>
@else
<p class="text-sm text-gray-400">No BOM defined for this product.</p>
@endif
</div>
</div>
<!-- Material Issues -->
@if(isset($materialIssues) && $materialIssues->count())
<div class="bg-white rounded-xl border border-gray-200 shadow-sm overflow-hidden mb-6">
<div class="px-6 py-4 border-b border-gray-200 flex items-center justify-between">
<h2 class="text-base font-semibold text-gray-700">Material Issues</h2>
<a href="{{ route('production.material-issues.create', ['production_order_id' => $order->id]) }}"
class="btn-primary btn-sm">+ Issue Material</a>
</div>
<table class="table-base">
<thead>
<tr>
<th>Item</th>
<th>Warehouse</th>
<th class="text-right">Qty</th>
<th>Date</th>
</tr>
</thead>
<tbody>
@foreach($materialIssues as $issue)
<tr>
<td class="text-gray-800">{{ $issue->item->item_name ?? '' }}</td>
<td>{{ $issue->warehouse->name ?? '' }}</td>
<td class="text-right text-gray-700">{{ number_format($issue->quantity, 2) }}</td>
<td>{{ $issue->issue_date ? \Carbon\Carbon::parse($issue->issue_date)->format('d M Y') : '' }}</td>
</tr>
@endforeach
</tbody>
</table>
</div>
@endif
<!-- Production Output -->
@if(isset($outputs) && $outputs->count())
<div class="bg-white rounded-xl border border-gray-200 shadow-sm overflow-hidden mb-6">
<div class="px-6 py-4 border-b border-gray-200 flex items-center justify-between">
<h2 class="text-base font-semibold text-gray-700">Production Output</h2>
<a href="{{ route('production.outputs.create', ['production_order_id' => $order->id]) }}"
class="btn-primary btn-sm">+ Record Output</a>
</div>
<table class="table-base">
<thead>
<tr>
<th>Item</th>
<th>Warehouse</th>
<th class="text-right">Qty</th>
<th>Date</th>
</tr>
</thead>
<tbody>
@foreach($outputs as $output)
<tr>
<td class="text-gray-800">{{ $output->item->item_name ?? '' }}</td>
<td>{{ $output->warehouse->name ?? '' }}</td>
<td class="text-right text-gray-700">{{ number_format($output->quantity, 2) }}</td>
<td>{{ $output->output_date ? \Carbon\Carbon::parse($output->output_date)->format('d M Y') : '' }}</td>
</tr>
@endforeach
</tbody>
</table>
</div>
@endif
@endsection
@@ -0,0 +1,133 @@
@extends('layouts.app')
@section('title', 'Production Output')
@section('content')
<div class="page-header">
<div>
<h1 class="page-title">Production Output</h1>
<p class="page-subtitle">Record finished goods produced</p>
</div>
<a href="{{ route('production.outputs.create') }}" class="btn-primary">
+ Record Output
</a>
</div>
<div class="table-wrapper overflow-x-auto">
<table class="table-base">
<thead>
<tr>
<th>Production Order</th>
<th>Item</th>
<th>Warehouse</th>
<th class="text-right">Quantity</th>
<th>Output Date</th>
<th>Notes</th>
</tr>
</thead>
<tbody>
@forelse($outputs as $output)
<tr>
<td class="font-mono text-gray-700">
<a href="{{ route('production.orders.show', $output->productionOrder) }}" class="text-blue-600 hover:underline">
{{ $output->productionOrder->order_number ?? 'PRD-' . str_pad($output->production_order_id, 5, '0', STR_PAD_LEFT) }}
</a>
</td>
<td class="text-gray-800">{{ $output->item->item_name ?? '' }}</td>
<td>{{ $output->warehouse->name ?? '' }}</td>
<td class="text-right font-medium text-gray-800">{{ number_format($output->quantity, 2) }}</td>
<td>{{ $output->output_date ? \Carbon\Carbon::parse($output->output_date)->format('d M Y') : '' }}</td>
<td class="text-gray-500 text-xs">{{ $output->notes ?? '-' }}</td>
</tr>
@empty
<tr>
<td colspan="6" class="px-4 py-8 text-center text-gray-400">No production outputs recorded.</td>
</tr>
@endforelse
</tbody>
</table>
</div>
@if($outputs->hasPages())
<div class="mt-4">{{ $outputs->links() }}</div>
@endif
<!-- Inline Create Form -->
<div class="mt-8">
<h2 class="text-lg font-bold text-gray-800 mb-4">Record Production Output</h2>
@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('production.outputs.store') }}" method="POST">
@csrf
<div class="grid grid-cols-1 gap-5 sm:grid-cols-2">
<div>
<label class="form-label">Production Order <span class="text-red-500">*</span></label>
<select name="production_order_id" required class="form-select">
<option value="">-- Select Order --</option>
@foreach($productionOrders as $po)
<option value="{{ $po->id }}" {{ (old('production_order_id', request('production_order_id'))) == $po->id ? 'selected' : '' }}>
{{ $po->order_number ?? 'PRD-' . str_pad($po->id, 5, '0', STR_PAD_LEFT) }} - {{ $po->product->item_name ?? '' }}
</option>
@endforeach
</select>
</div>
<div>
<label class="form-label">Item (Finished Good) <span class="text-red-500">*</span></label>
<select name="item_id" required class="form-select">
<option value="">-- Select Item --</option>
@foreach($products as $product)
<option value="{{ $product->id }}" {{ old('item_id') == $product->id ? 'selected' : '' }}>
{{ $product->item_code }} - {{ $product->item_name }}
</option>
@endforeach
</select>
</div>
<div>
<label class="form-label">Warehouse <span class="text-red-500">*</span></label>
<select name="warehouse_id" required class="form-select">
<option value="">-- Select Warehouse --</option>
@foreach($warehouses as $warehouse)
<option value="{{ $warehouse->id }}" {{ old('warehouse_id') == $warehouse->id ? 'selected' : '' }}>
{{ $warehouse->name }}
</option>
@endforeach
</select>
</div>
<div>
<label class="form-label">Quantity <span class="text-red-500">*</span></label>
<input type="number" name="quantity" value="{{ old('quantity') }}" min="0.001" step="0.001" required class="form-input">
</div>
<div>
<label class="form-label">Output Date <span class="text-red-500">*</span></label>
<input type="date" name="output_date" value="{{ old('output_date', date('Y-m-d')) }}" required class="form-input">
</div>
<div>
<label class="form-label">Notes</label>
<input type="text" name="notes" value="{{ old('notes') }}" class="form-input">
</div>
</div>
<div class="mt-6">
<button type="submit" class="btn-primary">Record Output</button>
</div>
</form>
</div>
</div>
@endsection
+29
View File
@@ -0,0 +1,29 @@
<x-app-layout>
<x-slot name="header">
<h2 class="font-semibold text-xl text-gray-800 leading-tight">
{{ __('Profile') }}
</h2>
</x-slot>
<div class="py-12">
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8 space-y-6">
<div class="p-4 sm:p-8 bg-white shadow sm:rounded-lg">
<div class="max-w-xl">
@include('profile.partials.update-profile-information-form')
</div>
</div>
<div class="p-4 sm:p-8 bg-white shadow sm:rounded-lg">
<div class="max-w-xl">
@include('profile.partials.update-password-form')
</div>
</div>
<div class="p-4 sm:p-8 bg-white shadow sm:rounded-lg">
<div class="max-w-xl">
@include('profile.partials.delete-user-form')
</div>
</div>
</div>
</div>
</x-app-layout>
@@ -0,0 +1,55 @@
<section class="space-y-6">
<header>
<h2 class="text-lg font-medium text-gray-900">
{{ __('Delete Account') }}
</h2>
<p class="mt-1 text-sm text-gray-600">
{{ __('Once your account is deleted, all of its resources and data will be permanently deleted. Before deleting your account, please download any data or information that you wish to retain.') }}
</p>
</header>
<x-danger-button
x-data=""
x-on:click.prevent="$dispatch('open-modal', 'confirm-user-deletion')"
>{{ __('Delete Account') }}</x-danger-button>
<x-modal name="confirm-user-deletion" :show="$errors->userDeletion->isNotEmpty()" focusable>
<form method="post" action="{{ route('profile.destroy') }}" class="p-6">
@csrf
@method('delete')
<h2 class="text-lg font-medium text-gray-900">
{{ __('Are you sure you want to delete your account?') }}
</h2>
<p class="mt-1 text-sm text-gray-600">
{{ __('Once your account is deleted, all of its resources and data will be permanently deleted. Please enter your password to confirm you would like to permanently delete your account.') }}
</p>
<div class="mt-6">
<x-input-label for="password" value="{{ __('Password') }}" class="sr-only" />
<x-text-input
id="password"
name="password"
type="password"
class="mt-1 block w-3/4"
placeholder="{{ __('Password') }}"
/>
<x-input-error :messages="$errors->userDeletion->get('password')" class="mt-2" />
</div>
<div class="mt-6 flex justify-end">
<x-secondary-button x-on:click="$dispatch('close')">
{{ __('Cancel') }}
</x-secondary-button>
<x-danger-button class="ms-3">
{{ __('Delete Account') }}
</x-danger-button>
</div>
</form>
</x-modal>
</section>
@@ -0,0 +1,48 @@
<section>
<header>
<h2 class="text-lg font-medium text-gray-900">
{{ __('Update Password') }}
</h2>
<p class="mt-1 text-sm text-gray-600">
{{ __('Ensure your account is using a long, random password to stay secure.') }}
</p>
</header>
<form method="post" action="{{ route('password.update') }}" class="mt-6 space-y-6">
@csrf
@method('put')
<div>
<x-input-label for="update_password_current_password" :value="__('Current Password')" />
<x-text-input id="update_password_current_password" name="current_password" type="password" class="mt-1 block w-full" autocomplete="current-password" />
<x-input-error :messages="$errors->updatePassword->get('current_password')" class="mt-2" />
</div>
<div>
<x-input-label for="update_password_password" :value="__('New Password')" />
<x-text-input id="update_password_password" name="password" type="password" class="mt-1 block w-full" autocomplete="new-password" />
<x-input-error :messages="$errors->updatePassword->get('password')" class="mt-2" />
</div>
<div>
<x-input-label for="update_password_password_confirmation" :value="__('Confirm Password')" />
<x-text-input id="update_password_password_confirmation" name="password_confirmation" type="password" class="mt-1 block w-full" autocomplete="new-password" />
<x-input-error :messages="$errors->updatePassword->get('password_confirmation')" class="mt-2" />
</div>
<div class="flex items-center gap-4">
<x-primary-button>{{ __('Save') }}</x-primary-button>
@if (session('status') === 'password-updated')
<p
x-data="{ show: true }"
x-show="show"
x-transition
x-init="setTimeout(() => show = false, 2000)"
class="text-sm text-gray-600"
>{{ __('Saved.') }}</p>
@endif
</div>
</form>
</section>
@@ -0,0 +1,64 @@
<section>
<header>
<h2 class="text-lg font-medium text-gray-900">
{{ __('Profile Information') }}
</h2>
<p class="mt-1 text-sm text-gray-600">
{{ __("Update your account's profile information and email address.") }}
</p>
</header>
<form id="send-verification" method="post" action="{{ route('verification.send') }}">
@csrf
</form>
<form method="post" action="{{ route('profile.update') }}" class="mt-6 space-y-6">
@csrf
@method('patch')
<div>
<x-input-label for="name" :value="__('Name')" />
<x-text-input id="name" name="name" type="text" class="mt-1 block w-full" :value="old('name', $user->name)" required autofocus autocomplete="name" />
<x-input-error class="mt-2" :messages="$errors->get('name')" />
</div>
<div>
<x-input-label for="email" :value="__('Email')" />
<x-text-input id="email" name="email" type="email" class="mt-1 block w-full" :value="old('email', $user->email)" required autocomplete="username" />
<x-input-error class="mt-2" :messages="$errors->get('email')" />
@if ($user instanceof \Illuminate\Contracts\Auth\MustVerifyEmail && ! $user->hasVerifiedEmail())
<div>
<p class="text-sm mt-2 text-gray-800">
{{ __('Your email address is unverified.') }}
<button form="send-verification" class="underline text-sm text-gray-600 hover:text-gray-900 rounded-md focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">
{{ __('Click here to re-send the verification email.') }}
</button>
</p>
@if (session('status') === 'verification-link-sent')
<p class="mt-2 font-medium text-sm text-green-600">
{{ __('A new verification link has been sent to your email address.') }}
</p>
@endif
</div>
@endif
</div>
<div class="flex items-center gap-4">
<x-primary-button>{{ __('Save') }}</x-primary-button>
@if (session('status') === 'profile-updated')
<p
x-data="{ show: true }"
x-show="show"
x-transition
x-init="setTimeout(() => show = false, 2000)"
class="text-sm text-gray-600"
>{{ __('Saved.') }}</p>
@endif
</div>
</form>
</section>
@@ -0,0 +1,156 @@
@extends('layouts.app')
@section('title', 'New Goods Receipt Note')
@section('content')
<div class="mb-6">
<h1 class="page-title">New Goods Receipt Note</h1>
<p class="page-subtitle"><a href="{{ route('purchase.grns.index') }}" class="text-blue-600 hover:underline">GRNs</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('purchase.grns.store') }}" method="POST">
@csrf
<div class="card card-body mb-4">
<h2 class="text-base font-semibold text-gray-700 mb-4">GRN Details</h2>
<div class="grid grid-cols-1 gap-5 sm:grid-cols-2">
<div>
<label class="form-label">Purchase Order <span class="text-red-500">*</span></label>
<select name="purchase_order_id" id="po-select" required onchange="loadPOItems(this)" class="form-select">
<option value="">-- Select PO --</option>
@foreach($purchaseOrders as $po)
<option value="{{ $po->id }}"
data-items="{{ json_encode($po->items) }}"
{{ old('purchase_order_id') == $po->id ? 'selected' : '' }}>
{{ $po->po_number ?? 'PO-' . str_pad($po->id, 5, '0', STR_PAD_LEFT) }} - {{ $po->supplier->name ?? '' }}
</option>
@endforeach
</select>
</div>
<div>
<label class="form-label">Warehouse <span class="text-red-500">*</span></label>
<select name="warehouse_id" required class="form-select">
<option value="">-- Select Warehouse --</option>
@foreach($warehouses as $warehouse)
<option value="{{ $warehouse->id }}" {{ old('warehouse_id') == $warehouse->id ? 'selected' : '' }}>
{{ $warehouse->name }}
</option>
@endforeach
</select>
</div>
<div>
<label class="form-label">Received Date <span class="text-red-500">*</span></label>
<input type="date" name="received_date" value="{{ old('received_date', date('Y-m-d')) }}" required class="form-input">
</div>
<div>
<label class="form-label">Notes</label>
<textarea name="notes" rows="2" class="form-textarea">{{ old('notes') }}</textarea>
</div>
</div>
</div>
<!-- Items from PO -->
<div class="card card-body mb-4">
<h2 class="text-base font-semibold text-gray-700 mb-4">Items Received</h2>
<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">Item</th>
<th class="pb-2 text-left font-semibold text-gray-600 w-28">PO Qty</th>
<th class="pb-2 text-left font-semibold text-gray-600 w-28">Qty Received</th>
<th class="pb-2 text-left font-semibold text-gray-600 w-36">Type</th>
</tr>
</thead>
<tbody id="grn-items-body">
<tr id="no-items-row">
<td colspan="3" class="py-4 text-center text-gray-400">Select a Purchase Order to load items.</td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="flex items-center gap-3">
<button type="submit" class="btn-primary">Save GRN</button>
<a href="{{ route('purchase.grns.index') }}" class="btn-secondary">Cancel</a>
</div>
</form>
<script>
function setGrnType(idx, val) {
document.getElementById('grn-type-' + idx).value = val;
var inv = document.getElementById('grn-t-inv-' + idx);
var con = document.getElementById('grn-t-con-' + idx);
if (val === 'inventory') {
inv.style.background = '#eff6ff'; inv.style.color = '#2563eb';
con.style.background = '#fff'; con.style.color = '#94a3b8';
} else {
con.style.background = '#fef3c7'; con.style.color = '#92400e';
inv.style.background = '#fff'; inv.style.color = '#94a3b8';
}
}
function loadPOItems(select) {
var tbody = document.getElementById('grn-items-body');
var option = select.options[select.selectedIndex];
tbody.innerHTML = '';
if (!option.value) {
tbody.innerHTML = '<tr id="no-items-row"><td colspan="4" class="py-4 text-center text-gray-400">Select a Purchase Order to load items.</td></tr>';
return;
}
var items = JSON.parse(option.dataset.items || '[]');
if (!items.length) {
tbody.innerHTML = '<tr><td colspan="4" class="py-4 text-center text-gray-400">No items on this PO.</td></tr>';
return;
}
items.forEach(function(item, idx) {
var row = document.createElement('tr');
row.className = 'border-b border-gray-100';
row.innerHTML = `
<td class="py-2 pr-2 text-gray-800">${item.item ? item.item.item_name : 'Item #' + item.item_id}
<input type="hidden" name="items[${idx}][item_id]" value="${item.item_id}">
</td>
<td class="py-2 pr-2 text-gray-500">${item.quantity}</td>
<td class="py-2 pr-2">
<input type="number" name="items[${idx}][quantity_received]"
value="${item.quantity}" min="0" step="0.01"
max="${item.quantity}"
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">
</td>
<td class="py-2">
<div style="display:flex;gap:0;border:1px solid #e2e8f0;border-radius:6px;overflow:hidden;width:fit-content;">
<span id="grn-t-inv-${idx}" onclick="setGrnType(${idx},'inventory')"
style="padding:5px 10px;font-size:11px;font-weight:700;cursor:pointer;background:#eff6ff;color:#2563eb;white-space:nowrap;">
Inventory
</span>
<span id="grn-t-con-${idx}" onclick="setGrnType(${idx},'consumable')"
style="padding:5px 10px;font-size:11px;font-weight:700;cursor:pointer;background:#fff;color:#94a3b8;border-left:1px solid #e2e8f0;white-space:nowrap;">
Consumable
</span>
</div>
<input type="hidden" name="items[${idx}][type]" id="grn-type-${idx}" value="inventory">
</td>`;
tbody.appendChild(row);
});
}
</script>
@endsection
@@ -0,0 +1,64 @@
@extends('layouts.app')
@section('title', 'Goods Receipt Notes')
@section('content')
<div class="page-header">
<div>
<h1 class="page-title">Goods Receipt Notes</h1>
<p class="page-subtitle">Record goods received from suppliers</p>
</div>
<a href="{{ route('purchase.grns.create') }}" class="btn-primary">
+ New GRN
</a>
</div>
<div class="table-wrapper overflow-x-auto">
<table class="table-base">
<thead>
<tr>
<th>GRN #</th>
<th>PO #</th>
<th>Supplier</th>
<th>Warehouse</th>
<th>Date</th>
<th>Status</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
@forelse($grns as $grn)
<tr>
<td class="font-mono text-gray-700">{{ $grn->grn_number ?? 'GRN-' . str_pad($grn->id, 5, '0', STR_PAD_LEFT) }}</td>
<td class="font-mono text-gray-600">{{ $grn->purchaseOrder->po_number ?? 'PO-' . str_pad($grn->purchase_order_id, 5, '0', STR_PAD_LEFT) }}</td>
<td class="text-gray-800">{{ $grn->purchaseOrder->supplier->name ?? '' }}</td>
<td class="text-gray-700">{{ $grn->warehouse->name ?? '' }}</td>
<td>{{ $grn->received_date ? \Carbon\Carbon::parse($grn->received_date)->format('d M Y') : '' }}</td>
<td>
<span class="badge-green">{{ ucfirst($grn->status ?? 'received') }}</span>
</td>
<td>
<div class="flex items-center gap-2">
<a href="{{ route('purchase.grns.show', $grn) }}" class="btn-primary btn-sm">View</a>
<form action="{{ route('purchase.grns.destroy', $grn) }}" method="POST"
onsubmit="confirmDelete(this,'Delete this GRN?','This goods receipt note 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="7" class="px-4 py-8 text-center text-gray-400">No GRNs found.</td>
</tr>
@endforelse
</tbody>
</table>
</div>
@if($grns->hasPages())
<div class="mt-4">{{ $grns->links() }}</div>
@endif
@endsection
@@ -0,0 +1,84 @@
@extends('layouts.app')
@section('title', 'Goods Receipt Note')
@section('content')
<div class="page-header">
<div>
<h1 class="page-title">Goods Receipt Note</h1>
<p class="page-subtitle">
<a href="{{ route('purchase.grns.index') }}" class="text-blue-600 hover:underline">GRNs</a>
/ {{ $grn->grn_number ?? 'GRN-' . str_pad($grn->id, 5, '0', STR_PAD_LEFT) }}
</p>
</div>
</div>
<div class="grid grid-cols-1 lg:grid-cols-2 gap-4 mb-6">
<div class="bg-white rounded-xl border border-gray-200 p-6 shadow-sm">
<h2 class="text-sm font-semibold text-gray-500 uppercase tracking-wider mb-4">GRN Details</h2>
<dl class="space-y-3 text-sm">
<div class="flex justify-between">
<dt class="text-gray-500">GRN Number</dt>
<dd class="font-mono font-semibold text-gray-800">{{ $grn->grn_number ?? 'GRN-' . str_pad($grn->id, 5, '0', STR_PAD_LEFT) }}</dd>
</div>
<div class="flex justify-between">
<dt class="text-gray-500">Purchase Order</dt>
<dd class="font-mono text-gray-800">
<a href="{{ route('purchase.orders.show', $grn->purchaseOrder) }}" class="text-blue-600 hover:underline">
{{ $grn->purchaseOrder->po_number ?? 'PO-' . str_pad($grn->purchase_order_id, 5, '0', STR_PAD_LEFT) }}
</a>
</dd>
</div>
<div class="flex justify-between">
<dt class="text-gray-500">Supplier</dt>
<dd class="font-medium text-gray-800">{{ $grn->purchaseOrder->supplier->name ?? '-' }}</dd>
</div>
<div class="flex justify-between">
<dt class="text-gray-500">Warehouse</dt>
<dd class="text-gray-800">{{ $grn->warehouse->name ?? '-' }}</dd>
</div>
<div class="flex justify-between">
<dt class="text-gray-500">Received Date</dt>
<dd class="text-gray-800">{{ $grn->received_date ? \Carbon\Carbon::parse($grn->received_date)->format('d M Y') : '-' }}</dd>
</div>
<div class="flex justify-between">
<dt class="text-gray-500">Status</dt>
<dd><span class="badge-green">{{ ucfirst($grn->status ?? 'received') }}</span></dd>
</div>
@if($grn->notes)
<div>
<dt class="text-gray-500">Notes</dt>
<dd class="text-gray-700 mt-1">{{ $grn->notes }}</dd>
</div>
@endif
</dl>
</div>
</div>
<!-- Items Received -->
<div class="bg-white rounded-xl border border-gray-200 shadow-sm overflow-hidden">
<div class="px-6 py-4 border-b border-gray-200">
<h2 class="text-base font-semibold text-gray-700">Items Received</h2>
</div>
<table class="table-base">
<thead>
<tr>
<th>Item</th>
<th class="text-right">PO Qty</th>
<th class="text-right">Qty Received</th>
</tr>
</thead>
<tbody>
@forelse($grn->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_ordered ?? 0, 2) }}</td>
<td class="text-right font-medium text-gray-800">{{ number_format($item->quantity_received, 2) }}</td>
</tr>
@empty
<tr><td colspan="3" class="px-4 py-6 text-center text-gray-400">No items recorded.</td></tr>
@endforelse
</tbody>
</table>
</div>
@endsection
@@ -0,0 +1,112 @@
@extends('layouts.app')
@section('title', 'New Supplier Invoice')
@section('content')
<div class="mb-6">
<h1 class="page-title">New Supplier Invoice</h1>
<p class="page-subtitle"><a href="{{ route('purchase.invoices.index') }}" class="text-blue-600 hover:underline">Supplier Invoices</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
<div class="card card-body max-w-2xl">
<form action="{{ route('purchase.invoices.store') }}" method="POST">
@csrf
<div class="grid grid-cols-1 gap-5 sm:grid-cols-2">
<div>
<label class="form-label">Invoice Number <span class="text-red-500">*</span></label>
<input type="text" name="invoice_number" value="{{ old('invoice_number') }}" required class="form-input">
</div>
<div>
<label class="form-label">Supplier <span class="text-red-500">*</span></label>
<select name="supplier_id" required class="form-select">
<option value="">-- Select Supplier --</option>
@foreach($suppliers as $supplier)
<option value="{{ $supplier->id }}" {{ old('supplier_id') == $supplier->id ? 'selected' : '' }}>
{{ $supplier->name }}
</option>
@endforeach
</select>
</div>
<div>
<label class="form-label">Purchase Order</label>
<select name="purchase_order_id" class="form-select">
<option value="">-- None --</option>
@foreach($purchaseOrders as $po)
<option value="{{ $po->id }}" {{ old('purchase_order_id') == $po->id ? 'selected' : '' }}>
{{ $po->po_number ?? 'PO-' . str_pad($po->id, 5, '0', STR_PAD_LEFT) }}
</option>
@endforeach
</select>
</div>
<div>
<label class="form-label">GRN</label>
<select name="goods_receipt_note_id" class="form-select">
<option value="">-- None --</option>
@foreach($grns as $grn)
<option value="{{ $grn->id }}" {{ old('goods_receipt_note_id') == $grn->id ? 'selected' : '' }}>
{{ $grn->grn_number ?? 'GRN-' . str_pad($grn->id, 5, '0', STR_PAD_LEFT) }}
</option>
@endforeach
</select>
</div>
<div>
<label class="form-label">Invoice Date <span class="text-red-500">*</span></label>
<input type="date" name="invoice_date" value="{{ old('invoice_date', date('Y-m-d')) }}" required class="form-input">
</div>
<div>
<label class="form-label">Due Date</label>
<input type="date" name="due_date" value="{{ old('due_date') }}" class="form-input">
</div>
<div>
<label class="form-label">Subtotal <span class="text-red-500">*</span></label>
<input type="number" name="subtotal" id="subtotal" value="{{ old('subtotal') }}"
min="0" step="0.01" required onchange="calcInvoiceTotal()" class="form-input">
</div>
<div>
<label class="form-label">VAT Amount</label>
<input type="number" name="vat_amount" id="vat_amount" value="{{ old('vat_amount', 0) }}"
min="0" step="0.01" onchange="calcInvoiceTotal()" class="form-input">
</div>
<div class="sm:col-span-2">
<label class="form-label">Total Amount</label>
<input type="number" name="total_amount" id="total_amount" value="{{ old('total_amount') }}"
readonly
class="w-full border border-gray-200 bg-gray-50 rounded-lg px-3 py-2 text-sm font-semibold text-gray-800">
</div>
</div>
<div class="mt-6 flex items-center gap-3">
<button type="submit" class="btn-primary">Save Invoice</button>
<a href="{{ route('purchase.invoices.index') }}" class="btn-secondary">Cancel</a>
</div>
</form>
</div>
<script>
function calcInvoiceTotal() {
var subtotal = parseFloat(document.getElementById('subtotal').value) || 0;
var vat = parseFloat(document.getElementById('vat_amount').value) || 0;
document.getElementById('total_amount').value = (subtotal + vat).toFixed(2);
}
</script>
@endsection
@@ -0,0 +1,86 @@
@extends('layouts.app')
@section('title', 'Edit Supplier Invoice')
@section('content')
<div class="mb-6">
<h1 class="page-title">Edit Supplier Invoice</h1>
<p class="page-subtitle"><a href="{{ route('purchase.invoices.index') }}" class="text-blue-600 hover:underline">Supplier Invoices</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('purchase.invoices.update', $invoice) }}" method="POST">
@csrf
@method('PUT')
<div class="grid grid-cols-1 gap-5 sm:grid-cols-2">
<div>
<label class="form-label">Invoice Number <span class="text-red-500">*</span></label>
<input type="text" name="invoice_number" value="{{ old('invoice_number', $invoice->invoice_number) }}" required class="form-input">
</div>
<div>
<label class="form-label">Invoice Date <span class="text-red-500">*</span></label>
<input type="date" name="invoice_date" value="{{ old('invoice_date', $invoice->invoice_date ? \Carbon\Carbon::parse($invoice->invoice_date)->format('Y-m-d') : '') }}" required class="form-input">
</div>
<div>
<label class="form-label">Due Date</label>
<input type="date" name="due_date" value="{{ old('due_date', $invoice->due_date ? \Carbon\Carbon::parse($invoice->due_date)->format('Y-m-d') : '') }}" class="form-input">
</div>
<div>
<label class="form-label">Subtotal</label>
<input type="number" name="subtotal" id="edit-subtotal" value="{{ old('subtotal', $invoice->subtotal) }}"
min="0" step="0.01" onchange="calcPurchaseEditTotal()" class="form-input">
</div>
<div>
<label class="form-label">VAT Amount</label>
<input type="number" name="vat_amount" id="edit-vat" value="{{ old('vat_amount', $invoice->vat_amount) }}"
min="0" step="0.01" onchange="calcPurchaseEditTotal()" class="form-input">
</div>
<div>
<label class="form-label">Total Amount</label>
<input type="number" name="total_amount" id="edit-total" value="{{ old('total_amount', $invoice->total_amount) }}"
readonly
class="w-full border border-gray-200 bg-gray-50 rounded-lg px-3 py-2 text-sm font-semibold text-gray-800">
</div>
<div>
<label class="form-label">Status</label>
<select name="status" class="form-select">
<option value="unpaid" {{ old('status', $invoice->status) === 'unpaid' ? 'selected' : '' }}>Unpaid</option>
<option value="partial" {{ old('status', $invoice->status) === 'partial' ? 'selected' : '' }}>Partial</option>
<option value="paid" {{ old('status', $invoice->status) === 'paid' ? 'selected' : '' }}>Paid</option>
</select>
</div>
</div>
<div class="mt-6 flex items-center gap-3">
<button type="submit" class="btn-primary">Update Invoice</button>
<a href="{{ route('purchase.invoices.index') }}" class="btn-secondary">Cancel</a>
</div>
</form>
</div>
<script>
function calcPurchaseEditTotal() {
var sub = parseFloat(document.getElementById('edit-subtotal').value) || 0;
var vat = parseFloat(document.getElementById('edit-vat').value) || 0;
document.getElementById('edit-total').value = (sub + vat).toFixed(2);
}
</script>
@endsection
@@ -0,0 +1,78 @@
@extends('layouts.app')
@section('title', 'Supplier Invoices')
@section('content')
<div class="page-header">
<div>
<h1 class="page-title">Supplier Invoices</h1>
<p class="page-subtitle">Track and manage supplier invoices</p>
</div>
<a href="{{ route('purchase.invoices.create') }}" class="btn-primary">
+ New Invoice
</a>
</div>
<div class="table-wrapper overflow-x-auto">
<table class="table-base">
<thead>
<tr>
<th>Invoice #</th>
<th>Supplier</th>
<th>PO #</th>
<th>Date</th>
<th class="text-right">Total</th>
<th class="text-right">Paid</th>
<th class="text-right">Outstanding</th>
<th>Status</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
@forelse($invoices as $invoice)
@php $outstanding = $invoice->total_amount - $invoice->paid_amount; @endphp
<tr>
<td class="font-mono text-gray-700">{{ $invoice->invoice_number }}</td>
<td class="text-gray-800">{{ $invoice->supplier->name ?? '' }}</td>
<td class="font-mono text-gray-600">{{ $invoice->purchaseOrder->po_number ?? '-' }}</td>
<td>{{ $invoice->invoice_date ? \Carbon\Carbon::parse($invoice->invoice_date)->format('d M Y') : '' }}</td>
<td class="text-right text-gray-800">{{ number_format($invoice->total_amount, 2) }}</td>
<td class="text-right text-green-700">{{ number_format($invoice->paid_amount, 2) }}</td>
<td class="text-right {{ $outstanding > 0 ? 'text-red-600 font-semibold' : 'text-gray-500' }}">{{ number_format($outstanding, 2) }}</td>
<td>
@php
$badgeClass = match($invoice->status ?? 'unpaid') {
'unpaid' => 'badge-red',
'partial' => 'badge-yellow',
'paid' => 'badge-green',
default => 'badge-gray',
};
@endphp
<span class="{{ $badgeClass }}">{{ ucfirst($invoice->status ?? 'unpaid') }}</span>
</td>
<td>
<div class="flex items-center gap-2">
<a href="{{ route('purchase.payments.create', ['invoice_id' => $invoice->id]) }}" class="btn-success btn-sm">Pay</a>
<a href="{{ route('purchase.invoices.edit', $invoice) }}" class="btn-secondary btn-sm">Edit</a>
<form action="{{ route('purchase.invoices.destroy', $invoice) }}" method="POST"
onsubmit="confirmDelete(this,'Delete this invoice?','This invoice 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="9" class="px-4 py-8 text-center text-gray-400">No invoices found.</td>
</tr>
@endforelse
</tbody>
</table>
</div>
@if($invoices->hasPages())
<div class="mt-4">{{ $invoices->links() }}</div>
@endif
@endsection
@@ -0,0 +1,196 @@
@extends('layouts.app')
@section('title', 'New Purchase Order')
@section('content')
<div class="mb-6">
<h1 class="page-title">New Purchase Order</h1>
<p class="page-subtitle"><a href="{{ route('purchase.orders.index') }}" class="text-blue-600 hover:underline">Purchase 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('purchase.orders.store') }}" method="POST">
@csrf
<!-- Header Section -->
<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">Supplier <span class="text-red-500">*</span></label>
<select name="supplier_id" required class="form-select">
<option value="">-- Select Supplier --</option>
@foreach($suppliers as $supplier)
<option value="{{ $supplier->id }}" {{ old('supplier_id') == $supplier->id ? 'selected' : '' }}>
{{ $supplier->name }}
</option>
@endforeach
</select>
</div>
<div>
<label class="form-label">Purchase Request (optional)</label>
<select name="purchase_request_id" class="form-select">
<option value="">-- None --</option>
@foreach($purchaseRequests as $pr)
<option value="{{ $pr->id }}" {{ old('purchase_request_id') == $pr->id ? 'selected' : '' }}>
{{ $pr->request_number ?? '#' . $pr->id }} - {{ $pr->item->item_name ?? '' }}
</option>
@endforeach
</select>
</div>
<div>
<label class="form-label">PO Date <span class="text-red-500">*</span></label>
<input type="date" name="po_date" value="{{ old('po_date', date('Y-m-d')) }}" required class="form-input">
</div>
<div>
<label class="form-label">Expected Delivery Date</label>
<input type="date" name="expected_delivery_date" value="{{ old('expected_delivery_date') }}" class="form-input">
</div>
<div class="sm:col-span-2">
<label class="form-label">Notes</label>
<textarea name="notes" rows="2" class="form-textarea">{{ old('notes') }}</textarea>
</div>
</div>
</div>
<!-- Items Section -->
<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="addPORow()" class="btn-primary btn-sm">+ Add Row</button>
</div>
<div class="overflow-x-auto">
<table class="min-w-full text-sm" id="po-items-table">
<thead>
<tr class="border-b border-gray-200">
<th class="pb-2 text-left font-semibold text-gray-600">Item</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">Rate</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="po-items-body">
<tr class="po-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 Item --</option>
@foreach($items as $item)
<option value="{{ $item->id }}">{{ $item->item_code }} - {{ $item->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="calcRowTotal(this)"
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 qty-input">
</td>
<td class="py-1 pr-2">
<input type="number" name="items[0][rate]" placeholder="0.00"
min="0" step="0.01"
onchange="calcRowTotal(this)"
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 rate-input">
</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 row-total">
</td>
<td class="py-1">
<button type="button" onclick="removePORow(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="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 Purchase Order</button>
<a href="{{ route('purchase.orders.index') }}" class="btn-secondary">Cancel</a>
</div>
</form>
<script>
var poRowIndex = 1;
var itemsHtml = `@foreach($items as $item)<option value="{{ $item->id }}">{{ $item->item_code }} - {{ $item->item_name }}</option>@endforeach`;
function addPORow() {
var tbody = document.getElementById('po-items-body');
var row = document.createElement('tr');
row.className = 'po-item-row';
row.innerHTML = `
<td class="py-1 pr-2">
<select name="items[${poRowIndex}][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 Item --</option>
${itemsHtml}
</select>
</td>
<td class="py-1 pr-2">
<input type="number" name="items[${poRowIndex}][quantity]" placeholder="0" min="0" step="0.01"
onchange="calcRowTotal(this)" class="w-full border border-gray-300 rounded px-2 py-1.5 text-sm qty-input">
</td>
<td class="py-1 pr-2">
<input type="number" name="items[${poRowIndex}][rate]" placeholder="0.00" min="0" step="0.01"
onchange="calcRowTotal(this)" class="w-full border border-gray-300 rounded px-2 py-1.5 text-sm rate-input">
</td>
<td class="py-1 pr-2">
<input type="number" name="items[${poRowIndex}][total]" placeholder="0.00" readonly
class="w-full border border-gray-200 bg-gray-50 rounded px-2 py-1.5 text-sm row-total">
</td>
<td class="py-1">
<button type="button" onclick="removePORow(this)" class="text-red-400 hover:text-red-600 text-lg leading-none">&times;</button>
</td>`;
tbody.appendChild(row);
poRowIndex++;
}
function removePORow(btn) {
var rows = document.querySelectorAll('.po-item-row');
if (rows.length > 1) {
btn.closest('tr').remove();
updateGrandTotal();
}
}
function calcRowTotal(input) {
var row = input.closest('tr');
var qty = parseFloat(row.querySelector('.qty-input').value) || 0;
var rate = parseFloat(row.querySelector('.rate-input').value) || 0;
var total = qty * rate;
row.querySelector('.row-total').value = total.toFixed(2);
updateGrandTotal();
}
function updateGrandTotal() {
var totals = document.querySelectorAll('.row-total');
var grand = 0;
totals.forEach(function(t) { grand += parseFloat(t.value) || 0; });
document.getElementById('grand-total').textContent = grand.toFixed(2);
}
</script>
@endsection
@@ -0,0 +1,72 @@
@extends('layouts.app')
@section('title', 'Edit Purchase Order')
@section('content')
<div class="mb-6">
<h1 class="page-title">Edit Purchase Order</h1>
<p class="page-subtitle"><a href="{{ route('purchase.orders.index') }}" class="text-blue-600 hover:underline">Purchase 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('purchase.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">Supplier <span class="text-red-500">*</span></label>
<select name="supplier_id" required class="form-select">
<option value="">-- Select Supplier --</option>
@foreach($suppliers as $supplier)
<option value="{{ $supplier->id }}" {{ old('supplier_id', $order->supplier_id) == $supplier->id ? 'selected' : '' }}>
{{ $supplier->name }}
</option>
@endforeach
</select>
</div>
<div>
<label class="form-label">PO Date <span class="text-red-500">*</span></label>
<input type="date" name="po_date" value="{{ old('po_date', $order->po_date ? \Carbon\Carbon::parse($order->po_date)->format('Y-m-d') : '') }}" required class="form-input">
</div>
<div>
<label class="form-label">Expected Delivery Date</label>
<input type="date" name="expected_delivery_date" value="{{ old('expected_delivery_date', $order->expected_delivery_date ? \Carbon\Carbon::parse($order->expected_delivery_date)->format('Y-m-d') : '') }}" class="form-input">
</div>
<div>
<label class="form-label">Status</label>
<select name="status" class="form-select">
<option value="draft" {{ old('status', $order->status) === 'draft' ? 'selected' : '' }}>Draft</option>
<option value="sent" {{ old('status', $order->status) === 'sent' ? 'selected' : '' }}>Sent</option>
<option value="received" {{ old('status', $order->status) === 'received' ? 'selected' : '' }}>Received</option>
<option value="cancelled" {{ old('status', $order->status) === 'cancelled' ? 'selected' : '' }}>Cancelled</option>
</select>
</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('purchase.orders.show', $order) }}" class="btn-secondary">Cancel</a>
</div>
</form>
</div>
@endsection
@@ -0,0 +1,74 @@
@extends('layouts.app')
@section('title', 'Purchase Orders')
@section('content')
<div class="page-header">
<div>
<h1 class="page-title">Purchase Orders</h1>
<p class="page-subtitle">Manage all purchase orders</p>
</div>
<a href="{{ route('purchase.orders.create') }}" class="btn-primary">
+ New PO
</a>
</div>
<div class="table-wrapper overflow-x-auto">
<table class="table-base">
<thead>
<tr>
<th>PO #</th>
<th>Supplier</th>
<th>Date</th>
<th>Expected Delivery</th>
<th class="text-right">Total Amount</th>
<th>Status</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
@forelse($orders as $order)
<tr>
<td class="font-mono text-gray-700">{{ $order->po_number ?? 'PO-' . str_pad($order->id, 5, '0', STR_PAD_LEFT) }}</td>
<td class="text-gray-800">{{ $order->supplier->name ?? '' }}</td>
<td>{{ $order->po_date ? \Carbon\Carbon::parse($order->po_date)->format('d M Y') : '' }}</td>
<td>{{ $order->expected_delivery_date ? \Carbon\Carbon::parse($order->expected_delivery_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',
'sent' => 'badge-blue',
'received' => '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">
<a href="{{ route('purchase.orders.show', $order) }}" class="btn-primary btn-sm">View</a>
<a href="{{ route('purchase.orders.edit', $order) }}" class="btn-secondary btn-sm">Edit</a>
<form action="{{ route('purchase.orders.destroy', $order) }}" method="POST"
onsubmit="confirmDelete(this,'Delete this purchase order?','This order 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="7" class="px-4 py-8 text-center text-gray-400">No purchase orders found.</td>
</tr>
@endforelse
</tbody>
</table>
</div>
@if($orders->hasPages())
<div class="mt-4">{{ $orders->links() }}</div>
@endif
@endsection
@@ -0,0 +1,152 @@
@extends('layouts.app')
@section('title', 'Purchase Order')
@section('content')
<div class="page-header">
<div>
<h1 class="page-title">Purchase Order</h1>
<p class="page-subtitle">
<a href="{{ route('purchase.orders.index') }}" class="text-blue-600 hover:underline">Purchase Orders</a>
/ {{ $order->po_number ?? 'PO-' . str_pad($order->id, 5, '0', STR_PAD_LEFT) }}
</p>
</div>
<div class="flex gap-2">
<a href="{{ route('purchase.orders.edit', $order) }}" class="btn-secondary">Edit</a>
<a href="{{ route('purchase.grns.create', ['purchase_order_id' => $order->id]) }}" class="btn-primary">Create GRN</a>
</div>
</div>
<div class="grid grid-cols-1 lg:grid-cols-3 gap-4 mb-6">
<!-- PO Info -->
<div class="lg:col-span-2 bg-white rounded-xl border border-gray-200 p-6 shadow-sm">
<h2 class="text-sm font-semibold text-gray-500 uppercase tracking-wider mb-4">Order Information</h2>
<dl class="grid grid-cols-2 gap-4 text-sm">
<div>
<dt class="text-gray-500">PO Number</dt>
<dd class="font-semibold text-gray-800 font-mono">{{ $order->po_number ?? 'PO-' . 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',
'sent' => 'badge-blue',
'received' => 'badge-green',
'cancelled' => 'badge-red',
default => 'badge-gray',
};
@endphp
<span class="{{ $badgeClass }}">{{ ucfirst($order->status ?? 'draft') }}</span>
</dd>
</div>
<div>
<dt class="text-gray-500">PO Date</dt>
<dd class="font-medium text-gray-800">{{ $order->po_date ? \Carbon\Carbon::parse($order->po_date)->format('d M Y') : '-' }}</dd>
</div>
<div>
<dt class="text-gray-500">Expected Delivery</dt>
<dd class="font-medium text-gray-800">{{ $order->expected_delivery_date ? \Carbon\Carbon::parse($order->expected_delivery_date)->format('d M Y') : '-' }}</dd>
</div>
@if($order->notes)
<div class="col-span-2">
<dt class="text-gray-500">Notes</dt>
<dd class="text-gray-700">{{ $order->notes }}</dd>
</div>
@endif
</dl>
</div>
<!-- Supplier Info -->
<div class="bg-white rounded-xl border border-gray-200 p-6 shadow-sm">
<h2 class="text-sm font-semibold text-gray-500 uppercase tracking-wider mb-4">Supplier</h2>
@if($order->supplier)
<dl class="space-y-2 text-sm">
<div>
<dt class="text-gray-500">Name</dt>
<dd class="font-semibold text-gray-800">{{ $order->supplier->name }}</dd>
</div>
<div>
<dt class="text-gray-500">Contact</dt>
<dd class="text-gray-700">{{ $order->supplier->contact_person }}</dd>
</div>
<div>
<dt class="text-gray-500">Email</dt>
<dd class="text-gray-700">{{ $order->supplier->email }}</dd>
</div>
<div>
<dt class="text-gray-500">Phone</dt>
<dd class="text-gray-700">{{ $order->supplier->phone }}</dd>
</div>
</dl>
@endif
</div>
</div>
<!-- Items Table -->
<div class="bg-white rounded-xl border border-gray-200 shadow-sm overflow-hidden mb-6">
<div class="px-6 py-4 border-b border-gray-200">
<h2 class="text-base font-semibold text-gray-700">Order Items</h2>
</div>
<table class="table-base">
<thead>
<tr>
<th>Item</th>
<th class="text-right">Quantity</th>
<th class="text-right">Rate</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 ?? $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->rate, 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 Amount</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>
<!-- GRNs Linked -->
@if($order->goodsReceiptNotes && $order->goodsReceiptNotes->count())
<div class="bg-white rounded-xl border border-gray-200 shadow-sm overflow-hidden">
<div class="px-6 py-4 border-b border-gray-200">
<h2 class="text-base font-semibold text-gray-700">Goods Receipt Notes</h2>
</div>
<table class="table-base">
<thead>
<tr>
<th>GRN #</th>
<th>Warehouse</th>
<th>Received Date</th>
<th>Status</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
@foreach($order->goodsReceiptNotes as $grn)
<tr>
<td class="font-mono text-gray-700">{{ $grn->grn_number ?? 'GRN-' . str_pad($grn->id, 5, '0', STR_PAD_LEFT) }}</td>
<td class="text-gray-700">{{ $grn->warehouse->name ?? '' }}</td>
<td>{{ $grn->received_date ? \Carbon\Carbon::parse($grn->received_date)->format('d M Y') : '' }}</td>
<td><span class="badge-green">{{ ucfirst($grn->status ?? 'received') }}</span></td>
<td><a href="{{ route('purchase.grns.show', $grn) }}" class="btn-primary btn-sm">View</a></td>
</tr>
@endforeach
</tbody>
</table>
</div>
@endif
@endsection
@@ -0,0 +1,78 @@
@extends('layouts.app')
@section('title', 'Record Supplier Payment')
@section('content')
<div class="mb-6">
<h1 class="page-title">Record Supplier Payment</h1>
<p class="page-subtitle"><a href="{{ route('purchase.payments.index') }}" class="text-blue-600 hover:underline">Payments</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
<div class="card card-body max-w-2xl">
<form action="{{ route('purchase.payments.store') }}" method="POST">
@csrf
<div class="grid grid-cols-1 gap-5 sm:grid-cols-2">
<div class="sm:col-span-2">
<label class="form-label">Invoice <span class="text-red-500">*</span></label>
<select name="supplier_invoice_id" required class="form-select">
<option value="">-- Select Unpaid Invoice --</option>
@foreach($unpaidInvoices as $invoice)
<option value="{{ $invoice->id }}" {{ (old('supplier_invoice_id', request('invoice_id'))) == $invoice->id ? 'selected' : '' }}>
{{ $invoice->invoice_number }} - {{ $invoice->supplier->name ?? '' }}
(Outstanding: {{ number_format($invoice->total_amount - $invoice->paid_amount, 2) }})
</option>
@endforeach
</select>
</div>
<div>
<label class="form-label">Payment Date <span class="text-red-500">*</span></label>
<input type="date" name="payment_date" value="{{ old('payment_date', date('Y-m-d')) }}" required class="form-input">
</div>
<div>
<label class="form-label">Amount <span class="text-red-500">*</span></label>
<input type="number" name="amount" value="{{ old('amount') }}" min="0.01" step="0.01" required class="form-input">
</div>
<div>
<label class="form-label">Payment Method <span class="text-red-500">*</span></label>
<select name="payment_method" required class="form-select">
<option value="">-- Select Method --</option>
<option value="cash" {{ old('payment_method') === 'cash' ? 'selected' : '' }}>Cash</option>
<option value="bank_transfer" {{ old('payment_method') === 'bank_transfer' ? 'selected' : '' }}>Bank Transfer</option>
<option value="cheque" {{ old('payment_method') === 'cheque' ? 'selected' : '' }}>Cheque</option>
<option value="other" {{ old('payment_method') === 'other' ? 'selected' : '' }}>Other</option>
</select>
</div>
<div>
<label class="form-label">Reference Number</label>
<input type="text" name="reference_number" value="{{ old('reference_number') }}" class="form-input">
</div>
<div class="sm:col-span-2">
<label class="form-label">Notes</label>
<textarea name="notes" rows="2" class="form-textarea">{{ old('notes') }}</textarea>
</div>
</div>
<div class="mt-6 flex items-center gap-3">
<button type="submit" class="btn-primary">Record Payment</button>
<a href="{{ route('purchase.payments.index') }}" class="btn-secondary">Cancel</a>
</div>
</form>
</div>
@endsection
@@ -0,0 +1,50 @@
@extends('layouts.app')
@section('title', 'Supplier Payments')
@section('content')
<div class="page-header">
<div>
<h1 class="page-title">Supplier Payments</h1>
<p class="page-subtitle">Payment history to suppliers</p>
</div>
<a href="{{ route('purchase.payments.create') }}" class="btn-primary">
+ Record Payment
</a>
</div>
<div class="table-wrapper overflow-x-auto">
<table class="table-base">
<thead>
<tr>
<th>Invoice #</th>
<th>Supplier</th>
<th>Payment Date</th>
<th class="text-right">Amount</th>
<th>Method</th>
<th>Reference</th>
</tr>
</thead>
<tbody>
@forelse($payments as $payment)
<tr>
<td class="font-mono text-gray-700">{{ $payment->supplierInvoice->invoice_number ?? '-' }}</td>
<td class="text-gray-800">{{ $payment->supplierInvoice->supplier->name ?? '' }}</td>
<td>{{ $payment->payment_date ? \Carbon\Carbon::parse($payment->payment_date)->format('d M Y') : '' }}</td>
<td class="text-right font-medium text-gray-800">{{ number_format($payment->amount, 2) }}</td>
<td class="capitalize">{{ str_replace('_', ' ', $payment->payment_method) }}</td>
<td>{{ $payment->reference_number ?? '-' }}</td>
</tr>
@empty
<tr>
<td colspan="6" class="px-4 py-8 text-center text-gray-400">No payments recorded.</td>
</tr>
@endforelse
</tbody>
</table>
</div>
@if($payments->hasPages())
<div class="mt-4">{{ $payments->links() }}</div>
@endif
@endsection
@@ -0,0 +1,62 @@
<div style="background:#fff;border-radius:14px;box-shadow:0 2px 10px rgba(0,0,0,.05);overflow:hidden;">
<table style="width:100%;border-collapse:collapse;font-size:13px;">
<thead>
<tr style="border-bottom:1px solid #f1f5f9;">
<th style="padding:12px 18px;text-align:left;font-size:11px;font-weight:700;color:#94a3b8;text-transform:uppercase;letter-spacing:.05em;">Request #</th>
<th style="padding:12px 18px;text-align:left;font-size:11px;font-weight:700;color:#94a3b8;text-transform:uppercase;letter-spacing:.05em;">Project / Description</th>
<th style="padding:12px 18px;text-align:left;font-size:11px;font-weight:700;color:#94a3b8;text-transform:uppercase;letter-spacing:.05em;">Department</th>
<th style="padding:12px 18px;text-align:left;font-size:11px;font-weight:700;color:#94a3b8;text-transform:uppercase;letter-spacing:.05em;">Requested By</th>
<th style="padding:12px 18px;text-align:left;font-size:11px;font-weight:700;color:#94a3b8;text-transform:uppercase;letter-spacing:.05em;">Stage</th>
<th style="padding:12px 18px;text-align:left;font-size:11px;font-weight:700;color:#94a3b8;text-transform:uppercase;letter-spacing:.05em;">Progress</th>
<th style="padding:12px 18px;text-align:left;font-size:11px;font-weight:700;color:#94a3b8;text-transform:uppercase;letter-spacing:.05em;">Date</th>
<th style="padding:12px 18px;"></th>
</tr>
</thead>
<tbody>
@foreach($rows as $pr)
@php
$stageIdx = $stages->stageIndex($pr->stage ?? 'draft');
$total = count(\App\Services\PurchaseStageService::STAGES);
$pct = $total > 1 ? round(($stageIdx / ($total - 1)) * 100) : 100;
$isDone = $pr->stage === 'complete';
@endphp
<tr class="pr-row" onclick="window.location='{{ route('purchase.pipeline.show', $pr) }}'"
style="border-bottom:1px solid #f8fafc;transition:background .12s;">
<td style="padding:14px 18px;font-weight:700;color:#0f172a;">{{ $pr->request_number }}</td>
<td style="padding:14px 18px;color:#374151;max-width:200px;">
<div style="font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;">
{{ $pr->project_name ?: '—' }}
</div>
@if($pr->remarks)
<div style="font-size:11px;color:#94a3b8;margin-top:1px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;">
{{ Str::limit($pr->remarks, 50) }}
</div>
@endif
</td>
<td style="padding:14px 18px;color:#64748b;">{{ $pr->department ?: '—' }}</td>
<td style="padding:14px 18px;color:#64748b;">{{ $pr->requested_by_name ?? $pr->requestedBy?->name ?? '—' }}</td>
<td style="padding:14px 18px;">
<span class="stage-pill"
style="background:{{ $isDone ? '#dcfce7' : '#fffbeb' }};color:{{ $isDone ? '#15803d' : '#92400e' }};">
{{ $stages->stageLabel($pr->stage) }}
</span>
</td>
<td style="padding:14px 18px;min-width:100px;">
<div style="height:5px;background:#f1f5f9;border-radius:3px;overflow:hidden;">
<div style="height:5px;background:{{ $isDone ? '#22c55e' : '#f59e0b' }};width:{{ $pct }}%;border-radius:3px;"></div>
</div>
<div style="font-size:10px;color:#94a3b8;margin-top:3px;">{{ $pct }}%</div>
</td>
<td style="padding:14px 18px;color:#94a3b8;white-space:nowrap;">
{{ $pr->date ? \Carbon\Carbon::parse($pr->date)->format('d M Y') : '—' }}
</td>
<td style="padding:14px 18px;">
<svg width="16" height="16" fill="none" stroke="#cbd5e1" stroke-width="2" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" d="M9 5l7 7-7 7"/>
</svg>
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
@@ -0,0 +1,79 @@
@extends('layouts.app')
@section('title', 'Purchase Pipeline')
@section('content')
<style>
.hidden-tab { display:none; }
.pr-row:hover { background:#f8fafc; cursor:pointer; }
.stage-pill { display:inline-block; font-size:11px; font-weight:700; padding:3px 10px; border-radius:20px; white-space:nowrap; }
</style>
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:24px;flex-wrap:wrap;gap:12px;">
<div>
<h1 style="font-size:22px;font-weight:700;color:#0f172a;">Purchase Pipeline</h1>
<p style="font-size:13px;color:#64748b;margin-top:3px;">Track every purchase from request to payment</p>
</div>
<a href="{{ route('purchase.requests.create') }}"
style="padding:10px 22px;background:#2563eb;color:#fff;border-radius:9px;font-size:13px;font-weight:700;text-decoration:none;">
+ New Request
</a>
</div>
{{-- Tabs --}}
<div style="display:flex;gap:0;border-bottom:2px solid #e2e8f0;margin-bottom:20px;">
<button id="tab-active" onclick="switchTab('active')"
style="padding:10px 22px;font-size:13px;font-weight:700;border:none;background:none;cursor:pointer;color:#2563eb;border-bottom:2px solid #2563eb;margin-bottom:-2px;">
Active
<span style="margin-left:6px;background:#dbeafe;color:#1d4ed8;font-size:11px;font-weight:700;padding:2px 8px;border-radius:20px;">{{ $active->count() }}</span>
</button>
<button id="tab-completed" onclick="switchTab('completed')"
style="padding:10px 22px;font-size:13px;font-weight:700;border:none;background:none;cursor:pointer;color:#64748b;border-bottom:2px solid transparent;margin-bottom:-2px;">
Completed
<span style="margin-left:6px;background:#f1f5f9;color:#64748b;font-size:11px;font-weight:700;padding:2px 8px;border-radius:20px;">{{ $completed->count() }}</span>
</button>
</div>
{{-- Active tab --}}
<div id="pane-active">
@if($active->isEmpty())
<div style="text-align:center;padding:60px 0;color:#94a3b8;">
<div style="font-size:44px;margin-bottom:12px;">✅</div>
<div style="font-size:15px;font-weight:600;">No active pipelines</div>
<div style="font-size:13px;margin-top:4px;">All purchases are complete, or create a new request.</div>
</div>
@else
@include('purchase.pipeline._table', ['rows' => $active, 'stages' => $stages])
@endif
</div>
{{-- Completed tab --}}
<div id="pane-completed" class="hidden-tab">
@if($completed->isEmpty())
<div style="text-align:center;padding:60px 0;color:#94a3b8;">
<div style="font-size:44px;margin-bottom:12px;">📋</div>
<div style="font-size:15px;font-weight:600;">No completed purchases yet</div>
</div>
@else
@include('purchase.pipeline._table', ['rows' => $completed, 'stages' => $stages])
@endif
</div>
<script>
function switchTab(tab) {
document.getElementById('pane-active').classList.toggle('hidden-tab', tab !== 'active');
document.getElementById('pane-completed').classList.toggle('hidden-tab', tab !== 'completed');
var btnActive = document.getElementById('tab-active');
var btnCompleted = document.getElementById('tab-completed');
if (tab === 'active') {
btnActive.style.color = '#2563eb'; btnActive.style.borderBottomColor = '#2563eb';
btnCompleted.style.color = '#64748b'; btnCompleted.style.borderBottomColor = 'transparent';
} else {
btnCompleted.style.color = '#2563eb'; btnCompleted.style.borderBottomColor = '#2563eb';
btnActive.style.color = '#64748b'; btnActive.style.borderBottomColor = 'transparent';
}
}
</script>
@endsection
@@ -0,0 +1,885 @@
@extends('layouts.app')
@section('title', 'Pipeline — ' . $pr->request_number)
@section('content')
<style>
.action-btn { display:inline-flex;align-items:center;gap:5px;font-size:12px;font-weight:700;padding:6px 14px;border-radius:7px;text-decoration:none;white-space:nowrap;cursor:pointer;border:none; }
.sup-item:hover { background:#f8fafc; }
.pipe-modal { display:none;position:fixed;inset:0;background:rgba(15,23,42,.55);z-index:9999;align-items:center;justify-content:center;padding:16px; }
.pipe-modal.open { display:flex; }
</style>
{{-- Breadcrumb --}}
<div style="margin-bottom:20px;">
<a href="{{ route('purchase.pipeline.index') }}"
style="font-size:13px;color:#2563eb;text-decoration:none;display:inline-flex;align-items:center;gap:5px;">
<svg width="14" height="14" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" d="M15 19l-7-7 7-7"/>
</svg>
Purchase Pipeline
</a>
</div>
@php
$stageIdx = $stages->stageIndex($pr->stage);
$allStages = \App\Services\PurchaseStageService::STAGES;
$total = count($allStages);
$pct = $total > 1 ? round(($stageIdx / ($total - 1)) * 100) : 100;
$isDone = $pr->stage === 'complete';
$pendingInv = $pr->rfqInvitations->where('status', 'pending');
$sentInv = $pr->rfqInvitations->where('status', '!=', 'pending');
$selectedIds = $pr->rfqInvitations->pluck('supplier_id')->toArray();
@endphp
{{-- Header card --}}
<div style="background:#fff;border-radius:16px;box-shadow:0 2px 12px rgba(0,0,0,.06);overflow:hidden;margin-bottom:20px;">
<div style="padding:20px 24px;display:flex;align-items:flex-start;justify-content:space-between;flex-wrap:wrap;gap:12px;">
<div>
<div style="display:flex;align-items:center;gap:10px;flex-wrap:wrap;">
<h1 style="font-size:20px;font-weight:700;color:#0f172a;margin:0;">{{ $pr->request_number }}</h1>
<span style="font-size:11px;font-weight:700;padding:4px 12px;border-radius:20px;
background:{{ $isDone ? '#dcfce7' : '#fffbeb' }};
color:{{ $isDone ? '#15803d' : '#92400e' }};">
{{ $stages->stageLabel($pr->stage) }}
</span>
</div>
<div style="font-size:13px;color:#64748b;margin-top:6px;display:flex;flex-wrap:wrap;gap:14px;">
@if($pr->project_name) <span>📁 {{ $pr->project_name }}</span> @endif
@if($pr->department) <span>🏢 {{ $pr->department }}</span> @endif
@if($pr->requested_by_name ?? $pr->requestedBy) <span>👤 {{ $pr->requested_by_name ?? $pr->requestedBy->name }}</span> @endif
@if($pr->date) <span>📅 {{ \Carbon\Carbon::parse($pr->date)->format('d M Y') }}</span> @endif
</div>
</div>
<div style="display:flex;gap:8px;flex-wrap:wrap;">
<a href="{{ route('purchase.requests.edit', $pr) }}"
style="font-size:12px;color:#2563eb;text-decoration:none;border:1px solid #bfdbfe;padding:6px 14px;border-radius:7px;white-space:nowrap;background:#eff6ff;">
✏️ Edit Request
</a>
<a href="{{ route('purchase.requests.show', $pr) }}"
style="font-size:12px;color:#64748b;text-decoration:none;border:1px solid #e2e8f0;padding:6px 14px;border-radius:7px;white-space:nowrap;">
View Full Request →
</a>
</div>
</div>
<div style="height:4px;background:#f1f5f9;">
<div style="height:4px;background:{{ $isDone ? '#22c55e' : '#f59e0b' }};width:{{ $pct }}%;transition:width .4s ease;"></div>
</div>
</div>
{{-- Two-column layout --}}
<div style="display:grid;grid-template-columns:1fr 320px;gap:20px;align-items:start;">
{{-- Timeline --}}
<div style="background:#fff;border-radius:16px;box-shadow:0 2px 12px rgba(0,0,0,.06);padding:24px;">
<h2 style="font-size:14px;font-weight:700;color:#0f172a;margin:0 0 20px;">Pipeline Stages</h2>
<div style="display:flex;flex-direction:column;gap:0;">
@foreach($allStages as $i => $stage)
@php
$done = $i < $stageIdx;
$current = $i === $stageIdx;
$isLast = $i === $total - 1;
@endphp
<div style="display:flex;align-items:stretch;gap:16px;">
{{-- Dot + line --}}
<div style="display:flex;flex-direction:column;align-items:center;width:20px;flex-shrink:0;padding-top:2px;">
<div style="width:18px;height:18px;border-radius:50%;flex-shrink:0;display:flex;align-items:center;justify-content:center;
{{ $done ? 'background:#2563eb;' : ($current ? 'background:#f59e0b;box-shadow:0 0 0 5px #fde68a;' : 'background:#e2e8f0;') }}">
@if($done)
<svg width="9" height="9" viewBox="0 0 8 8" fill="none">
<path d="M1.5 4L3 5.5L6.5 2" stroke="#fff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
@endif
</div>
@if(!$isLast)
<div style="width:2px;flex:1;min-height:12px;margin:4px 0;background:{{ $done ? '#2563eb' : '#e2e8f0' }};"></div>
@endif
</div>
{{-- Stage content --}}
<div style="flex:1;min-width:0;padding-bottom:{{ $isLast ? '0' : '16' }}px;">
<div style="display:flex;align-items:flex-start;justify-content:space-between;flex-wrap:wrap;gap:8px;">
<div>
<div style="font-size:14px;font-weight:{{ $current ? '700' : ($done ? '600' : '400') }};
color:{{ $done ? '#1d4ed8' : ($current ? '#d97706' : '#94a3b8') }};">
{{ $stages->stageLabel($stage) }}
</div>
@if($done || $current)
<div style="font-size:12px;color:#94a3b8;margin-top:2px;">
@if($stage === 'draft')
Created by {{ $pr->requested_by_name ?? $pr->requestedBy?->name ?? '—' }}
@if($pr->created_at) · {{ $pr->created_at->format('d M Y') }} @endif
@elseif($stage === 'gm_approval')
@if($pr->signature)
Signed by {{ $pr->signature->signedBy?->name ?? '—' }} · {{ $pr->signature->signed_at?->format('d M Y') }}
@elseif($current)
Awaiting GM signature
@endif
@elseif($stage === 'rfq')
@if($pr->rfqInvitations->count())
{{ $pr->rfqInvitations->count() }} supplier(s) selected
@if($pendingInv->count()) · {{ $pendingInv->count() }} unsent @endif
@elseif($current)
Select suppliers to receive quote requests
@endif
@elseif($stage === 'quoting')
{{ $pr->supplierQuotes->count() }} quote(s) received · {{ $sentInv->count() }} invited
@elseif($stage === 'comparison')
{{ $pr->supplierQuotes->count() }} quote(s) ready to compare
@elseif($stage === 'lpo' && $pr->awardedQuote)
Awarded to {{ $pr->awardedQuote->supplier->name }}
@endif
</div>
@endif
</div>
{{-- Action buttons per stage --}}
@if($current)
@if($stage === 'draft')
<button type="button" onclick="openSignModal()"
class="action-btn" style="background:#7c3aed;color:#fff;">
@if($pr->signature)
<svg width="13" height="13" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/><path stroke-linecap="round" stroke-linejoin="round" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"/></svg>
View Signature
@else
<svg width="13" height="13" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M15.232 5.232l3.536 3.536m-2.036-5.036a2.5 2.5 0 113.536 3.536L6.5 21.036H3v-3.572L16.732 3.732z"/></svg>
Sign
@endif
</button>
@elseif($stage === 'gm_approval')
<div style="display:flex;gap:8px;flex-wrap:wrap;">
<button type="button" onclick="openSignModal()"
class="action-btn" style="background:#7c3aed;color:#fff;">
@if($pr->signature)
<svg width="13" height="13" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/><path stroke-linecap="round" stroke-linejoin="round" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"/></svg>
View Signature
@else
<svg width="13" height="13" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M15.232 5.232l3.536 3.536m-2.036-5.036a2.5 2.5 0 113.536 3.536L6.5 21.036H3v-3.572L16.732 3.732z"/></svg>
Sign
@endif
</button>
<button type="button" onclick="openSupplierModal()"
class="action-btn" style="background:#2563eb;color:#fff;">
🏭 Select Suppliers
</button>
</div>
@elseif($stage === 'rfq')
<div style="display:flex;gap:8px;flex-wrap:wrap;">
<button type="button" onclick="openSupplierModal()"
class="action-btn" style="background:#2563eb;color:#fff;">
+ Add Suppliers
</button>
@if($pendingInv->count() > 0)
<form action="{{ route('purchase.requests.rfq.send-all', $pr) }}" method="POST" style="display:inline;">
@csrf
<button type="submit" class="action-btn" style="background:#16a34a;color:#fff;">
📨 Send ({{ $pendingInv->count() }})
</button>
</form>
@endif
</div>
@elseif($stage === 'quoting')
<a href="{{ route('purchase.requests.quotes', $pr) }}"
class="action-btn" style="background:#f59e0b;color:#fff;">
View Quotes ({{ $pr->supplierQuotes->count() }}) →
</a>
@elseif($stage === 'comparison')
<a href="{{ route('purchase.requests.compare', $pr) }}"
class="action-btn" style="background:#f59e0b;color:#fff;">
Compare & Award →
</a>
@elseif($stage === 'lpo')
<a href="{{ route('purchase.orders.create') }}"
class="action-btn" style="background:#16a34a;color:#fff;">
Issue LPO →
</a>
@elseif($stage === 'receiving')
<a href="{{ route('purchase.grns.create') }}"
class="action-btn" style="background:#16a34a;color:#fff;">
Record GRN →
</a>
@elseif($stage === 'payment')
<a href="{{ route('purchase.payments.create') }}"
class="action-btn" style="background:#0f172a;color:#fff;">
Issue Payment →
</a>
@endif
@endif
</div>
</div>
</div>
@endforeach
</div>
</div>
{{-- Info sidebar --}}
<div style="display:flex;flex-direction:column;gap:16px;">
{{-- Request details --}}
<div style="background:#fff;border-radius:14px;box-shadow:0 2px 10px rgba(0,0,0,.05);padding:20px;">
<h3 style="font-size:13px;font-weight:700;color:#0f172a;margin:0 0 14px;">Request Details</h3>
<dl style="display:flex;flex-direction:column;gap:8px;font-size:13px;">
@if($pr->location)
<div style="display:flex;justify-content:space-between;gap:8px;">
<dt style="color:#64748b;flex-shrink:0;">Location</dt>
<dd style="color:#0f172a;font-weight:600;text-align:right;margin:0;">{{ $pr->location }}</dd>
</div>
@endif
@if($pr->required_date_text)
<div style="display:flex;justify-content:space-between;gap:8px;">
<dt style="color:#64748b;flex-shrink:0;">Required By</dt>
<dd style="color:#0f172a;font-weight:600;text-align:right;margin:0;">{{ $pr->required_date_text }}</dd>
</div>
@endif
@if($pr->verified_by_name)
<div style="display:flex;justify-content:space-between;gap:8px;">
<dt style="color:#64748b;flex-shrink:0;">Verified By</dt>
<dd style="color:#0f172a;font-weight:600;text-align:right;margin:0;">{{ $pr->verified_by_name }}</dd>
</div>
@endif
<div style="display:flex;justify-content:space-between;gap:8px;">
<dt style="color:#64748b;flex-shrink:0;">Status</dt>
<dd style="color:#0f172a;font-weight:600;text-align:right;margin:0;">{{ ucfirst($pr->status ?? '—') }}</dd>
</div>
</dl>
</div>
{{-- Selected suppliers (rfq stage+) --}}
@if($pr->rfqInvitations->isNotEmpty())
<div style="background:#fff;border-radius:14px;box-shadow:0 2px 10px rgba(0,0,0,.05);padding:20px;">
<h3 style="font-size:13px;font-weight:700;color:#0f172a;margin:0 0 14px;">
Suppliers ({{ $pr->rfqInvitations->count() }})
</h3>
<div style="display:flex;flex-direction:column;gap:6px;">
@foreach($pr->rfqInvitations as $inv)
@php
$statusMap = [
'pending' => ['bg'=>'#f1f5f9','fg'=>'#64748b','label'=>'Pending'],
'sent' => ['bg'=>'#dbeafe','fg'=>'#1d4ed8','label'=>'Sent'],
'opened' => ['bg'=>'#e0e7ff','fg'=>'#3730a3','label'=>'Opened'],
'submitted' => ['bg'=>'#dcfce7','fg'=>'#15803d','label'=>'Submitted'],
'declined' => ['bg'=>'#fee2e2','fg'=>'#991b1b','label'=>'Declined'],
];
$sc = $statusMap[$inv->status] ?? $statusMap['pending'];
@endphp
<div style="display:flex;justify-content:space-between;align-items:center;gap:8px;font-size:12px;padding:7px 0;border-bottom:1px solid #f8fafc;">
<div>
<div style="font-weight:600;color:#0f172a;">{{ $inv->supplier->name }}</div>
@if($inv->channel !== 'email')
<div style="font-size:10px;color:#94a3b8;">{{ ucfirst($inv->channel) }}</div>
@endif
</div>
<div style="display:flex;align-items:center;gap:6px;">
@if($inv->status === 'pending' && in_array($pr->stage, ['rfq','quoting']))
{{-- WhatsApp link for pending --}}
@if($inv->supplier->phone)
<a href="{{ app(\App\Services\RfqInvitationService::class)->whatsappLink($inv) }}" target="_blank"
style="font-size:10px;background:#dcfce7;color:#15803d;padding:2px 7px;border-radius:10px;text-decoration:none;font-weight:700;">WA</a>
@endif
@endif
<span style="background:{{ $sc['bg'] }};color:{{ $sc['fg'] }};padding:2px 8px;border-radius:12px;font-weight:700;font-size:10px;">
{{ $sc['label'] }}
</span>
</div>
</div>
@endforeach
</div>
</div>
@endif
{{-- Quotes summary --}}
@if($pr->supplierQuotes->isNotEmpty())
<div style="background:#fff;border-radius:14px;box-shadow:0 2px 10px rgba(0,0,0,.05);padding:20px;">
<h3 style="font-size:13px;font-weight:700;color:#0f172a;margin:0 0 14px;">
Quotes ({{ $pr->supplierQuotes->count() }})
</h3>
<div style="display:flex;flex-direction:column;gap:8px;">
@foreach($pr->supplierQuotes->sortBy('total_amount') as $quote)
<div style="display:flex;justify-content:space-between;align-items:center;font-size:12px;padding:8px 10px;border-radius:8px;
background:{{ $quote->is_awarded ? '#f0fdf4' : '#f8fafc' }};
border:1px solid {{ $quote->is_awarded ? '#bbf7d0' : '#f1f5f9' }};">
<div style="font-weight:600;color:#0f172a;">{{ $quote->supplier->name }}</div>
<div style="color:{{ $quote->is_awarded ? '#15803d' : '#374151' }};font-weight:700;">
{{ number_format($quote->total_amount, 2) }}
@if($quote->is_awarded)
<span style="font-size:10px;background:#22c55e;color:#fff;padding:1px 6px;border-radius:10px;margin-left:4px;">Awarded</span>
@endif
</div>
</div>
@endforeach
</div>
<a href="{{ route('purchase.requests.quotes', $pr) }}"
style="display:block;margin-top:12px;text-align:center;font-size:12px;color:#2563eb;text-decoration:none;font-weight:600;">
View All Quotes →
</a>
</div>
@endif
</div>
</div>
{{-- ============================================================
GM SIGNATURE MODAL (always in DOM so the button always works)
============================================================ --}}
<div id="sign-modal" class="pipe-modal" role="dialog" aria-modal="true" aria-labelledby="sign-modal-title">
<div style="background:#fff;border-radius:20px;width:100%;max-width:560px;display:flex;flex-direction:column;box-shadow:0 30px 60px rgba(0,0,0,.3);overflow:hidden;">
{{-- Header --}}
<div style="background:linear-gradient(135deg,#7c3aed,#4f46e5);padding:22px 24px;display:flex;align-items:flex-start;justify-content:space-between;">
<div>
<div style="font-size:11px;font-weight:600;color:rgba(255,255,255,.7);text-transform:uppercase;letter-spacing:.06em;">GM Digital Signature</div>
<div id="sign-modal-title" style="font-size:18px;font-weight:700;color:#fff;margin-top:4px;">{{ $pr->request_number }}</div>
@if($pr->project_name)
<div style="font-size:12px;color:rgba(255,255,255,.75);margin-top:2px;">{{ $pr->project_name }}</div>
@endif
</div>
<button onclick="closeSignModal()" aria-label="Close"
style="width:32px;height:32px;border-radius:8px;border:none;background:rgba(255,255,255,.2);cursor:pointer;font-size:18px;color:#fff;display:flex;align-items:center;justify-content:center;flex-shrink:0;">
×
</button>
</div>
{{-- Body --}}
<div style="padding:24px;">
@if($pr->signature)
{{-- Already signed — show the captured signature --}}
<div style="text-align:center;">
<img src="{{ $pr->signature->signature_image }}"
style="max-width:100%;border:1px solid #e2e8f0;border-radius:10px;background:#f8fafc;">
<div style="font-size:12px;color:#64748b;margin-top:10px;">
Signed by <strong>{{ $pr->signature->signedBy?->name ?? '—' }}</strong>
on {{ $pr->signature->signed_at?->format('d M Y, H:i') }}
</div>
</div>
<button type="button" onclick="closeSignModal()"
style="width:100%;margin-top:18px;padding:11px;border:1.5px solid #e2e8f0;border-radius:8px;font-size:13px;font-weight:600;color:#64748b;background:#f8fafc;cursor:pointer;">
Close
</button>
@else
{{-- Signature pad --}}
<p style="font-size:13px;color:#475569;margin:0 0 14px;">
Draw your signature below to approve this purchase request. This is recorded with your name, timestamp, and IP.
</p>
<div style="position:relative;">
<canvas id="sig-canvas" width="510" height="180"
style="width:100%;border:2px dashed #cbd5e1;border-radius:10px;cursor:crosshair;touch-action:none;background:#fafafa;display:block;">
</canvas>
<div id="sig-hint" style="position:absolute;inset:0;display:flex;align-items:center;justify-content:center;pointer-events:none;">
<span style="font-size:13px;color:#cbd5e1;font-style:italic;">Sign here</span>
</div>
</div>
<form id="sig-form" method="POST" action="{{ route('purchase.requests.sign.store', $pr) }}">
@csrf
<input type="hidden" name="signature_image" id="sig-data">
</form>
<div style="display:flex;gap:10px;margin-top:14px;">
<button type="button" onclick="clearSigCanvas()"
style="flex:1;padding:11px;border:1.5px solid #e2e8f0;border-radius:8px;font-size:13px;font-weight:600;color:#64748b;background:#f8fafc;cursor:pointer;">
Clear
</button>
<button type="button" onclick="submitSignature()"
style="flex:2;padding:11px;border:none;border-radius:8px;font-size:13px;font-weight:700;color:#fff;background:linear-gradient(135deg,#7c3aed,#4f46e5);cursor:pointer;">
Confirm Signature →
</button>
</div>
@endif
</div>
</div>
</div>
{{-- ============================================================
SELECT SUPPLIERS MODAL
============================================================ --}}
<div id="supplier-modal" class="pipe-modal" role="dialog" aria-modal="true" aria-labelledby="sup-modal-title">
<div style="background:#fff;border-radius:20px;width:100%;max-width:680px;max-height:90vh;display:flex;flex-direction:column;box-shadow:0 30px 60px rgba(0,0,0,.3);overflow:hidden;">
{{-- Header --}}
<div style="padding:20px 24px 0;border-bottom:1px solid #f1f5f9;flex-shrink:0;">
<div style="display:flex;align-items:flex-start;justify-content:space-between;gap:12px;margin-bottom:16px;">
<div>
<div id="sup-modal-title" style="font-size:17px;font-weight:700;color:#0f172a;">Select Suppliers</div>
<div style="font-size:12px;color:#64748b;margin-top:3px;">Choose who receives the quote request</div>
</div>
<button onclick="closeSupplierModal()" aria-label="Close"
style="width:32px;height:32px;border-radius:8px;border:none;background:#f1f5f9;cursor:pointer;font-size:18px;color:#64748b;display:flex;align-items:center;justify-content:center;flex-shrink:0;">×</button>
</div>
{{-- Tab bar --}}
<div style="display:flex;gap:0;">
<button id="stab-global" type="button" onclick="switchSupTab('global')"
style="padding:10px 20px;font-size:13px;font-weight:700;border:none;background:none;cursor:pointer;color:#2563eb;border-bottom:2px solid #2563eb;margin-bottom:-1px;">
Full Order
</button>
<button id="stab-item" type="button" onclick="switchSupTab('item')"
style="padding:10px 20px;font-size:13px;font-weight:700;border:none;background:none;cursor:pointer;color:#94a3b8;border-bottom:2px solid transparent;margin-bottom:-1px;">
By Item
</button>
</div>
</div>
{{-- Single form wrapping both panes --}}
<form id="sup-form" action="{{ route('purchase.requests.rfq.select', $pr) }}" method="POST"
style="flex:1;overflow:hidden;display:flex;flex-direction:column;min-height:0;">
@csrf
<input type="hidden" name="mode" id="sup-mode" value="global">
{{-- ── GLOBAL PANE ── --}}
<div id="sup-global-pane" style="flex:1;overflow-y:auto;overscroll-behavior:contain;display:flex;flex-direction:column;">
{{-- Search --}}
<div style="padding:14px 24px 10px;flex-shrink:0;">
<div style="position:relative;">
<svg style="position:absolute;left:11px;top:50%;transform:translateY(-50%);pointer-events:none;"
width="14" height="14" fill="none" stroke="#94a3b8" stroke-width="2" viewBox="0 0 24 24">
<circle cx="11" cy="11" r="8"/><path stroke-linecap="round" stroke-linejoin="round" d="m21 21-4.35-4.35"/>
</svg>
<input id="sup-search" type="text" placeholder="Search suppliers…" oninput="filterGlobalSups(this.value)"
style="width:100%;box-sizing:border-box;padding:9px 12px 9px 34px;border:1px solid #e2e8f0;border-radius:9px;font-size:13px;outline:none;"
onfocus="this.style.borderColor='#2563eb'" onblur="this.style.borderColor='#e2e8f0'">
</div>
</div>
<div id="sup-list" style="flex:1;">
@forelse($suppliers as $sup)
@php $alreadyAdded = in_array($sup->id, $selectedIds); @endphp
<div class="g-sup-item" data-name="{{ strtolower($sup->name) }}"
style="padding:11px 24px;display:flex;align-items:center;gap:14px;border-bottom:1px solid #f8fafc;transition:background .1s;
{{ $alreadyAdded ? 'opacity:.45;pointer-events:none;' : '' }}">
<input type="checkbox" name="supplier_ids[]" value="{{ $sup->id }}" id="gsup-{{ $sup->id }}"
{{ $alreadyAdded ? 'disabled checked' : '' }}
onchange="toggleGlobalChan({{ $sup->id }}, this.checked); updateGlobalCount();"
style="width:17px;height:17px;accent-color:#2563eb;cursor:pointer;flex-shrink:0;">
<label for="gsup-{{ $sup->id }}" style="flex:1;cursor:{{ $alreadyAdded ? 'default' : 'pointer' }};">
<div style="font-size:13px;font-weight:600;color:#0f172a;">
{{ $sup->name }}
@if($alreadyAdded)
<span style="font-size:10px;background:#dcfce7;color:#15803d;padding:1px 6px;border-radius:10px;margin-left:6px;font-weight:700;">Added</span>
@endif
</div>
@if($sup->email || $sup->phone)
<div style="font-size:11px;color:#94a3b8;margin-top:1px;">
{{ collect([$sup->email, $sup->phone])->filter()->implode(' · ') }}
</div>
@endif
</label>
@if(!$alreadyAdded)
<div id="gchan-{{ $sup->id }}" style="display:none;flex-shrink:0;">
<input type="hidden" name="channel_{{ $sup->id }}" id="gchan-val-{{ $sup->id }}" value="email">
<div style="display:flex;border:1px solid #e2e8f0;border-radius:7px;overflow:hidden;">
<span onclick="setGChan({{ $sup->id }},'email')" id="gce-{{ $sup->id }}"
style="padding:5px 9px;font-size:10px;font-weight:700;cursor:pointer;background:#eff6ff;color:#2563eb;">Email</span>
<span onclick="setGChan({{ $sup->id }},'whatsapp')" id="gcw-{{ $sup->id }}"
style="padding:5px 9px;font-size:10px;font-weight:700;cursor:pointer;background:#fff;color:#94a3b8;border-left:1px solid #e2e8f0;">WA</span>
<span onclick="setGChan({{ $sup->id }},'both')" id="gcb-{{ $sup->id }}"
style="padding:5px 9px;font-size:10px;font-weight:700;cursor:pointer;background:#fff;color:#94a3b8;border-left:1px solid #e2e8f0;">Both</span>
</div>
</div>
@endif
</div>
@empty
<div style="padding:40px;text-align:center;color:#94a3b8;font-size:13px;">No active suppliers found.</div>
@endforelse
<div id="no-sup-msg" style="display:none;padding:30px;text-align:center;color:#94a3b8;font-size:13px;">No suppliers match your search.</div>
</div>
</div>
{{-- ── BY ITEM PANE ── --}}
<div id="sup-item-pane" style="flex:1;overflow-y:auto;overscroll-behavior:contain;display:none;flex-direction:column;">
@if($pr->items->isEmpty())
<div style="padding:40px;text-align:center;color:#94a3b8;font-size:13px;">This request has no items yet.</div>
@else
{{-- Column headers --}}
<div style="display:grid;grid-template-columns:1fr 250px;gap:12px;padding:9px 24px;background:#f8fafc;border-bottom:1px solid #e2e8f0;flex-shrink:0;">
<div style="font-size:10px;font-weight:700;color:#94a3b8;text-transform:uppercase;letter-spacing:.06em;">Item</div>
<div style="font-size:10px;font-weight:700;color:#94a3b8;text-transform:uppercase;letter-spacing:.06em;">Assign Suppliers</div>
</div>
{{-- Item rows --}}
@foreach($pr->items as $item)
<div style="display:grid;grid-template-columns:1fr 250px;gap:12px;align-items:center;padding:13px 24px;border-bottom:1px solid #f8fafc;">
{{-- Left: item info --}}
<div>
<div style="font-size:13px;font-weight:600;color:#0f172a;line-height:1.3;">{{ $item->description }}</div>
<div style="font-size:11px;color:#94a3b8;margin-top:2px;">
Qty: {{ rtrim(rtrim(number_format($item->quantity_required,2),'0'),'.') }}{{ $item->unit ? ' '.$item->unit : '' }}
</div>
</div>
{{-- Right: multi-select dropdown trigger --}}
<div>
<button type="button" id="idd-btn-{{ $item->id }}"
onclick="toggleItemDd(event,{{ $item->id }})"
style="width:100%;padding:8px 11px;border:1.5px solid #e2e8f0;border-radius:8px;background:#fff;cursor:pointer;
display:flex;align-items:center;justify-content:space-between;gap:6px;text-align:left;transition:border .15s;">
<span id="idd-label-{{ $item->id }}" style="font-size:12px;color:#94a3b8;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;flex:1;">
Select suppliers…
</span>
<svg width="11" height="11" fill="none" stroke="#94a3b8" stroke-width="2.5" viewBox="0 0 24 24" style="flex-shrink:0;">
<path stroke-linecap="round" stroke-linejoin="round" d="M19 9l-7 7-7-7"/>
</svg>
</button>
</div>
</div>
@endforeach
{{-- Per-supplier channel settings --}}
<div id="item-chan-section" style="padding:14px 24px;background:#fafafa;border-top:2px solid #f1f5f9;display:none;flex-shrink:0;">
<div style="font-size:10px;font-weight:700;color:#94a3b8;text-transform:uppercase;letter-spacing:.06em;margin-bottom:10px;">Notify via</div>
@foreach($suppliers as $sup)
<div id="ic-{{ $sup->id }}" style="display:none;align-items:center;justify-content:space-between;padding:7px 0;border-bottom:1px solid #f1f5f9;">
<span style="font-size:12px;font-weight:600;color:#0f172a;">{{ $sup->name }}</span>
<div>
<input type="hidden" name="channel_{{ $sup->id }}" id="ichan-val-{{ $sup->id }}" value="email">
<div style="display:flex;border:1px solid #e2e8f0;border-radius:7px;overflow:hidden;">
<span onclick="setIChan({{ $sup->id }},'email')" id="ice-{{ $sup->id }}"
style="padding:5px 10px;font-size:10px;font-weight:700;cursor:pointer;background:#eff6ff;color:#2563eb;">Email</span>
<span onclick="setIChan({{ $sup->id }},'whatsapp')" id="icw-{{ $sup->id }}"
style="padding:5px 10px;font-size:10px;font-weight:700;cursor:pointer;background:#fff;color:#94a3b8;border-left:1px solid #e2e8f0;">WA</span>
<span onclick="setIChan({{ $sup->id }},'both')" id="icb-{{ $sup->id }}"
style="padding:5px 10px;font-size:10px;font-weight:700;cursor:pointer;background:#fff;color:#94a3b8;border-left:1px solid #e2e8f0;">Both</span>
</div>
</div>
</div>
@endforeach
</div>
@endif
</div>
{{-- Dropdown panels: fixed-positioned, one per item — must be inside form so checkboxes are submitted --}}
@foreach($pr->items as $item)
<div id="idd-{{ $item->id }}"
style="display:none;position:fixed;z-index:99999;background:#fff;border:1.5px solid #e2e8f0;
border-radius:12px;box-shadow:0 12px 32px rgba(0,0,0,.18);overflow:hidden;min-width:240px;">
{{-- Search --}}
<div style="padding:10px 10px 6px;">
<input type="text" placeholder="Search suppliers…"
oninput="filterItemDd({{ $item->id }}, this.value)"
style="width:100%;box-sizing:border-box;padding:7px 10px;border:1px solid #e2e8f0;border-radius:7px;font-size:12px;outline:none;"
onfocus="this.style.borderColor='#2563eb'" onblur="this.style.borderColor='#e2e8f0'">
</div>
{{-- Supplier list --}}
<div style="max-height:210px;overflow-y:auto;padding-bottom:4px;">
@forelse($suppliers as $sup)
@php $alreadyAdded = in_array($sup->id, $selectedIds); @endphp
<label class="idd-row-{{ $item->id }}" data-name="{{ strtolower($sup->name) }}"
style="display:flex;align-items:center;gap:10px;padding:8px 12px;cursor:{{ $alreadyAdded ? 'default' : 'pointer' }};
{{ $alreadyAdded ? 'opacity:.45;' : '' }}"
onmouseover="{{ $alreadyAdded ? '' : "this.style.background='#f8fafc'" }}"
onmouseout="this.style.background='transparent'">
<input type="checkbox" name="item_suppliers[{{ $item->id }}][]" value="{{ $sup->id }}"
data-supname="{{ $sup->name }}"
id="isup-{{ $item->id }}-{{ $sup->id }}"
{{ $alreadyAdded ? 'disabled checked' : '' }}
onchange="onItemSupChange({{ $item->id }}, {{ $sup->id }})"
style="width:15px;height:15px;accent-color:#2563eb;cursor:{{ $alreadyAdded ? 'default' : 'pointer' }};flex-shrink:0;">
<div style="min-width:0;">
<div style="font-size:12px;font-weight:600;color:#0f172a;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;">
{{ $sup->name }}
@if($alreadyAdded)
<span style="font-size:9px;background:#dcfce7;color:#15803d;padding:1px 5px;border-radius:8px;margin-left:4px;">Added</span>
@endif
</div>
@if($sup->email)
<div style="font-size:10px;color:#94a3b8;">{{ $sup->email }}</div>
@endif
</div>
</label>
@empty
<div style="padding:16px;text-align:center;color:#94a3b8;font-size:12px;">No suppliers found.</div>
@endforelse
<div id="idd-no-{{ $item->id }}" style="display:none;padding:14px;text-align:center;color:#94a3b8;font-size:12px;">No results</div>
</div>
</div>
@endforeach
</form>
{{-- Footer --}}
<div style="padding:14px 24px;border-top:1px solid #f1f5f9;display:flex;align-items:center;justify-content:space-between;flex-shrink:0;background:#fafafa;">
<div style="font-size:12px;color:#64748b;" id="sup-footer-msg">0 selected</div>
<div style="display:flex;gap:10px;">
<button type="button" onclick="closeSupplierModal()"
style="padding:8px 18px;border:1px solid #e2e8f0;border-radius:8px;font-size:13px;font-weight:600;color:#64748b;background:#fff;cursor:pointer;">
Cancel
</button>
<button type="button" onclick="submitSuppliers()"
style="padding:8px 22px;background:#2563eb;color:#fff;border:none;border-radius:8px;font-size:13px;font-weight:700;cursor:pointer;">
Save &amp; Continue →
</button>
</div>
</div>
</div>
</div>
<script>
// ---- Signature modal ----
(function () {
var canvas, ctx, hint, drawing = false, hasMark = false;
function initCanvas() {
canvas = document.getElementById('sig-canvas');
if (!canvas || canvas._bound) return;
canvas._bound = true;
ctx = canvas.getContext('2d');
hint = document.getElementById('sig-hint');
ctx.lineWidth = 2.5; ctx.lineCap = 'round'; ctx.lineJoin = 'round'; ctx.strokeStyle = '#1e293b';
function pos(e) {
var r = canvas.getBoundingClientRect();
var src = e.touches ? e.touches[0] : e;
return { x: (src.clientX - r.left) * (canvas.width / r.width),
y: (src.clientY - r.top) * (canvas.height / r.height) };
}
function start(e) { e.preventDefault(); drawing = true; var p = pos(e); ctx.beginPath(); ctx.moveTo(p.x, p.y); if (!hasMark) { hasMark = true; if (hint) hint.style.display = 'none'; } }
function move(e) { e.preventDefault(); if (!drawing) return; var p = pos(e); ctx.lineTo(p.x, p.y); ctx.stroke(); ctx.beginPath(); ctx.moveTo(p.x, p.y); }
function end() { drawing = false; }
canvas.addEventListener('mousedown', start);
canvas.addEventListener('mousemove', move);
canvas.addEventListener('mouseup', end);
canvas.addEventListener('mouseleave', end);
canvas.addEventListener('touchstart', start, { passive: false });
canvas.addEventListener('touchmove', move, { passive: false });
canvas.addEventListener('touchend', end);
}
window.openSignModal = function () {
document.getElementById('sign-modal').classList.add('open');
initCanvas();
};
window.closeSignModal = function () {
document.getElementById('sign-modal').classList.remove('open');
};
window.clearSigCanvas = function () {
if (!canvas) return;
ctx.clearRect(0, 0, canvas.width, canvas.height);
hasMark = false;
if (hint) hint.style.display = 'flex';
};
window.submitSignature = function () {
if (!hasMark) { showToast('Please draw your signature first.', 'warn'); return; }
document.getElementById('sig-data').value = canvas.toDataURL('image/png');
document.getElementById('sig-form').submit();
};
document.getElementById('sign-modal').addEventListener('click', function (e) {
if (e.target === this) closeSignModal();
});
}());
// ---- Supplier modal ----
var _supTab = 'global';
function openSupplierModal() {
document.getElementById('supplier-modal').classList.add('open');
switchSupTab('global');
document.getElementById('sup-search').focus();
}
function closeSupplierModal() {
closeAllItemDd();
document.getElementById('supplier-modal').classList.remove('open');
}
document.getElementById('supplier-modal').addEventListener('click', function(e) {
if (e.target === this) closeSupplierModal();
});
document.addEventListener('keydown', function(e) {
if (e.key === 'Escape') { closeSignModal(); closeSupplierModal(); }
});
// Tab switching
function switchSupTab(tab) {
_supTab = tab;
document.getElementById('sup-mode').value = tab === 'item' ? 'by_item' : 'global';
document.getElementById('sup-global-pane').style.display = tab === 'global' ? 'flex' : 'none';
document.getElementById('sup-item-pane').style.display = tab === 'item' ? 'flex' : 'none';
var gBtn = document.getElementById('stab-global');
var iBtn = document.getElementById('stab-item');
if (tab === 'global') {
gBtn.style.color = '#2563eb'; gBtn.style.borderBottomColor = '#2563eb';
iBtn.style.color = '#94a3b8'; iBtn.style.borderBottomColor = 'transparent';
} else {
iBtn.style.color = '#2563eb'; iBtn.style.borderBottomColor = '#2563eb';
gBtn.style.color = '#94a3b8'; gBtn.style.borderBottomColor = 'transparent';
}
updateFooter();
}
// ── Global tab helpers ──
function filterGlobalSups(q) {
q = q.toLowerCase().trim();
var items = document.querySelectorAll('.g-sup-item');
var visible = 0;
items.forEach(function(item) {
var match = !q || item.dataset.name.indexOf(q) !== -1;
item.style.display = match ? 'flex' : 'none';
if (match) visible++;
});
document.getElementById('no-sup-msg').style.display = (visible === 0 && q) ? 'block' : 'none';
}
function toggleGlobalChan(id, checked) {
var el = document.getElementById('gchan-' + id);
if (el) el.style.display = checked ? 'block' : 'none';
updateFooter();
}
var chanStyles = {
email: { bg:'#eff6ff', fg:'#2563eb' },
whatsapp: { bg:'#f0fdf4', fg:'#15803d' },
both: { bg:'#fef3c7', fg:'#92400e' },
};
function applyBtnStyles(prefix, id, val) {
[['email','e'],['whatsapp','w'],['both','b']].forEach(function(p) {
var el = document.getElementById(prefix + p[1] + '-' + id);
if (!el) return;
if (p[0] === val) { el.style.background = chanStyles[val].bg; el.style.color = chanStyles[val].fg; }
else { el.style.background = '#fff'; el.style.color = '#94a3b8'; }
});
}
function setGChan(id, val) {
document.getElementById('gchan-val-' + id).value = val;
applyBtnStyles('gc', id, val);
}
function updateGlobalCount() { updateFooter(); }
// ── By-item tab helpers ──
var _openItemDd = null;
function toggleItemDd(event, itemId) {
event.stopPropagation();
var dd = document.getElementById('idd-' + itemId);
if (!dd) return;
// close if already open
if (_openItemDd === itemId) {
dd.style.display = 'none';
_openItemDd = null;
return;
}
// close any other open dropdown
closeAllItemDd();
// position relative to the trigger button
var btn = document.getElementById('idd-btn-' + itemId);
var rect = btn.getBoundingClientRect();
var ddWidth = 260;
var left = Math.min(rect.left, window.innerWidth - ddWidth - 8);
dd.style.left = left + 'px';
dd.style.top = (rect.bottom + 4) + 'px';
dd.style.width = ddWidth + 'px';
dd.style.display = 'block';
_openItemDd = itemId;
// focus search input
var inp = dd.querySelector('input[type="text"]');
if (inp) setTimeout(function(){ inp.focus(); }, 30);
}
function closeAllItemDd() {
if (_openItemDd !== null) {
var dd = document.getElementById('idd-' + _openItemDd);
if (dd) dd.style.display = 'none';
_openItemDd = null;
}
}
function filterItemDd(itemId, q) {
q = q.toLowerCase().trim();
var rows = document.querySelectorAll('.idd-row-' + itemId);
var visible = 0;
rows.forEach(function(row) {
var match = !q || (row.dataset.name || '').indexOf(q) !== -1;
row.style.display = match ? 'flex' : 'none';
if (match) visible++;
});
var noEl = document.getElementById('idd-no-' + itemId);
if (noEl) noEl.style.display = (visible === 0 && q) ? 'block' : 'none';
}
function updateItemDdLabel(itemId) {
var checked = document.querySelectorAll('[id^="isup-' + itemId + '-"]:checked:not([disabled])');
var label = document.getElementById('idd-label-' + itemId);
if (!label) return;
if (checked.length === 0) {
label.textContent = 'Select suppliers…';
label.style.color = '#94a3b8';
} else {
var names = Array.from(checked).map(function(c) { return c.dataset.supname || c.value; });
label.textContent = names.join(', ');
label.style.color = '#0f172a';
}
}
function onItemSupChange(itemId, supId) {
// update dropdown trigger label for this item
updateItemDdLabel(itemId);
// show/hide channel row for this supplier (appears if checked in ANY item)
var anyChecked = document.querySelectorAll('input[name^="item_suppliers["][value="' + supId + '"]:checked:not([disabled])').length > 0;
var row = document.getElementById('ic-' + supId);
if (row) row.style.display = anyChecked ? 'flex' : 'none';
// show/hide entire channel section
var anyItemChecked = document.querySelectorAll('#sup-item-pane input[type="checkbox"]:checked:not([disabled])').length > 0;
document.getElementById('item-chan-section').style.display = anyItemChecked ? 'block' : 'none';
updateFooter();
}
function setIChan(id, val) {
document.getElementById('ichan-val-' + id).value = val;
applyBtnStyles('ic', id, val);
}
// close dropdown when clicking outside
document.addEventListener('click', function(e) {
if (_openItemDd === null) return;
var dd = document.getElementById('idd-' + _openItemDd);
var btn = document.getElementById('idd-btn-' + _openItemDd);
if (dd && !dd.contains(e.target) && btn && !btn.contains(e.target)) {
closeAllItemDd();
}
});
// ── Footer count ──
function updateFooter() {
var msg = document.getElementById('sup-footer-msg');
if (_supTab === 'global') {
var n = document.querySelectorAll('#sup-list input[type="checkbox"]:checked:not([disabled])').length;
msg.textContent = n + ' supplier' + (n === 1 ? '' : 's') + ' selected';
} else {
var checked = document.querySelectorAll('#sup-item-pane input[type="checkbox"]:checked:not([disabled])');
var supIds = new Set();
checked.forEach(function(c) { supIds.add(c.value); });
var n = supIds.size;
msg.textContent = n + ' supplier' + (n === 1 ? '' : 's') + ' assigned';
}
}
// ── Submit ──
function submitSuppliers() {
if (_supTab === 'global') {
var checked = document.querySelectorAll('#sup-list input[type="checkbox"]:checked:not([disabled])');
if (checked.length === 0) { showToast('Please select at least one supplier.', 'warn'); return; }
} else {
var checked = document.querySelectorAll('#sup-item-pane input[type="checkbox"]:checked:not([disabled])');
if (checked.length === 0) { showToast('Please assign at least one supplier to an item.', 'warn'); return; }
}
document.getElementById('sup-form').submit();
}
</script>
@endsection
@@ -0,0 +1,179 @@
@extends('layouts.app')
@section('title', 'Compare Quotes — ' . $request->request_number)
@section('content')
<div style="max-width:1100px;margin:0 auto;">
<div style="background:#fff;border-radius:16px;box-shadow:0 4px 24px rgba(0,0,0,.08);overflow:hidden;">
<div style="background:linear-gradient(135deg,#f59e0b,#d97706);padding:24px 28px;display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:12px;">
<div>
<div style="font-size:11px;font-weight:600;color:rgba(255,255,255,.7);text-transform:uppercase;letter-spacing:.06em;">Quote Comparison</div>
<div style="font-size:20px;font-weight:700;color:#fff;margin-top:4px;">{{ $request->request_number }}</div>
@if($request->project_name)
<div style="font-size:13px;color:rgba(255,255,255,.8);margin-top:2px;">{{ $request->project_name }}</div>
@endif
</div>
<a href="{{ route('purchase.requests.quotes', $request) }}"
style="padding:9px 18px;background:rgba(255,255,255,.15);color:#fff;border:1.5px solid rgba(255,255,255,.35);border-radius:8px;font-size:13px;font-weight:600;text-decoration:none;">
← Back to Quotes
</a>
</div>
<div style="padding:24px;overflow-x:auto;">
@if($quotes->isEmpty())
<p style="text-align:center;color:#94a3b8;padding:60px 0;">No quotes submitted yet.</p>
@else
@php
$awardedQuote = $request->awardedQuote;
@endphp
<table style="width:100%;border-collapse:collapse;font-size:13px;min-width:500px;">
<thead>
<tr>
<th style="padding:10px 14px;text-align:left;background:#f8fafc;font-size:11px;font-weight:700;color:#64748b;text-transform:uppercase;min-width:200px;border-radius:8px 0 0 0;">Item</th>
@foreach($quotes as $quote)
<th style="padding:10px 14px;text-align:center;background:#f8fafc;font-size:12px;font-weight:700;color:{{ $quote->is_awarded ? '#15803d' : '#0f172a' }};min-width:150px;
{{ $quote->is_awarded ? 'background:#f0fdf4;' : '' }}">
{{ $quote->supplier->name }}
@if($quote->is_awarded)
<div style="font-size:10px;font-weight:700;color:#15803d;margin-top:2px;">✓ AWARDED</div>
@endif
</th>
@endforeach
</tr>
</thead>
<tbody>
{{-- Per-item rows --}}
@foreach($items as $i => $reqItem)
@php
$rowPrices = $quotes->map(fn($q) => optional($q->items->get($i))->unit_price)->filter()->values();
$minPrice = $rowPrices->count() ? $rowPrices->min() : null;
@endphp
<tr>
<td style="padding:10px 14px;border-bottom:1px solid #f1f5f9;font-weight:500;color:#0f172a;">
{{ $reqItem->description }}
<div style="font-size:11px;color:#94a3b8;margin-top:2px;">Qty: {{ $reqItem->quantity }} {{ $reqItem->unit }}</div>
</td>
@foreach($quotes as $q)
@php $qItem = $q->items->get($i); $isMin = $qItem && $minPrice !== null && (float)$qItem->unit_price === (float)$minPrice && $rowPrices->count() > 1; @endphp
<td style="padding:10px 14px;border-bottom:1px solid #f1f5f9;text-align:center;background:{{ $isMin ? '#f0fdf4' : '' }};">
@if($qItem)
<div style="font-weight:600;color:{{ $isMin ? '#15803d' : '#0f172a' }};">
@if($isMin)<span style="font-size:10px;font-weight:700;color:#15803d;display:block;">LOWEST</span>@endif
BD {{ number_format($qItem->unit_price, 3) }}
</div>
<div style="font-size:11px;color:#64748b;margin-top:2px;">BD {{ number_format($qItem->total_price, 3) }}</div>
@else
<span style="color:#e2e8f0;">—</span>
@endif
</td>
@endforeach
</tr>
@endforeach
{{-- Grand totals --}}
@php $minTotal = $quotes->min('total_amount'); @endphp
<tr style="background:#f8fafc;font-weight:700;">
<td style="padding:12px 14px;border-top:2px solid #e2e8f0;">Grand Total</td>
@foreach($quotes as $quote)
@php $isCheapest = (float)$quote->total_amount === (float)$minTotal && $quotes->count() > 1; @endphp
<td style="padding:12px 14px;text-align:center;border-top:2px solid #e2e8f0;font-size:15px;color:{{ $isCheapest ? '#15803d' : '#0f172a' }};background:{{ $isCheapest ? '#f0fdf4' : '' }};">
BD {{ number_format($quote->total_amount, 3) }}
</td>
@endforeach
</tr>
{{-- Lead time --}}
<tr>
<td style="padding:10px 14px;color:#64748b;font-size:12px;border-top:1px solid #f1f5f9;">Lead Time</td>
@foreach($quotes as $quote)
<td style="padding:10px 14px;text-align:center;font-size:12px;color:#64748b;border-top:1px solid #f1f5f9;">
{{ $quote->lead_time_days !== null ? $quote->lead_time_days.' days' : '—' }}
</td>
@endforeach
</tr>
{{-- Payment terms --}}
<tr>
<td style="padding:10px 14px;color:#64748b;font-size:12px;">Payment Terms</td>
@foreach($quotes as $quote)
<td style="padding:10px 14px;text-align:center;font-size:12px;color:#64748b;">
{{ $quote->payment_terms ?: '—' }}
</td>
@endforeach
</tr>
{{-- Award buttons --}}
@if(!$awardedQuote)
<tr style="background:#fffbeb;">
<td style="padding:12px 14px;font-size:12px;font-weight:700;color:#92400e;border-top:2px solid #fde68a;">Award to:</td>
@foreach($quotes as $quote)
<td style="padding:12px 14px;text-align:center;border-top:2px solid #fde68a;">
<button type="button"
onclick="openAwardModal({{ $quote->id }}, '{{ addslashes($quote->supplier->name) }}', 'BD {{ number_format($quote->total_amount, 3) }}')"
style="padding:8px 16px;background:#f59e0b;color:#fff;border:none;border-radius:7px;font-size:12px;font-weight:700;cursor:pointer;">
Award →
</button>
</td>
@endforeach
</tr>
@else
<tr style="background:#f0fdf4;">
<td colspan="{{ $quotes->count() + 1 }}" style="padding:14px;text-align:center;font-size:13px;font-weight:700;color:#15803d;border-top:2px solid #bbf7d0;">
✓ Awarded to {{ $awardedQuote->supplier->name }} on {{ $awardedQuote->awarded_at?->format('d M Y') }}
</td>
</tr>
@endif
</tbody>
</table>
@endif
</div>
</div>
</div>
{{-- Award modal --}}
<div id="award-modal" style="display:none;position:fixed;inset:0;background:rgba(15,23,42,.6);backdrop-filter:blur(4px);z-index:9999;align-items:center;justify-content:center;padding:20px;">
<div style="background:#fff;border-radius:18px;width:100%;max-width:440px;overflow:hidden;box-shadow:0 24px 60px rgba(0,0,0,.25);">
<div style="height:3px;background:linear-gradient(90deg,#f59e0b,#fbbf24);"></div>
<div style="padding:28px 28px 0;">
<div style="font-size:17px;font-weight:700;color:#0f172a;margin-bottom:4px;">Award Quote</div>
<div style="font-size:13px;color:#64748b;margin-bottom:4px;" id="award-supplier-line"></div>
<div style="font-size:13px;color:#64748b;margin-bottom:20px;" id="award-amount-line"></div>
<form id="award-form" method="POST">
@csrf
<label style="display:block;font-size:11px;font-weight:700;color:#64748b;text-transform:uppercase;margin-bottom:6px;">Reason for selection (required)</label>
<textarea name="award_reason" rows="3" required
style="width:100%;padding:9px 12px;border:1.5px solid #e2e8f0;border-radius:8px;font-size:13px;margin-bottom:16px;resize:vertical;"
placeholder="e.g. Lowest price, reliable supplier, suitable lead time…"></textarea>
<div style="display:flex;gap:10px;padding-bottom:24px;">
<button type="button" onclick="closeAwardModal()"
style="flex:1;padding:11px;border:1.5px solid #e2e8f0;border-radius:9px;font-size:13px;font-weight:600;background:#f8fafc;color:#475569;cursor:pointer;">
Cancel
</button>
<button type="submit"
style="flex:2;padding:11px;background:#f59e0b;color:#fff;border:none;border-radius:9px;font-size:13px;font-weight:700;cursor:pointer;">
Confirm Award
</button>
</div>
</form>
</div>
</div>
</div>
<script>
function openAwardModal(quoteId, supplierName, amount) {
document.getElementById('award-form').action = '/purchase/requests/{{ $request->id }}/quotes/' + quoteId + '/award';
document.getElementById('award-supplier-line').textContent = 'Supplier: ' + supplierName;
document.getElementById('award-amount-line').textContent = 'Total: ' + amount;
document.getElementById('award-modal').style.display = 'flex';
}
function closeAwardModal() {
document.getElementById('award-modal').style.display = 'none';
}
document.getElementById('award-modal').addEventListener('click', function(e) {
if (e.target === this) closeAwardModal();
});
</script>
@endsection
@@ -0,0 +1,104 @@
@extends('layouts.app')
@section('title', 'Quotes — ' . $request->request_number)
@section('content')
<div style="max-width:760px;margin:0 auto;">
<div style="background:#fff;border-radius:16px;box-shadow:0 4px 24px rgba(0,0,0,.08);overflow:hidden;">
<div style="background:linear-gradient(135deg,#f59e0b,#d97706);padding:24px 28px;display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:12px;">
<div>
<div style="font-size:11px;font-weight:600;color:rgba(255,255,255,.7);text-transform:uppercase;letter-spacing:.06em;">Supplier Quotes</div>
<div style="font-size:20px;font-weight:700;color:#fff;margin-top:4px;">{{ $request->request_number }}</div>
</div>
@if($quotes->count() >= 1)
<a href="{{ route('purchase.requests.compare', $request) }}"
style="padding:10px 20px;background:rgba(255,255,255,.2);color:#fff;border:1.5px solid rgba(255,255,255,.4);border-radius:8px;font-size:13px;font-weight:700;text-decoration:none;">
Compare All →
</a>
@endif
</div>
<div style="padding:24px;">
@if($quotes->isEmpty())
<div style="text-align:center;padding:60px 0;color:#94a3b8;">
<div style="font-size:40px;margin-bottom:12px;">📬</div>
<div style="font-size:15px;font-weight:600;margin-bottom:4px;">No quotes yet</div>
<div style="font-size:13px;">Waiting for suppliers to submit their quotes via the private links.</div>
</div>
@else
<div style="display:flex;flex-direction:column;gap:12px;">
@foreach($quotes->sortBy('total_amount') as $quote)
@php $isLowest = $quote->total_amount == $quotes->min('total_amount') && $quotes->count() > 1; @endphp
<div style="border:1.5px solid {{ $quote->is_awarded ? '#bbf7d0' : ($isLowest ? '#bfdbfe' : '#e2e8f0') }};border-radius:12px;padding:18px 20px;background:{{ $quote->is_awarded ? '#f0fdf4' : ($isLowest ? '#eff6ff' : '#fff') }};">
<div style="display:flex;align-items:flex-start;justify-content:space-between;gap:12px;flex-wrap:wrap;">
<div>
<div style="font-size:14px;font-weight:700;color:#0f172a;">{{ $quote->supplier->name }}</div>
<div style="font-size:12px;color:#64748b;margin-top:3px;">
Lead time: {{ $quote->lead_time_days !== null ? $quote->lead_time_days.' days' : '—' }}
@if($quote->payment_terms) · {{ $quote->payment_terms }} @endif
</div>
<div style="font-size:11px;color:#94a3b8;margin-top:2px;">Submitted {{ $quote->submitted_at->format('d M Y, H:i') }}</div>
</div>
<div style="text-align:right;flex-shrink:0;">
<div style="font-size:18px;font-weight:700;color:{{ $isLowest ? '#2563eb' : '#0f172a' }};">BD {{ number_format($quote->total_amount, 3) }}</div>
@if($isLowest && $quotes->count() > 1)
<div style="font-size:10px;font-weight:700;color:#2563eb;margin-top:2px;">LOWEST PRICE</div>
@endif
@if($quote->is_awarded)
<div style="font-size:11px;font-weight:700;color:#15803d;margin-top:4px;">✓ Awarded</div>
@endif
</div>
</div>
@if($quote->notes)
<div style="margin-top:10px;padding-top:10px;border-top:1px dashed #e2e8f0;font-size:12px;color:#64748b;">
{{ $quote->notes }}
</div>
@endif
{{-- Item breakdown --}}
<details style="margin-top:10px;">
<summary style="font-size:12px;font-weight:600;color:#2563eb;cursor:pointer;">View item breakdown ({{ $quote->items->count() }} items)</summary>
<div style="margin-top:8px;overflow-x:auto;">
<table style="width:100%;border-collapse:collapse;font-size:12px;">
<thead>
<tr style="background:#f8fafc;">
<th style="padding:6px 10px;text-align:left;color:#64748b;">Item</th>
<th style="padding:6px 10px;text-align:right;color:#64748b;">Qty</th>
<th style="padding:6px 10px;text-align:right;color:#64748b;">Unit Price</th>
<th style="padding:6px 10px;text-align:right;color:#64748b;">Total</th>
</tr>
</thead>
<tbody>
@foreach($quote->items as $qi)
<tr style="border-top:1px solid #f1f5f9;">
<td style="padding:6px 10px;">{{ $qi->description }}</td>
<td style="padding:6px 10px;text-align:right;">{{ $qi->quantity }}</td>
<td style="padding:6px 10px;text-align:right;">BD {{ number_format($qi->unit_price, 3) }}</td>
<td style="padding:6px 10px;text-align:right;font-weight:600;">BD {{ number_format($qi->total_price, 3) }}</td>
</tr>
@endforeach
</tbody>
</table>
</div>
</details>
</div>
@endforeach
</div>
@if(!$request->awardedQuote && $quotes->count() >= 1)
<div style="margin-top:20px;padding:14px;background:#fffbeb;border:1px solid #fde68a;border-radius:10px;display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:10px;">
<div style="font-size:13px;color:#92400e;font-weight:500;">Ready to pick a winner?</div>
<a href="{{ route('purchase.requests.compare', $request) }}"
style="padding:9px 20px;background:#f59e0b;color:#fff;border-radius:8px;font-size:13px;font-weight:700;text-decoration:none;">
Open Comparison Table →
</a>
</div>
@endif
@endif
</div>
</div>
</div>
@endsection
@@ -0,0 +1,186 @@
@extends('layouts.app')
@section('title', 'New Material Purchase Request')
@section('content')
<div class="mb-6">
<h1 class="page-title">New Material Purchase Request</h1>
<p class="page-subtitle"><a href="{{ route('purchase.requests.index') }}" class="text-blue-600 hover:underline">Purchase Requests</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('purchase.requests.store') }}" method="POST" id="mpr-form">
@csrf
{{-- Header Details --}}
<div class="card card-body mb-6">
<h2 class="text-base font-semibold text-gray-700 mb-4">Project / Department Details</h2>
<div class="grid grid-cols-1 gap-5 sm:grid-cols-2 lg:grid-cols-3">
<div>
<label class="form-label">Date <span class="text-red-500">*</span></label>
<input type="date" name="date" value="{{ old('date', date('Y-m-d')) }}" required class="form-input">
</div>
<div>
<label class="form-label">Project / Site Name <span class="text-red-500">*</span></label>
<input type="text" name="project_name" value="{{ old('project_name') }}" required class="form-input" placeholder="e.g. Steel Tech Co WLL">
</div>
<div>
<label class="form-label">Requested By <span class="text-red-500">*</span></label>
<input type="text" name="requested_by_name" value="{{ old('requested_by_name') }}" required class="form-input" placeholder="Person's name">
</div>
<div>
<label class="form-label">Required Date / Urgency</label>
<input type="text" name="required_date_text" value="{{ old('required_date_text') }}" class="form-input" placeholder="e.g. Urgent, or 2026-06-01">
</div>
<div>
<label class="form-label">Location / Site</label>
<input type="text" name="location" value="{{ old('location') }}" class="form-input" placeholder="e.g. BaFa - Hidd">
</div>
<div>
<label class="form-label">Department</label>
<input type="text" name="department" value="{{ old('department') }}" class="form-input" placeholder="e.g. Operations, Production">
</div>
</div>
</div>
{{-- Material Items --}}
<div class="card card-body mb-6">
<div class="flex items-center justify-between mb-4">
<h2 class="text-base font-semibold text-gray-700">Material Details</h2>
<button type="button" id="add-row" class="btn-primary btn-sm">+ Add Item</button>
</div>
<div class="overflow-x-auto">
<table class="w-full text-sm border-collapse" id="items-table">
<thead>
<tr class="bg-gray-50 text-gray-600 uppercase text-xs">
<th class="border border-gray-300 px-3 py-2 text-left w-10">S.No</th>
<th class="border border-gray-300 px-3 py-2 text-left">Description of Material <span class="text-red-500">*</span></th>
<th class="border border-gray-300 px-3 py-2 text-left w-24">Unit</th>
<th class="border border-gray-300 px-3 py-2 text-left w-28">Qty Required <span class="text-red-500">*</span></th>
<th class="border border-gray-300 px-3 py-2 text-left w-40">Purpose / Use</th>
<th class="border border-gray-300 px-3 py-2 text-left w-36">Required Date</th>
<th class="border border-gray-300 px-2 py-2 w-10"></th>
</tr>
</thead>
<tbody id="items-body">
@php $oldItems = old('items', [[]]); @endphp
@foreach($oldItems as $idx => $oldItem)
<tr class="item-row">
<td class="border border-gray-300 px-3 py-2 text-center text-gray-500 row-num">{{ $idx + 1 }}</td>
<td class="border border-gray-300 px-2 py-1">
<input type="text" name="items[{{ $idx }}][description]" value="{{ $oldItem['description'] ?? '' }}"
class="w-full border-0 focus:ring-0 text-sm" placeholder="Material description" required>
</td>
<td class="border border-gray-300 px-2 py-1">
<input type="text" name="items[{{ $idx }}][unit]" value="{{ $oldItem['unit'] ?? '' }}"
class="w-full border-0 focus:ring-0 text-sm" placeholder="PCS, NOS, KG…">
</td>
<td class="border border-gray-300 px-2 py-1">
<input type="number" name="items[{{ $idx }}][quantity_required]" value="{{ $oldItem['quantity_required'] ?? '' }}"
min="0.01" step="0.01" class="w-full border-0 focus:ring-0 text-sm" placeholder="0" required>
</td>
<td class="border border-gray-300 px-2 py-1">
<input type="text" name="items[{{ $idx }}][purpose_use]" value="{{ $oldItem['purpose_use'] ?? '' }}"
class="w-full border-0 focus:ring-0 text-sm" placeholder="Purpose…">
</td>
<td class="border border-gray-300 px-2 py-1">
<input type="date" name="items[{{ $idx }}][required_date]" value="{{ $oldItem['required_date'] ?? '' }}"
class="w-full border-0 focus:ring-0 text-sm">
</td>
<td class="border border-gray-300 px-2 py-1 text-center">
<button type="button" class="remove-row text-red-500 hover:text-red-700 font-bold text-lg leading-none">×</button>
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
{{-- Remarks --}}
<div class="card card-body mb-6">
<label class="form-label">Remarks / Notes</label>
<textarea name="remarks" rows="2" class="form-textarea">{{ old('remarks') }}</textarea>
</div>
<div class="flex items-center gap-3">
<button type="submit" class="btn-primary">Submit Request</button>
<a href="{{ route('purchase.requests.index') }}" class="btn-secondary">Cancel</a>
</div>
</form>
<script>
(function () {
let rowIndex = {{ count($oldItems ?? [[]]) }};
function renumber() {
document.querySelectorAll('#items-body .row-num').forEach((el, i) => {
el.textContent = i + 1;
});
}
function newRow() {
const idx = rowIndex++;
const tr = document.createElement('tr');
tr.className = 'item-row';
tr.innerHTML = `
<td class="border border-gray-300 px-3 py-2 text-center text-gray-500 row-num"></td>
<td class="border border-gray-300 px-2 py-1">
<input type="text" name="items[${idx}][description]" class="w-full border-0 focus:ring-0 text-sm" placeholder="Material description" required>
</td>
<td class="border border-gray-300 px-2 py-1">
<input type="text" name="items[${idx}][unit]" class="w-full border-0 focus:ring-0 text-sm" placeholder="PCS, NOS, KG…">
</td>
<td class="border border-gray-300 px-2 py-1">
<input type="number" name="items[${idx}][quantity_required]" min="0.01" step="0.01" class="w-full border-0 focus:ring-0 text-sm" placeholder="0" required>
</td>
<td class="border border-gray-300 px-2 py-1">
<input type="text" name="items[${idx}][purpose_use]" class="w-full border-0 focus:ring-0 text-sm" placeholder="Purpose…">
</td>
<td class="border border-gray-300 px-2 py-1">
<input type="date" name="items[${idx}][required_date]" class="w-full border-0 focus:ring-0 text-sm">
</td>
<td class="border border-gray-300 px-2 py-1 text-center">
<button type="button" class="remove-row text-red-500 hover:text-red-700 font-bold text-lg leading-none">×</button>
</td>
`;
return tr;
}
document.getElementById('add-row').addEventListener('click', function () {
const tbody = document.getElementById('items-body');
tbody.appendChild(newRow());
renumber();
});
document.getElementById('items-body').addEventListener('click', function (e) {
if (e.target.classList.contains('remove-row')) {
const rows = document.querySelectorAll('#items-body .item-row');
if (rows.length > 1) {
e.target.closest('tr').remove();
renumber();
}
}
});
renumber();
})();
</script>
@endsection
@@ -0,0 +1,212 @@
@extends('layouts.app')
@section('title', 'Edit Purchase Request')
@section('content')
<div class="mb-6">
<h1 class="page-title">Edit Purchase Request</h1>
<p class="page-subtitle">
<a href="{{ route('purchase.requests.index') }}" class="text-blue-600 hover:underline">Purchase Requests</a> /
<a href="{{ route('purchase.pipeline.show', $purchaseRequest) }}" class="text-blue-600 hover:underline">{{ $purchaseRequest->request_number }}</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
<form action="{{ route('purchase.requests.update', $purchaseRequest) }}" method="POST" id="mpr-form">
@csrf
@method('PUT')
{{-- Header Details --}}
<div class="card card-body mb-6">
<h2 class="text-base font-semibold text-gray-700 mb-4">Project / Department Details</h2>
<div class="grid grid-cols-1 gap-5 sm:grid-cols-2 lg:grid-cols-3">
<div>
<label class="form-label">Date <span class="text-red-500">*</span></label>
<input type="date" name="date"
value="{{ old('date', $purchaseRequest->date ? \Carbon\Carbon::parse($purchaseRequest->date)->format('Y-m-d') : '') }}"
required class="form-input">
</div>
<div>
<label class="form-label">Project / Site Name <span class="text-red-500">*</span></label>
<input type="text" name="project_name"
value="{{ old('project_name', $purchaseRequest->project_name) }}"
required class="form-input" placeholder="e.g. Steel Tech Co WLL">
</div>
<div>
<label class="form-label">Requested By <span class="text-red-500">*</span></label>
<input type="text" name="requested_by_name"
value="{{ old('requested_by_name', $purchaseRequest->requested_by_name) }}"
required class="form-input" placeholder="Person's name">
</div>
<div>
<label class="form-label">Required Date / Urgency</label>
<input type="text" name="required_date_text"
value="{{ old('required_date_text', $purchaseRequest->required_date_text) }}"
class="form-input" placeholder="e.g. Urgent, or 2026-06-01">
</div>
<div>
<label class="form-label">Location / Site</label>
<input type="text" name="location"
value="{{ old('location', $purchaseRequest->location) }}"
class="form-input" placeholder="e.g. BaFa - Hidd">
</div>
<div>
<label class="form-label">Department</label>
<input type="text" name="department"
value="{{ old('department', $purchaseRequest->department) }}"
class="form-input" placeholder="e.g. Operations, Production">
</div>
</div>
</div>
{{-- Material Items --}}
<div class="card card-body mb-6">
<div class="flex items-center justify-between mb-4">
<h2 class="text-base font-semibold text-gray-700">Material Details</h2>
<button type="button" id="add-row" class="btn-primary btn-sm">+ Add Item</button>
</div>
<div class="overflow-x-auto">
<table class="w-full text-sm border-collapse" id="items-table">
<thead>
<tr class="bg-gray-50 text-gray-600 uppercase text-xs">
<th class="border border-gray-300 px-3 py-2 text-left w-10">S.No</th>
<th class="border border-gray-300 px-3 py-2 text-left">Description of Material <span class="text-red-500">*</span></th>
<th class="border border-gray-300 px-3 py-2 text-left w-24">Unit</th>
<th class="border border-gray-300 px-3 py-2 text-left w-28">Qty Required <span class="text-red-500">*</span></th>
<th class="border border-gray-300 px-3 py-2 text-left w-40">Purpose / Use</th>
<th class="border border-gray-300 px-3 py-2 text-left w-36">Required Date</th>
<th class="border border-gray-300 px-2 py-2 w-10"></th>
</tr>
</thead>
<tbody id="items-body">
@php
$existingItems = old('items')
? collect(old('items'))->map(fn($i) => (object)$i)
: $purchaseRequest->items;
@endphp
@foreach($existingItems as $idx => $item)
<tr class="item-row">
<td class="border border-gray-300 px-3 py-2 text-center text-gray-500 row-num">{{ $idx + 1 }}</td>
<td class="border border-gray-300 px-2 py-1">
<input type="text" name="items[{{ $idx }}][description]"
value="{{ is_array($item) ? ($item['description'] ?? '') : $item->description }}"
class="w-full border-0 focus:ring-0 text-sm" placeholder="Material description" required>
</td>
<td class="border border-gray-300 px-2 py-1">
<input type="text" name="items[{{ $idx }}][unit]"
value="{{ is_array($item) ? ($item['unit'] ?? '') : $item->unit }}"
class="w-full border-0 focus:ring-0 text-sm" placeholder="PCS, NOS, KG…">
</td>
<td class="border border-gray-300 px-2 py-1">
<input type="number" name="items[{{ $idx }}][quantity_required]"
value="{{ is_array($item) ? ($item['quantity_required'] ?? '') : $item->quantity_required }}"
min="0.01" step="0.01" class="w-full border-0 focus:ring-0 text-sm" placeholder="0" required>
</td>
<td class="border border-gray-300 px-2 py-1">
<input type="text" name="items[{{ $idx }}][purpose_use]"
value="{{ is_array($item) ? ($item['purpose_use'] ?? '') : $item->purpose_use }}"
class="w-full border-0 focus:ring-0 text-sm" placeholder="Purpose…">
</td>
<td class="border border-gray-300 px-2 py-1">
<input type="date" name="items[{{ $idx }}][required_date]"
value="{{ is_array($item) ? ($item['required_date'] ?? '') : ($item->required_date ? \Carbon\Carbon::parse($item->required_date)->format('Y-m-d') : '') }}"
class="w-full border-0 focus:ring-0 text-sm">
</td>
<td class="border border-gray-300 px-2 py-1 text-center">
<button type="button" class="remove-row text-red-500 hover:text-red-700 font-bold text-lg leading-none">×</button>
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
{{-- Remarks --}}
<div class="card card-body mb-6">
<label class="form-label">Remarks / Notes</label>
<textarea name="remarks" rows="2" class="form-textarea">{{ old('remarks', $purchaseRequest->remarks) }}</textarea>
</div>
<div class="flex items-center gap-3">
<button type="submit" class="btn-primary">Save Changes</button>
<a href="{{ route('purchase.pipeline.show', $purchaseRequest) }}" class="btn-secondary">Cancel</a>
</div>
</form>
<script>
(function () {
let rowIndex = {{ $existingItems->count() }};
function renumber() {
document.querySelectorAll('#items-body .row-num').forEach((el, i) => {
el.textContent = i + 1;
});
}
function newRow() {
const idx = rowIndex++;
const tr = document.createElement('tr');
tr.className = 'item-row';
tr.innerHTML = `
<td class="border border-gray-300 px-3 py-2 text-center text-gray-500 row-num"></td>
<td class="border border-gray-300 px-2 py-1">
<input type="text" name="items[${idx}][description]" class="w-full border-0 focus:ring-0 text-sm" placeholder="Material description" required>
</td>
<td class="border border-gray-300 px-2 py-1">
<input type="text" name="items[${idx}][unit]" class="w-full border-0 focus:ring-0 text-sm" placeholder="PCS, NOS, KG…">
</td>
<td class="border border-gray-300 px-2 py-1">
<input type="number" name="items[${idx}][quantity_required]" min="0.01" step="0.01" class="w-full border-0 focus:ring-0 text-sm" placeholder="0" required>
</td>
<td class="border border-gray-300 px-2 py-1">
<input type="text" name="items[${idx}][purpose_use]" class="w-full border-0 focus:ring-0 text-sm" placeholder="Purpose…">
</td>
<td class="border border-gray-300 px-2 py-1">
<input type="date" name="items[${idx}][required_date]" class="w-full border-0 focus:ring-0 text-sm">
</td>
<td class="border border-gray-300 px-2 py-1 text-center">
<button type="button" class="remove-row text-red-500 hover:text-red-700 font-bold text-lg leading-none">×</button>
</td>
`;
return tr;
}
document.getElementById('add-row').addEventListener('click', function () {
const tbody = document.getElementById('items-body');
tbody.appendChild(newRow());
renumber();
});
document.getElementById('items-body').addEventListener('click', function (e) {
if (e.target.classList.contains('remove-row')) {
const rows = document.querySelectorAll('#items-body .item-row');
if (rows.length > 1) {
e.target.closest('tr').remove();
renumber();
}
}
});
renumber();
})();
</script>
@endsection
@@ -0,0 +1,87 @@
@extends('layouts.app')
@section('title', 'Purchase Requests')
@section('content')
<div class="page-header">
<div>
<h1 class="page-title">Purchase Requests</h1>
<p class="page-subtitle">Manage internal purchase requests</p>
</div>
<a href="{{ route('purchase.requests.create') }}" class="btn-primary">
+ New Request
</a>
</div>
<div class="table-wrapper overflow-x-auto">
<table class="table-base">
<thead>
<tr>
<th>Request #</th>
<th>Date</th>
<th>Department</th>
<th>Item</th>
<th>Quantity</th>
<th>Status</th>
<th>Requested By</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
@forelse($requests as $request)
<tr>
<td class="font-mono text-gray-700">{{ $request->request_number ?? '#' . $request->id }}</td>
<td>{{ $request->date ? $request->date->format('d M Y') : '' }}</td>
<td>{{ $request->department }}</td>
<td class="text-gray-800">{{ $request->item->item_name ?? $request->item_name }}</td>
<td>{{ $request->quantity }} {{ $request->unit_of_measure }}</td>
<td>
@php
$badgeClass = match($request->status) {
'pending' => 'badge-yellow',
'approved' => 'badge-green',
'rejected' => 'badge-red',
'ordered' => 'badge-blue',
default => 'badge-gray',
};
@endphp
<span class="{{ $badgeClass }}">{{ ucfirst($request->status) }}</span>
</td>
<td>{{ $request->requestedBy->name ?? $request->requested_by }}</td>
<td>
<div class="flex items-center gap-2 flex-wrap">
@if($request->status === 'pending')
<form action="{{ route('purchase.requests.approve', $request) }}" method="POST">
@csrf
@method('PATCH')
<button type="submit" class="btn-success btn-sm">Approve</button>
</form>
<form action="{{ route('purchase.requests.reject', $request) }}" method="POST">
@csrf
@method('PATCH')
<button type="submit" class="btn-danger btn-sm">Reject</button>
</form>
@endif
<a href="{{ route('purchase.requests.edit', $request) }}" class="btn-secondary btn-sm">Edit</a>
<form action="{{ route('purchase.requests.destroy', $request) }}" method="POST"
onsubmit="confirmDelete(this,'Delete this purchase request?','This request 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="8" class="px-4 py-8 text-center text-gray-400">No purchase requests found.</td>
</tr>
@endforelse
</tbody>
</table>
</div>
@if($requests->hasPages())
<div class="mt-4">{{ $requests->links() }}</div>
@endif
@endsection
@@ -0,0 +1,133 @@
@extends('layouts.app')
@section('title', 'MPR ' . $purchaseRequest->request_number)
@section('content')
<div class="mb-6 flex items-start justify-between flex-wrap gap-3">
<div>
<h1 class="page-title">{{ $purchaseRequest->request_number }}</h1>
<p class="page-subtitle"><a href="{{ route('purchase.requests.index') }}" class="text-blue-600 hover:underline">Purchase Requests</a> / {{ $purchaseRequest->request_number }}</p>
</div>
<div class="flex items-center gap-2 flex-wrap">
<a href="{{ route('purchase.requests.print', $purchaseRequest) }}" target="_blank"
class="btn-primary">Print MPR Form</a>
@if($purchaseRequest->status === 'pending')
<a href="{{ route('purchase.requests.edit', $purchaseRequest) }}" class="btn-secondary">Edit</a>
<form action="{{ route('purchase.requests.approve', $purchaseRequest) }}" method="POST">
@csrf @method('PATCH')
<button type="submit" class="btn-success">Approve</button>
</form>
<form action="{{ route('purchase.requests.reject', $purchaseRequest) }}" method="POST">
@csrf @method('PATCH')
<button type="submit" class="btn-danger">Reject</button>
</form>
@endif
</div>
</div>
{{-- Status Badge --}}
<div class="mb-4">
@php
$badgeClass = match($purchaseRequest->status) {
'pending' => 'badge-yellow',
'approved' => 'badge-green',
'rejected' => 'badge-red',
'ordered' => 'badge-blue',
default => 'badge-gray',
};
@endphp
<span class="text-sm font-medium">Status:</span>
<span class="{{ $badgeClass }} ml-1">{{ ucfirst($purchaseRequest->status) }}</span>
</div>
{{-- Header Info --}}
<div class="card card-body mb-6">
<h2 class="text-sm font-semibold text-gray-500 uppercase tracking-wide mb-4">Project / Department Details</h2>
<div class="grid grid-cols-2 sm:grid-cols-3 gap-4 text-sm">
<div>
<p class="text-gray-500">MPR Number</p>
<p class="font-semibold text-gray-800">{{ $purchaseRequest->request_number }}</p>
</div>
<div>
<p class="text-gray-500">Date</p>
<p class="font-semibold text-gray-800">{{ $purchaseRequest->date->format('d-m-Y') }}</p>
</div>
<div>
<p class="text-gray-500">Project / Site Name</p>
<p class="font-semibold text-gray-800">{{ $purchaseRequest->project_name ?? '—' }}</p>
</div>
<div>
<p class="text-gray-500">Requested By</p>
<p class="font-semibold text-gray-800">{{ $purchaseRequest->requested_by_name ?? $purchaseRequest->requestedBy?->name ?? '—' }}</p>
</div>
<div>
<p class="text-gray-500">Required Date</p>
<p class="font-semibold text-gray-800">{{ $purchaseRequest->required_date_text ?? '—' }}</p>
</div>
<div>
<p class="text-gray-500">Location / Site</p>
<p class="font-semibold text-gray-800">{{ $purchaseRequest->location ?? '—' }}</p>
</div>
@if($purchaseRequest->department)
<div>
<p class="text-gray-500">Department</p>
<p class="font-semibold text-gray-800">{{ $purchaseRequest->department }}</p>
</div>
@endif
@if($purchaseRequest->remarks)
<div class="col-span-2 sm:col-span-3">
<p class="text-gray-500">Remarks</p>
<p class="font-semibold text-gray-800">{{ $purchaseRequest->remarks }}</p>
</div>
@endif
</div>
</div>
{{-- Material Items --}}
<div class="card card-body mb-6">
<h2 class="text-sm font-semibold text-gray-500 uppercase tracking-wide mb-4">Material Details</h2>
<div class="overflow-x-auto">
<table class="table-base">
<thead>
<tr>
<th class="w-12">S.No</th>
<th>Description of Material</th>
<th>Unit</th>
<th>Qty Required</th>
<th>Purpose / Use</th>
<th>Required Date</th>
</tr>
</thead>
<tbody>
@foreach($purchaseRequest->items as $i => $item)
<tr>
<td class="text-center">{{ $i + 1 }}</td>
<td class="font-medium">{{ $item->description }}</td>
<td>{{ $item->unit ?? '—' }}</td>
<td>{{ number_format($item->quantity_required, 2) }}</td>
<td>{{ $item->purpose_use ?? '—' }}</td>
<td>{{ $item->required_date ? $item->required_date->format('d-m-Y') : '—' }}</td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
{{-- Approval Info --}}
@if($purchaseRequest->approvedBy)
<div class="card card-body">
<h2 class="text-sm font-semibold text-gray-500 uppercase tracking-wide mb-3">Approval Info</h2>
<div class="grid grid-cols-2 gap-4 text-sm">
<div>
<p class="text-gray-500">Approved By</p>
<p class="font-semibold text-gray-800">{{ $purchaseRequest->approvedBy->name }}</p>
</div>
<div>
<p class="text-gray-500">Approved At</p>
<p class="font-semibold text-gray-800">{{ $purchaseRequest->approved_at?->format('d M Y, H:i') ?? '—' }}</p>
</div>
</div>
</div>
@endif
@endsection
+156
View File
@@ -0,0 +1,156 @@
@extends('layouts.app')
@section('title', 'Send RFQ — ' . $request->request_number)
@section('content')
<div style="max-width:760px;margin:0 auto;">
<div style="background:#fff;border-radius:16px;box-shadow:0 4px 24px rgba(0,0,0,.08);overflow:hidden;">
<!-- Header -->
<div style="background:linear-gradient(135deg,#0ea5e9,#0284c7);padding:24px 28px;">
<div style="font-size:11px;font-weight:600;color:rgba(255,255,255,.7);text-transform:uppercase;letter-spacing:.06em;">Send RFQ Invitations</div>
<div style="font-size:20px;font-weight:700;color:#fff;margin-top:4px;">{{ $request->request_number }}</div>
@if($request->project_name)
<div style="font-size:13px;color:rgba(255,255,255,.8);margin-top:2px;">{{ $request->project_name }}</div>
@endif
</div>
<div style="padding:28px;">
{{-- Already invited --}}
@if($invitations->count())
<div style="margin-bottom:24px;">
<div style="font-size:11px;font-weight:700;color:#64748b;text-transform:uppercase;letter-spacing:.05em;margin-bottom:10px;">Already Invited</div>
<div style="display:flex;flex-direction:column;gap:8px;">
@foreach($invitations as $inv)
@php
$statusColors = [
'sent' => ['bg'=>'#eff6ff','border'=>'#bfdbfe','color'=>'#1d4ed8'],
'opened' => ['bg'=>'#fffbeb','border'=>'#fde68a','color'=>'#92400e'],
'submitted' => ['bg'=>'#f0fdf4','border'=>'#bbf7d0','color'=>'#15803d'],
'declined' => ['bg'=>'#fef2f2','border'=>'#fecaca','color'=>'#dc2626'],
'pending' => ['bg'=>'#f8fafc','border'=>'#e2e8f0','color'=>'#64748b'],
];
$sc = $statusColors[$inv->status] ?? $statusColors['pending'];
@endphp
<div style="display:flex;align-items:center;justify-content:space-between;background:{{ $sc['bg'] }};border:1px solid {{ $sc['border'] }};border-radius:10px;padding:12px 16px;">
<div>
<div style="font-size:13px;font-weight:600;color:#0f172a;">{{ $inv->supplier->name }}</div>
<div style="font-size:11px;color:#64748b;margin-top:2px;">
via {{ ucfirst($inv->channel) }}
@if($inv->sent_at) · sent {{ $inv->sent_at->format('d M Y') }} @endif
</div>
</div>
<div style="display:flex;align-items:center;gap:8px;">
@if(in_array($inv->channel, ['whatsapp', 'both']))
@php $wsLink = app(\App\Services\RfqInvitationService::class)->whatsappLink($inv); @endphp
<a href="{{ $wsLink }}" target="_blank"
style="font-size:11px;font-weight:700;padding:4px 10px;background:#25d366;color:#fff;border-radius:6px;text-decoration:none;">
WhatsApp
</a>
@endif
<span style="font-size:11px;font-weight:700;padding:4px 12px;border-radius:20px;background:{{ $sc['bg'] }};color:{{ $sc['color'] }};border:1px solid {{ $sc['border'] }};">
{{ ucfirst($inv->status) }}
</span>
</div>
</div>
@endforeach
</div>
</div>
@endif
{{-- Invite more suppliers --}}
<div style="font-size:11px;font-weight:700;color:#64748b;text-transform:uppercase;letter-spacing:.05em;margin-bottom:10px;">
{{ $invitations->count() ? 'Add More Suppliers' : 'Select Suppliers' }}
</div>
<input type="text" id="supplier-search" placeholder="Search suppliers…" oninput="filterSuppliers(this.value)"
style="width:100%;padding:9px 12px;border:1px solid #e2e8f0;border-radius:8px;font-size:13px;margin-bottom:12px;box-sizing:border-box;">
<form method="POST" action="{{ route('purchase.requests.rfq.store', $request) }}" id="rfq-form">
@csrf
<input type="hidden" name="_token" value="{{ csrf_token() }}">
<div id="supplier-list" style="display:flex;flex-direction:column;gap:6px;max-height:400px;overflow-y:auto;padding-right:2px;">
@php $alreadyIds = $invitations->pluck('supplier_id')->toArray(); @endphp
@forelse($suppliers as $supplier)
@php $already = in_array($supplier->id, $alreadyIds); @endphp
<div class="sup-row" data-name="{{ strtolower($supplier->name) }}"
style="border:1.5px solid #e2e8f0;border-radius:10px;padding:12px 14px;transition:border-color .15s;{{ $already ? 'opacity:.45;' : '' }}">
<label style="display:flex;align-items:center;gap:12px;cursor:{{ $already ? 'default' : 'pointer' }};">
<input type="checkbox" name="supplier_ids[]" value="{{ $supplier->id }}"
data-sid="{{ $supplier->id }}" onchange="toggleRow(this)"
{{ $already ? 'disabled' : '' }}
style="width:17px;height:17px;cursor:pointer;flex-shrink:0;">
<div style="flex:1;min-width:0;">
<div style="font-size:13px;font-weight:600;color:#0f172a;">{{ $supplier->name }}</div>
<div style="font-size:11px;color:#64748b;margin-top:1px;">
{{ $supplier->email ?: '—' }} · {{ $supplier->phone ?: '—' }}
</div>
</div>
@if($already)
<span style="font-size:11px;font-weight:600;color:#64748b;background:#f1f5f9;padding:3px 10px;border-radius:20px;">Already invited</span>
@endif
</label>
{{-- Channel selector (hidden until checked) --}}
<div id="ch-{{ $supplier->id }}" style="display:none;margin-top:10px;padding-top:10px;border-top:1px dashed #e2e8f0;">
<div style="font-size:11px;font-weight:700;color:#64748b;margin-bottom:8px;">Send via:</div>
<div style="display:flex;gap:8px;">
@foreach(['whatsapp' => 'WhatsApp', 'email' => 'Email', 'both' => 'Both'] as $val => $lbl)
<label style="flex:1;cursor:pointer;">
<input type="radio" name="channel_{{ $supplier->id }}" value="{{ $val }}" {{ $val === 'both' ? 'checked' : '' }}
style="display:none;" id="ch-{{ $supplier->id }}-{{ $val }}"
onchange="styleChannelBtns({{ $supplier->id }})">
<span id="chbtn-{{ $supplier->id }}-{{ $val }}"
onclick="document.getElementById('ch-{{ $supplier->id }}-{{ $val }}').checked=true;styleChannelBtns({{ $supplier->id }});"
style="display:block;text-align:center;padding:7px 6px;border-radius:7px;font-size:12px;font-weight:600;cursor:pointer;
border:1.5px solid {{ $val === 'both' ? '#0ea5e9' : '#e2e8f0' }};
background:{{ $val === 'both' ? '#eff6ff' : '#fff' }};
color:{{ $val === 'both' ? '#0284c7' : '#64748b' }};">
{{ $lbl }}
</span>
</label>
@endforeach
</div>
</div>
</div>
@empty
<p style="text-align:center;color:#94a3b8;padding:24px;">No active suppliers found. Add suppliers first.</p>
@endforelse
</div>
<button type="submit"
style="width:100%;margin-top:20px;padding:13px;background:linear-gradient(135deg,#0ea5e9,#0284c7);color:#fff;border:none;border-radius:9px;font-size:14px;font-weight:700;cursor:pointer;">
Send Invitations →
</button>
</form>
</div>
</div>
</div>
<script>
function filterSuppliers(q) {
document.querySelectorAll('.sup-row').forEach(function(row) {
row.style.display = row.dataset.name.includes(q.toLowerCase()) ? '' : 'none';
});
}
function toggleRow(cb) {
var ch = document.getElementById('ch-' + cb.dataset.sid);
if (ch) ch.style.display = cb.checked ? 'block' : 'none';
}
function styleChannelBtns(sid) {
['whatsapp','email','both'].forEach(function(v) {
var inp = document.getElementById('ch-' + sid + '-' + v);
var btn = document.getElementById('chbtn-' + sid + '-' + v);
if (!inp || !btn) return;
if (inp.checked) {
btn.style.borderColor = '#0ea5e9'; btn.style.background = '#eff6ff'; btn.style.color = '#0284c7';
} else {
btn.style.borderColor = '#e2e8f0'; btn.style.background = '#fff'; btn.style.color = '#64748b';
}
});
}
</script>
@endsection
@@ -0,0 +1,122 @@
@extends('layouts.app')
@section('title', 'GM Signature — ' . $request->request_number)
@section('content')
<div style="max-width:640px;margin:0 auto;">
<div style="background:#fff;border-radius:16px;box-shadow:0 4px 24px rgba(0,0,0,.08);overflow:hidden;">
<!-- Header -->
<div style="background:linear-gradient(135deg,#7c3aed,#4f46e5);padding:24px 28px;">
<div style="font-size:11px;font-weight:600;color:rgba(255,255,255,.7);text-transform:uppercase;letter-spacing:.06em;">GM Digital Signature</div>
<div style="font-size:20px;font-weight:700;color:#fff;margin-top:4px;">{{ $request->request_number }}</div>
@if($request->project_name)
<div style="font-size:13px;color:rgba(255,255,255,.8);margin-top:2px;">{{ $request->project_name }}</div>
@endif
</div>
<div style="padding:28px;">
@if($request->signature)
<!-- Already signed -->
<div style="text-align:center;margin-bottom:8px;">
<img src="{{ $request->signature->signature_image }}"
style="max-width:100%;border:1px solid #e2e8f0;border-radius:10px;background:#f8fafc;">
<div style="font-size:12px;color:#64748b;margin-top:10px;">
Signed by <strong>{{ $request->signature->signedBy->name }}</strong>
on {{ $request->signature->signed_at->format('d M Y, H:i') }}
</div>
</div>
<a href="{{ route('purchase.pipeline.index') }}"
style="display:block;text-align:center;margin-top:20px;padding:10px;background:#f1f5f9;color:#475569;border-radius:8px;font-size:13px;font-weight:600;text-decoration:none;">
← Back to Pipeline
</a>
@else
<!-- Signature pad -->
<p style="font-size:13px;color:#475569;margin-bottom:16px;">
Draw your signature below using your mouse or touch screen to approve this purchase request.
</p>
<div style="position:relative;">
<canvas id="sig-canvas" width="580" height="200"
style="width:100%;border:2px dashed #cbd5e1;border-radius:10px;cursor:crosshair;touch-action:none;background:#fafafa;display:block;">
</canvas>
<div id="sig-hint" style="position:absolute;inset:0;display:flex;align-items:center;justify-content:center;pointer-events:none;">
<span style="font-size:13px;color:#cbd5e1;font-style:italic;">Sign here</span>
</div>
</div>
<div style="display:flex;gap:10px;margin-top:14px;">
<button type="button" onclick="clearCanvas()"
style="flex:1;padding:11px;border:1.5px solid #e2e8f0;border-radius:8px;font-size:13px;font-weight:600;color:#64748b;background:#f8fafc;cursor:pointer;">
Clear
</button>
<button type="button" onclick="submitSignature()"
style="flex:2;padding:11px;border:none;border-radius:8px;font-size:13px;font-weight:700;color:#fff;background:linear-gradient(135deg,#7c3aed,#4f46e5);cursor:pointer;">
Confirm Signature →
</button>
</div>
<form id="sig-form" method="POST" action="{{ route('purchase.requests.sign.store', $request) }}">
@csrf
<input type="hidden" name="signature_image" id="sig-data">
</form>
<p style="font-size:11px;color:#94a3b8;margin-top:14px;text-align:center;">
By signing, you approve this purchase request. This action is recorded with your name, timestamp, and IP address.
</p>
@endif
</div>
</div>
</div>
<script>
(function () {
const canvas = document.getElementById('sig-canvas');
if (!canvas) return;
const ctx = canvas.getContext('2d');
const hint = document.getElementById('sig-hint');
let drawing = false;
let hasMark = false;
ctx.lineWidth = 2.5;
ctx.lineCap = 'round';
ctx.lineJoin = 'round';
ctx.strokeStyle = '#1e293b';
function pos(e) {
const r = canvas.getBoundingClientRect();
const sx = canvas.width / r.width;
const sy = canvas.height / r.height;
const src = e.touches ? e.touches[0] : e;
return { x: (src.clientX - r.left) * sx, y: (src.clientY - r.top) * sy };
}
function start(e) { e.preventDefault(); drawing = true; const p = pos(e); ctx.beginPath(); ctx.moveTo(p.x, p.y); if (!hasMark) { hasMark = true; if (hint) hint.style.display = 'none'; } }
function move(e) { e.preventDefault(); if (!drawing) return; const p = pos(e); ctx.lineTo(p.x, p.y); ctx.stroke(); ctx.beginPath(); ctx.moveTo(p.x, p.y); }
function end() { drawing = false; }
canvas.addEventListener('mousedown', start);
canvas.addEventListener('mousemove', move);
canvas.addEventListener('mouseup', end);
canvas.addEventListener('mouseleave', end);
canvas.addEventListener('touchstart', start, { passive: false });
canvas.addEventListener('touchmove', move, { passive: false });
canvas.addEventListener('touchend', end);
window.clearCanvas = function () {
ctx.clearRect(0, 0, canvas.width, canvas.height);
hasMark = false;
if (hint) hint.style.display = 'flex';
};
window.submitSignature = function () {
if (!hasMark) { showToast('Please draw your signature first.', 'warn'); return; }
document.getElementById('sig-data').value = canvas.toDataURL('image/png');
document.getElementById('sig-form').submit();
};
})();
</script>
@endsection
@@ -0,0 +1,71 @@
@extends('layouts.app')
@section('title', 'Add Supplier')
@section('content')
<div class="mb-6">
<h1 class="page-title">Add Supplier</h1>
<p class="page-subtitle"><a href="{{ route('purchase.suppliers.index') }}" class="text-blue-600 hover:underline">Suppliers</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
<div class="card card-body max-w-2xl">
<form action="{{ route('purchase.suppliers.store') }}" method="POST">
@csrf
<div class="grid grid-cols-1 gap-5 sm:grid-cols-2">
<div class="sm:col-span-2">
<label class="form-label">Supplier Name <span class="text-red-500">*</span></label>
<input type="text" name="name" value="{{ old('name') }}" required class="form-input">
</div>
<div>
<label class="form-label">Contact Person</label>
<input type="text" name="contact_person" value="{{ old('contact_person') }}" class="form-input">
</div>
<div>
<label class="form-label">Email</label>
<input type="email" name="email" value="{{ old('email') }}" class="form-input">
</div>
<div>
<label class="form-label">Phone</label>
<input type="text" name="phone" value="{{ old('phone') }}" class="form-input">
</div>
<div>
<label class="form-label">Tax Number</label>
<input type="text" name="tax_number" value="{{ old('tax_number') }}" class="form-input">
</div>
<div class="sm:col-span-2">
<label class="form-label">Address</label>
<textarea name="address" rows="3" class="form-textarea">{{ old('address') }}</textarea>
</div>
<div class="sm:col-span-2 flex items-center gap-2">
<input type="checkbox" name="is_active" id="is_active" value="1"
{{ old('is_active', '1') ? 'checked' : '' }}
class="h-4 w-4 text-blue-600 border-gray-300 rounded">
<label for="is_active" class="form-label mb-0">Active</label>
</div>
</div>
<div class="mt-6 flex items-center gap-3">
<button type="submit" class="btn-primary">Save Supplier</button>
<a href="{{ route('purchase.suppliers.index') }}" class="btn-secondary">Cancel</a>
</div>
</form>
</div>
@endsection
@@ -0,0 +1,72 @@
@extends('layouts.app')
@section('title', 'Edit Supplier')
@section('content')
<div class="mb-6">
<h1 class="page-title">Edit Supplier</h1>
<p class="page-subtitle"><a href="{{ route('purchase.suppliers.index') }}" class="text-blue-600 hover:underline">Suppliers</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('purchase.suppliers.update', $supplier) }}" method="POST">
@csrf
@method('PUT')
<div class="grid grid-cols-1 gap-5 sm:grid-cols-2">
<div class="sm:col-span-2">
<label class="form-label">Supplier Name <span class="text-red-500">*</span></label>
<input type="text" name="name" value="{{ old('name', $supplier->name) }}" required class="form-input">
</div>
<div>
<label class="form-label">Contact Person</label>
<input type="text" name="contact_person" value="{{ old('contact_person', $supplier->contact_person) }}" class="form-input">
</div>
<div>
<label class="form-label">Email</label>
<input type="email" name="email" value="{{ old('email', $supplier->email) }}" class="form-input">
</div>
<div>
<label class="form-label">Phone</label>
<input type="text" name="phone" value="{{ old('phone', $supplier->phone) }}" class="form-input">
</div>
<div>
<label class="form-label">Tax Number</label>
<input type="text" name="tax_number" value="{{ old('tax_number', $supplier->tax_number) }}" class="form-input">
</div>
<div class="sm:col-span-2">
<label class="form-label">Address</label>
<textarea name="address" rows="3" class="form-textarea">{{ old('address', $supplier->address) }}</textarea>
</div>
<div class="sm:col-span-2 flex items-center gap-2">
<input type="checkbox" name="is_active" id="is_active" value="1"
{{ old('is_active', $supplier->is_active) ? 'checked' : '' }}
class="h-4 w-4 text-blue-600 border-gray-300 rounded">
<label for="is_active" class="form-label mb-0">Active</label>
</div>
</div>
<div class="mt-6 flex items-center gap-3">
<button type="submit" class="btn-primary">Update Supplier</button>
<a href="{{ route('purchase.suppliers.index') }}" class="btn-secondary">Cancel</a>
</div>
</form>
</div>
@endsection
File diff suppressed because it is too large. Load diff
@@ -0,0 +1,198 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Suppliers List</title>
<style>
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
font-family: 'DejaVu Sans', 'Arial', sans-serif;
font-size: 11px;
color: #1e293b;
background: #fff;
}
/* ── Header ── */
.header {
display: flex;
justify-content: space-between;
align-items: flex-start;
padding: 24px 28px 16px;
border-bottom: 2px solid #2563eb;
margin-bottom: 20px;
}
.brand { display: flex; align-items: center; gap: 10px; }
.brand-box {
width: 36px; height: 36px; border-radius: 8px;
background: #2563eb;
display: flex; align-items: center; justify-content: center;
}
.brand-name { font-size: 17px; font-weight: 700; color: #0f172a; }
.brand-sub { font-size: 9px; color: #64748b; margin-top: 1px; }
.report-info { text-align: right; }
.report-title { font-size: 15px; font-weight: 700; color: #1e293b; }
.report-meta { font-size: 9px; color: #64748b; margin-top: 3px; }
/* ── Summary strip ── */
.summary {
display: flex;
gap: 12px;
margin: 0 28px 16px;
}
.summary-card {
flex: 1;
padding: 10px 14px;
background: #f1f5f9;
border-radius: 8px;
border-left: 3px solid #2563eb;
}
.summary-card .val { font-size: 20px; font-weight: 700; color: #2563eb; }
.summary-card .lbl { font-size: 9px; color: #64748b; margin-top: 2px; }
.summary-card.green { border-color: #10b981; }
.summary-card.green .val { color: #10b981; }
.summary-card.gray { border-color: #94a3b8; }
.summary-card.gray .val { color: #94a3b8; }
/* ── Table ── */
.table-wrap { padding: 0 28px; }
table {
width: 100%;
border-collapse: collapse;
font-size: 10.5px;
}
thead th {
background: #1e293b;
color: #fff;
font-weight: 600;
font-size: 9px;
letter-spacing: .06em;
text-transform: uppercase;
padding: 8px 10px;
text-align: left;
}
tbody tr:nth-child(even) { background: #f8fafc; }
tbody tr:nth-child(odd) { background: #ffffff; }
tbody td {
padding: 7px 10px;
color: #334155;
border-bottom: 1px solid #e2e8f0;
vertical-align: middle;
}
tbody td:first-child { font-weight: 600; color: #0f172a; }
.badge {
display: inline-block;
padding: 2px 7px;
border-radius: 20px;
font-size: 8.5px;
font-weight: 700;
letter-spacing: .04em;
}
.badge-active { background: #d1fae5; color: #065f46; }
.badge-inactive { background: #f1f5f9; color: #64748b; }
/* ── Footer ── */
.footer {
margin-top: 24px;
padding: 12px 28px 0;
border-top: 1px solid #e2e8f0;
display: flex;
justify-content: space-between;
font-size: 9px;
color: #94a3b8;
}
</style>
</head>
<body>
<div class="header">
<div class="brand">
<div class="brand-box">
<svg width="18" height="18" fill="none" stroke="#fff" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
d="M19 21V5a2 2 0 00-2-2H7a2 2 0 00-2 2v16m14 0h2m-2 0h-5m-9 0H3m2 0h5M9 7h1m-1 4h1m4-4h1m-1 4h1m-5 10v-5a1 1 0 011-1h2a1 1 0 011 1v5m-4 0h4"/>
</svg>
</div>
<div>
<div class="brand-name">SteelERP</div>
<div class="brand-sub">Manufacturing &amp; Trading</div>
</div>
</div>
<div class="report-info">
<div class="report-title">Supplier Directory</div>
<div class="report-meta">Generated: {{ now()->format('d M Y, H:i') }}</div>
<div class="report-meta">Total Records: {{ $suppliers->count() }}</div>
</div>
</div>
@php
$active = $suppliers->where('is_active', true)->count();
$inactive = $suppliers->where('is_active', false)->count();
@endphp
<div class="summary">
<div class="summary-card">
<div class="val">{{ $suppliers->count() }}</div>
<div class="lbl">Total Suppliers</div>
</div>
<div class="summary-card green">
<div class="val">{{ $active }}</div>
<div class="lbl">Active</div>
</div>
<div class="summary-card gray">
<div class="val">{{ $inactive }}</div>
<div class="lbl">Inactive</div>
</div>
</div>
<div class="table-wrap">
<table>
<thead>
<tr>
<th style="width:4%">#</th>
<th style="width:22%">Supplier Name</th>
<th style="width:17%">Contact Person</th>
<th style="width:20%">Email</th>
<th style="width:14%">Phone</th>
<th style="width:15%">Tax Number</th>
<th style="width:8%">Status</th>
</tr>
</thead>
<tbody>
@forelse($suppliers as $i => $supplier)
<tr>
<td style="color:#94a3b8;">{{ $i + 1 }}</td>
<td>{{ $supplier->name }}</td>
<td>{{ $supplier->contact_person ?: '—' }}</td>
<td>{{ $supplier->email ?: '—' }}</td>
<td>{{ $supplier->phone ?: '—' }}</td>
<td>{{ $supplier->tax_number ?: '—' }}</td>
<td>
@if($supplier->is_active)
<span class="badge badge-active">Active</span>
@else
<span class="badge badge-inactive">Inactive</span>
@endif
</td>
</tr>
@empty
<tr>
<td colspan="7" style="text-align:center;padding:20px;color:#94a3b8;">
No suppliers found.
</td>
</tr>
@endforelse
</tbody>
</table>
</div>
<div class="footer">
<span>SteelERP — Confidential</span>
<span>Exported by: {{ Auth::user()->name ?? '—' }}</span>
</div>
</body>
</html>
@@ -0,0 +1,135 @@
@extends('layouts.app')
@section('title', $supplier->name)
@section('content')
<div class="page-header">
<div>
<h1 class="page-title">{{ $supplier->name }}</h1>
<p class="page-subtitle">
<a href="{{ route('purchase.suppliers.index') }}" style="color:#2563eb;text-decoration:none;">Suppliers</a>
&nbsp;/&nbsp; View Details
</p>
</div>
<div class="flex items-center gap-2">
<a href="{{ route('purchase.suppliers.edit', $supplier) }}" class="btn btn-secondary">Edit Supplier</a>
<a href="{{ route('purchase.suppliers.index') }}" class="btn btn-secondary">Back to List</a>
</div>
</div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:20px;max-width:900px;">
{{-- Identity --}}
<div style="background:#fff;border-radius:14px;border:1px solid #e2e8f0;padding:22px;grid-column:1/-1;">
<div style="display:flex;align-items:center;justify-content:space-between;margin-bottom:18px;">
<h3 style="font-size:13px;font-weight:700;text-transform:uppercase;letter-spacing:.06em;color:#94a3b8;">Company</h3>
@if($supplier->is_active)
<span class="badge-green">Active</span>
@else
<span class="badge-gray">Inactive</span>
@endif
</div>
<div style="font-size:22px;font-weight:700;color:#0f172a;margin-bottom:6px;">{{ $supplier->name }}</div>
@if($supplier->supplier_code)
<div style="font-family:monospace;font-size:12px;color:#94a3b8;margin-bottom:4px;">{{ $supplier->supplier_code }}</div>
@endif
@if($supplier->category)
<span style="display:inline-block;padding:3px 12px;border-radius:20px;font-size:12px;font-weight:600;background:#f1f5f9;color:#475569;margin-top:4px;">
{{ $supplier->category }}
</span>
@endif
@if($supplier->website)
<div style="margin-top:10px;">
<a href="{{ $supplier->website }}" target="_blank"
style="font-size:13px;color:#2563eb;text-decoration:none;">
{{ $supplier->website }}
</a>
</div>
@endif
</div>
{{-- Contact --}}
<div style="background:#fff;border-radius:14px;border:1px solid #e2e8f0;padding:22px;">
<h3 style="font-size:13px;font-weight:700;text-transform:uppercase;letter-spacing:.06em;color:#94a3b8;margin-bottom:16px;">Contact</h3>
@if($supplier->contact_person)
<div style="margin-bottom:12px;">
<div style="font-size:11px;color:#94a3b8;margin-bottom:2px;">Contact Person</div>
<div style="font-size:14px;font-weight:600;color:#1e293b;">{{ $supplier->contact_person }}</div>
</div>
@endif
@if($supplier->email)
<div style="margin-bottom:10px;">
<div style="font-size:11px;color:#94a3b8;margin-bottom:2px;">Primary Email</div>
<a href="mailto:{{ $supplier->email }}" style="font-size:13px;color:#2563eb;text-decoration:none;">{{ $supplier->email }}</a>
</div>
@endif
@if($supplier->secondary_email)
<div style="margin-bottom:10px;">
<div style="font-size:11px;color:#94a3b8;margin-bottom:2px;">Secondary Email</div>
<a href="mailto:{{ $supplier->secondary_email }}" style="font-size:13px;color:#64748b;text-decoration:none;">{{ $supplier->secondary_email }}</a>
</div>
@endif
@if($supplier->phone)
<div style="margin-bottom:10px;">
<div style="font-size:11px;color:#94a3b8;margin-bottom:2px;">Phone 1</div>
<a href="tel:{{ $supplier->phone }}" style="font-size:13px;color:#334155;text-decoration:none;">{{ $supplier->phone }}</a>
</div>
@endif
@if($supplier->phone2)
<div style="margin-bottom:10px;">
<div style="font-size:11px;color:#94a3b8;margin-bottom:2px;">Phone 2</div>
<a href="tel:{{ $supplier->phone2 }}" style="font-size:13px;color:#334155;text-decoration:none;">{{ $supplier->phone2 }}</a>
</div>
@endif
@if($supplier->whatsapp)
<div>
<div style="font-size:11px;color:#94a3b8;margin-bottom:2px;">WhatsApp</div>
<a href="https://wa.me/{{ ltrim($supplier->whatsapp, '+') }}" target="_blank"
style="display:inline-flex;align-items:center;gap:5px;font-size:13px;color:#16a34a;text-decoration:none;">
<svg width="14" height="14" viewBox="0 0 24 24" fill="#16a34a">
<path d="M17.472 14.382c-.297-.149-1.758-.867-2.03-.967-.273-.099-.471-.148-.67.15-.197.297-.767.966-.94 1.164-.173.199-.347.223-.644.075-.297-.15-1.255-.463-2.39-1.475-.883-.788-1.48-1.761-1.653-2.059-.173-.297-.018-.458.13-.606.134-.133.298-.347.446-.52.149-.174.198-.298.298-.497.099-.198.05-.371-.025-.52-.075-.149-.669-1.612-.916-2.207-.242-.579-.487-.5-.669-.51-.173-.008-.371-.01-.57-.01-.198 0-.52.074-.792.372-.272.297-1.04 1.016-1.04 2.479 0 1.462 1.065 2.875 1.213 3.074.149.198 2.096 3.2 5.077 4.487.709.306 1.262.489 1.694.625.712.227 1.36.195 1.871.118.571-.085 1.758-.719 2.006-1.413.248-.694.248-1.289.173-1.413-.074-.124-.272-.198-.57-.347m-5.421 7.403h-.004a9.87 9.87 0 01-5.031-1.378l-.361-.214-3.741.982.998-3.648-.235-.374a9.86 9.86 0 01-1.51-5.26c.001-5.45 4.436-9.884 9.888-9.884 2.64 0 5.122 1.03 6.988 2.898a9.825 9.825 0 012.893 6.994c-.003 5.45-4.437 9.884-9.885 9.884m8.413-18.297A11.815 11.815 0 0012.05 0C5.495 0 .16 5.335.157 11.892c0 2.096.547 4.142 1.588 5.945L.057 24l6.305-1.654a11.882 11.882 0 005.683 1.448h.005c6.554 0 11.89-5.335 11.893-11.893a11.821 11.821 0 00-3.48-8.413Z"/>
</svg>
{{ $supplier->whatsapp }}
</a>
</div>
@endif
</div>
{{-- Financial --}}
<div style="background:#fff;border-radius:14px;border:1px solid #e2e8f0;padding:22px;">
<h3 style="font-size:13px;font-weight:700;text-transform:uppercase;letter-spacing:.06em;color:#94a3b8;margin-bottom:16px;">Financial &amp; Address</h3>
@if($supplier->address)
<div style="margin-bottom:12px;">
<div style="font-size:11px;color:#94a3b8;margin-bottom:2px;">Address</div>
<div style="font-size:13px;color:#334155;line-height:1.5;">{{ $supplier->address }}</div>
</div>
@endif
@if($supplier->tax_number)
<div style="margin-bottom:12px;">
<div style="font-size:11px;color:#94a3b8;margin-bottom:2px;">Tax / VAT Number</div>
<div style="font-family:monospace;font-size:13px;color:#334155;">{{ $supplier->tax_number }}</div>
</div>
@endif
@if($supplier->credit_terms)
<div style="margin-bottom:12px;">
<div style="font-size:11px;color:#94a3b8;margin-bottom:2px;">Credit Terms</div>
<div style="font-size:13px;color:#16a34a;font-weight:600;">
{{ strtolower($supplier->credit_terms) === 'y' ? 'Yes' : $supplier->credit_terms }}
@if($supplier->credit_days)
&nbsp;·&nbsp; {{ $supplier->credit_days }} days
@endif
</div>
</div>
@endif
@if($supplier->remarks)
<div>
<div style="font-size:11px;color:#94a3b8;margin-bottom:2px;">Remarks</div>
<div style="font-size:13px;color:#475569;line-height:1.5;">{{ $supplier->remarks }}</div>
</div>
@endif
</div>
</div>
@endsection
+28
View File
@@ -0,0 +1,28 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Link Expired</title>
<style>
*{box-sizing:border-box;margin:0;padding:0;}
body{font-family:system-ui,-apple-system,sans-serif;background:#fef2f2;min-height:100vh;display:flex;align-items:center;justify-content:center;padding:20px;}
.card{background:#fff;border-radius:20px;box-shadow:0 8px 32px rgba(0,0,0,.10);padding:52px 44px;max-width:480px;width:100%;text-align:center;}
</style>
</head>
<body>
<div class="card">
<div style="font-size:60px;margin-bottom:20px;">⏰</div>
<div style="font-size:24px;font-weight:700;color:#dc2626;margin-bottom:10px;">Link Expired</div>
<div style="font-size:14px;color:#475569;line-height:1.65;">
This quote invitation link has expired (valid for 7 days after sending).
Please contact the purchasing team if you still wish to submit a quote.
</div>
@if($invitation->expires_at)
<div style="margin-top:28px;padding:14px;background:#fef2f2;border-radius:10px;font-size:12px;color:#64748b;">
Expired on {{ $invitation->expires_at->format('d M Y') }}
</div>
@endif
</div>
</body>
</html>
+131
View File
@@ -0,0 +1,131 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Quote Request — {{ $purchaseRequest->request_number }}</title>
<style>
*{box-sizing:border-box;margin:0;padding:0;}
body{font-family:system-ui,-apple-system,sans-serif;background:#f1f5f9;min-height:100vh;padding:20px 16px;}
.card{background:#fff;border-radius:16px;box-shadow:0 4px 24px rgba(0,0,0,.08);max-width:740px;margin:0 auto;overflow:hidden;}
.hd{background:linear-gradient(135deg,#2563eb,#1d4ed8);padding:28px 32px;}
.body{padding:32px;}
label.fl{display:block;font-size:11px;font-weight:700;color:#64748b;text-transform:uppercase;letter-spacing:.05em;margin-bottom:5px;}
input[type=text],input[type=number],textarea{width:100%;padding:9px 12px;border:1.5px solid #e2e8f0;border-radius:8px;font-size:13px;outline:none;}
input:focus,textarea:focus{border-color:#2563eb;}
.btn{width:100%;padding:14px;background:linear-gradient(135deg,#2563eb,#1d4ed8);color:#fff;border:none;border-radius:10px;font-size:15px;font-weight:700;cursor:pointer;margin-top:24px;}
table{width:100%;border-collapse:collapse;font-size:13px;}
thead th{background:#f8fafc;padding:10px 12px;text-align:left;font-size:11px;font-weight:700;color:#64748b;text-transform:uppercase;}
tbody td{padding:10px 12px;border-bottom:1px solid #f1f5f9;vertical-align:middle;}
tfoot td{padding:12px;font-weight:700;}
input[type=number].price{width:130px;text-align:right;}
@media(max-width:600px){
.body{padding:20px;}
table,thead,tbody,tfoot,tr,th,td{display:block;}
thead{display:none;}
tbody td{border:none;padding:4px 0;}
tbody tr{border:1px solid #e2e8f0;border-radius:10px;padding:12px;margin-bottom:10px;}
input[type=number].price{width:100%;}
}
</style>
</head>
<body>
<div class="card">
<div class="hd">
<div style="font-size:11px;font-weight:600;color:rgba(255,255,255,.7);text-transform:uppercase;letter-spacing:.06em;">Request for Quotation</div>
<div style="font-size:22px;font-weight:700;color:#fff;margin-top:4px;">{{ $purchaseRequest->request_number }}</div>
@if($purchaseRequest->project_name)
<div style="font-size:13px;color:rgba(255,255,255,.8);margin-top:2px;">{{ $purchaseRequest->project_name }}</div>
@endif
</div>
<div class="body">
<p style="font-size:14px;color:#334155;margin-bottom:6px;">
Hello <strong>{{ $invitation->supplier->name }}</strong>,
</p>
<p style="font-size:13px;color:#64748b;margin-bottom:24px;">
Please enter your unit prices for the items below, then scroll down and submit. This link is private to your company and can only be submitted once.
</p>
<form method="POST" action="{{ route('rfq.submit', $invitation->token) }}">
@csrf
{{-- Items table --}}
<div style="overflow-x:auto;margin-bottom:24px;border:1px solid #e2e8f0;border-radius:10px;">
<table>
<thead>
<tr>
<th>#</th>
<th>Description</th>
<th>Qty</th>
<th>Unit</th>
<th style="text-align:right;">Unit Price (BD)</th>
<th style="text-align:right;">Total (BD)</th>
</tr>
</thead>
<tbody>
@foreach($items as $i => $item)
<tr>
<td style="color:#94a3b8;font-size:12px;">{{ $i + 1 }}</td>
<td style="font-weight:500;">{{ $item->description }}</td>
<td>{{ rtrim(rtrim(number_format((float)$item->quantity, 3), '0'), '.') }}</td>
<td style="color:#64748b;">{{ $item->unit ?: '—' }}</td>
<td style="text-align:right;">
<input type="number" class="price" name="items[{{ $i }}][unit_price]"
min="0" step="0.001" required placeholder="0.000"
oninput="calcRow({{ $i }}, {{ (float)$item->quantity }})">
</td>
<td style="text-align:right;font-weight:600;" id="tot-{{ $i }}">—</td>
</tr>
@endforeach
</tbody>
<tfoot>
<tr style="background:#f8fafc;">
<td colspan="5" style="text-align:right;font-size:13px;color:#475569;">Grand Total:</td>
<td style="text-align:right;font-size:15px;color:#2563eb;" id="grand-total">BD 0.000</td>
</tr>
</tfoot>
</table>
</div>
{{-- Terms --}}
<div style="display:grid;grid-template-columns:1fr 1fr;gap:16px;margin-bottom:16px;">
<div>
<label class="fl">Lead Time (days)</label>
<input type="number" name="lead_time_days" min="0" placeholder="e.g. 14">
</div>
<div>
<label class="fl">Payment Terms</label>
<input type="text" name="payment_terms" placeholder="e.g. 30 days net">
</div>
</div>
<div>
<label class="fl">Notes / Remarks</label>
<textarea name="notes" rows="3" placeholder="Any additional notes, conditions, or remarks…"></textarea>
</div>
<button class="btn" type="submit">Submit My Quote →</button>
</form>
<p style="font-size:11px;color:#cbd5e1;margin-top:16px;text-align:center;">
Link expires {{ $invitation->expires_at->format('d M Y') }} · One submission only
</p>
</div>
</div>
<script>
var _totals = {};
function calcRow(i, qty) {
var inp = document.querySelector('input[name="items[' + i + '][unit_price]"]');
var up = parseFloat(inp ? inp.value : 0) || 0;
var tot = Math.round(up * qty * 1000) / 1000;
_totals[i] = tot;
var el = document.getElementById('tot-' + i);
if (el) el.textContent = 'BD ' + tot.toFixed(3);
var grand = Object.values(_totals).reduce(function(a,b){return a+b;}, 0);
var ge = document.getElementById('grand-total');
if (ge) ge.textContent = 'BD ' + grand.toFixed(3);
}
</script>
</body>
</html>
+27
View File
@@ -0,0 +1,27 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Quote Submitted</title>
<style>
*{box-sizing:border-box;margin:0;padding:0;}
body{font-family:system-ui,-apple-system,sans-serif;background:#f0fdf4;min-height:100vh;display:flex;align-items:center;justify-content:center;padding:20px;}
.card{background:#fff;border-radius:20px;box-shadow:0 8px 32px rgba(0,0,0,.10);padding:52px 44px;max-width:480px;width:100%;text-align:center;}
</style>
</head>
<body>
<div class="card">
<div style="font-size:60px;margin-bottom:20px;">✅</div>
<div style="font-size:24px;font-weight:700;color:#15803d;margin-bottom:10px;">Quote Submitted!</div>
<div style="font-size:14px;color:#475569;line-height:1.65;">
Thank you, <strong>{{ $invitation->supplier->name }}</strong>. Your quote for
<strong>{{ $invitation->purchaseRequest->request_number }}</strong> has been received
and is under review. You will be contacted if you are selected.
</div>
<div style="margin-top:28px;padding:14px;background:#f0fdf4;border-radius:10px;font-size:12px;color:#64748b;">
Submitted on {{ now()->format('d M Y, H:i') }}
</div>
</div>
</body>
</html>
@@ -0,0 +1,76 @@
@extends('layouts.app')
@section('title', 'Add Customer')
@section('content')
<div class="mb-6">
<h1 class="page-title">Add Customer</h1>
<p class="page-subtitle"><a href="{{ route('sales.customers.index') }}" class="text-blue-600 hover:underline">Customers</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
<div class="card card-body max-w-2xl">
<form action="{{ route('sales.customers.store') }}" method="POST">
@csrf
<div class="grid grid-cols-1 gap-5 sm:grid-cols-2">
<div class="sm:col-span-2">
<label class="form-label">Customer Name <span class="text-red-500">*</span></label>
<input type="text" name="name" value="{{ old('name') }}" required class="form-input">
</div>
<div>
<label class="form-label">Contact Person</label>
<input type="text" name="contact_person" value="{{ old('contact_person') }}" class="form-input">
</div>
<div>
<label class="form-label">Email</label>
<input type="email" name="email" value="{{ old('email') }}" class="form-input">
</div>
<div>
<label class="form-label">Phone</label>
<input type="text" name="phone" value="{{ old('phone') }}" class="form-input">
</div>
<div>
<label class="form-label">Tax Number</label>
<input type="text" name="tax_number" value="{{ old('tax_number') }}" class="form-input">
</div>
<div>
<label class="form-label">Credit Limit</label>
<input type="number" name="credit_limit" value="{{ old('credit_limit', 0) }}" min="0" step="0.01" class="form-input">
</div>
<div class="sm:col-span-2">
<label class="form-label">Address</label>
<textarea name="address" rows="3" class="form-textarea">{{ old('address') }}</textarea>
</div>
<div class="sm:col-span-2 flex items-center gap-2">
<input type="checkbox" name="is_active" id="is_active" value="1"
{{ old('is_active', '1') ? 'checked' : '' }}
class="h-4 w-4 text-blue-600 border-gray-300 rounded">
<label for="is_active" class="form-label mb-0">Active</label>
</div>
</div>
<div class="mt-6 flex items-center gap-3">
<button type="submit" class="btn-primary">Save Customer</button>
<a href="{{ route('sales.customers.index') }}" class="btn-secondary">Cancel</a>
</div>
</form>
</div>
@endsection
@@ -0,0 +1,77 @@
@extends('layouts.app')
@section('title', 'Edit Customer')
@section('content')
<div class="mb-6">
<h1 class="page-title">Edit Customer</h1>
<p class="page-subtitle"><a href="{{ route('sales.customers.index') }}" class="text-blue-600 hover:underline">Customers</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.customers.update', $customer) }}" method="POST">
@csrf
@method('PUT')
<div class="grid grid-cols-1 gap-5 sm:grid-cols-2">
<div class="sm:col-span-2">
<label class="form-label">Customer Name <span class="text-red-500">*</span></label>
<input type="text" name="name" value="{{ old('name', $customer->name) }}" required class="form-input">
</div>
<div>
<label class="form-label">Contact Person</label>
<input type="text" name="contact_person" value="{{ old('contact_person', $customer->contact_person) }}" class="form-input">
</div>
<div>
<label class="form-label">Email</label>
<input type="email" name="email" value="{{ old('email', $customer->email) }}" class="form-input">
</div>
<div>
<label class="form-label">Phone</label>
<input type="text" name="phone" value="{{ old('phone', $customer->phone) }}" class="form-input">
</div>
<div>
<label class="form-label">Tax Number</label>
<input type="text" name="tax_number" value="{{ old('tax_number', $customer->tax_number) }}" class="form-input">
</div>
<div>
<label class="form-label">Credit Limit</label>
<input type="number" name="credit_limit" value="{{ old('credit_limit', $customer->credit_limit) }}" min="0" step="0.01" class="form-input">
</div>
<div class="sm:col-span-2">
<label class="form-label">Address</label>
<textarea name="address" rows="3" class="form-textarea">{{ old('address', $customer->address) }}</textarea>
</div>
<div class="sm:col-span-2 flex items-center gap-2">
<input type="checkbox" name="is_active" id="is_active" value="1"
{{ old('is_active', $customer->is_active) ? 'checked' : '' }}
class="h-4 w-4 text-blue-600 border-gray-300 rounded">
<label for="is_active" class="form-label mb-0">Active</label>
</div>
</div>
<div class="mt-6 flex items-center gap-3">
<button type="submit" class="btn-primary">Update Customer</button>
<a href="{{ route('sales.customers.index') }}" class="btn-secondary">Cancel</a>
</div>
</form>
</div>
@endsection
@@ -0,0 +1,70 @@
@extends('layouts.app')
@section('title', 'Customers')
@section('content')
<div class="page-header">
<div>
<h1 class="page-title">Customers</h1>
<p class="page-subtitle">Manage your customer directory</p>
</div>
<a href="{{ route('sales.customers.create') }}" class="btn-primary">
+ Add Customer
</a>
</div>
<div class="table-wrapper overflow-x-auto">
<table class="table-base">
<thead>
<tr>
<th>Name</th>
<th>Contact</th>
<th>Email</th>
<th class="text-right">Credit Limit</th>
<th class="text-right">Outstanding Balance</th>
<th>Status</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
@forelse($customers as $customer)
<tr>
<td class="font-medium text-gray-800">{{ $customer->name }}</td>
<td>{{ $customer->contact_person }}</td>
<td>{{ $customer->email }}</td>
<td class="text-right text-gray-700">{{ number_format($customer->credit_limit, 2) }}</td>
<td class="text-right {{ ($customer->outstanding_balance ?? 0) > 0 ? 'text-red-600 font-semibold' : 'text-gray-500' }}">
{{ number_format($customer->outstanding_balance ?? 0, 2) }}
</td>
<td>
@if($customer->is_active)
<span class="badge-green">Active</span>
@else
<span class="badge-gray">Inactive</span>
@endif
</td>
<td>
<div class="flex items-center gap-2">
<a href="{{ route('sales.customers.edit', $customer) }}" class="btn-secondary btn-sm">Edit</a>
<form action="{{ route('sales.customers.destroy', $customer) }}" method="POST"
onsubmit="confirmDelete(this,'Delete this customer?','This customer record 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="7" class="px-4 py-8 text-center text-gray-400">No customers found.</td>
</tr>
@endforelse
</tbody>
</table>
</div>
@if($customers->hasPages())
<div class="mt-4">{{ $customers->links() }}</div>
@endif
@endsection
@@ -0,0 +1,129 @@
@extends('layouts.app')
@section('title', 'New Delivery Note')
@section('content')
<div class="mb-6">
<h1 class="page-title">New Delivery Note</h1>
<p class="page-subtitle"><a href="{{ route('sales.delivery-notes.index') }}" class="text-blue-600 hover:underline">Delivery Notes</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.delivery-notes.store') }}" method="POST">
@csrf
<div class="card card-body mb-4">
<h2 class="text-base font-semibold text-gray-700 mb-4">Delivery Details</h2>
<div class="grid grid-cols-1 gap-5 sm:grid-cols-2">
<div>
<label class="form-label">Sales Order <span class="text-red-500">*</span></label>
<select name="sales_order_id" id="so-select" required onchange="loadSOItems(this)" class="form-select">
<option value="">-- Select Confirmed Order --</option>
@foreach($salesOrders as $so)
<option value="{{ $so->id }}"
data-items="{{ json_encode($so->items) }}"
{{ (old('sales_order_id', request('sales_order_id'))) == $so->id ? 'selected' : '' }}>
{{ $so->order_number ?? 'SO-' . str_pad($so->id, 5, '0', STR_PAD_LEFT) }} - {{ $so->customer->name ?? '' }}
</option>
@endforeach
</select>
</div>
<div>
<label class="form-label">Warehouse <span class="text-red-500">*</span></label>
<select name="warehouse_id" required class="form-select">
<option value="">-- Select Warehouse --</option>
@foreach($warehouses as $warehouse)
<option value="{{ $warehouse->id }}" {{ old('warehouse_id') == $warehouse->id ? 'selected' : '' }}>
{{ $warehouse->name }}
</option>
@endforeach
</select>
</div>
<div>
<label class="form-label">Delivery Date <span class="text-red-500">*</span></label>
<input type="date" name="delivery_date" value="{{ old('delivery_date', date('Y-m-d')) }}" required class="form-input">
</div>
<div>
<label class="form-label">Notes</label>
<textarea name="notes" rows="2" class="form-textarea">{{ old('notes') }}</textarea>
</div>
</div>
</div>
<!-- Items from SO -->
<div class="card card-body mb-4">
<h2 class="text-base font-semibold text-gray-700 mb-4">Items to Deliver</h2>
<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">SO Qty</th>
<th class="pb-2 text-left font-semibold text-gray-600 w-28">Deliver Qty</th>
</tr>
</thead>
<tbody id="dn-items-body">
<tr>
<td colspan="3" class="py-4 text-center text-gray-400">Select a Sales Order to load items.</td>
</tr>
</tbody>
</table>
</div>
</div>
<div class="flex items-center gap-3">
<button type="submit" class="btn-primary">Create Delivery Note</button>
<a href="{{ route('sales.delivery-notes.index') }}" class="btn-secondary">Cancel</a>
</div>
</form>
<script>
function loadSOItems(select) {
var tbody = document.getElementById('dn-items-body');
var option = select.options[select.selectedIndex];
tbody.innerHTML = '';
if (!option.value) {
tbody.innerHTML = '<tr><td colspan="3" class="py-4 text-center text-gray-400">Select a Sales Order to load items.</td></tr>';
return;
}
var items = JSON.parse(option.dataset.items || '[]');
if (!items.length) {
tbody.innerHTML = '<tr><td colspan="3" class="py-4 text-center text-gray-400">No items on this order.</td></tr>';
return;
}
items.forEach(function(item, idx) {
var row = document.createElement('tr');
row.className = 'border-b border-gray-100';
row.innerHTML = `
<td class="py-2 pr-2 text-gray-800">${item.item ? item.item.item_name : 'Item #' + item.item_id}
<input type="hidden" name="items[${idx}][item_id]" value="${item.item_id}">
</td>
<td class="py-2 pr-2 text-gray-500">${item.quantity}</td>
<td class="py-2 pr-2">
<input type="number" name="items[${idx}][quantity]"
value="${item.quantity}" min="0" step="0.01"
max="${item.quantity}"
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">
</td>`;
tbody.appendChild(row);
});
}
</script>
@endsection
@@ -0,0 +1,57 @@
@extends('layouts.app')
@section('title', 'Edit Delivery Note')
@section('content')
<div class="mb-6">
<h1 class="page-title">Edit Delivery Note</h1>
<p class="page-subtitle"><a href="{{ route('sales.delivery-notes.index') }}" class="text-blue-600 hover:underline">Delivery Notes</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.delivery-notes.update', $deliveryNote) }}" method="POST">
@csrf
@method('PUT')
<div class="grid grid-cols-1 gap-5 sm:grid-cols-2">
<div>
<label class="form-label">Warehouse <span class="text-red-500">*</span></label>
<select name="warehouse_id" required class="form-select">
<option value="">-- Select Warehouse --</option>
@foreach($warehouses as $warehouse)
<option value="{{ $warehouse->id }}" {{ old('warehouse_id', $deliveryNote->warehouse_id) == $warehouse->id ? 'selected' : '' }}>
{{ $warehouse->name }}
</option>
@endforeach
</select>
</div>
<div>
<label class="form-label">Delivery Date <span class="text-red-500">*</span></label>
<input type="date" name="delivery_date" value="{{ old('delivery_date', $deliveryNote->delivery_date ? \Carbon\Carbon::parse($deliveryNote->delivery_date)->format('Y-m-d') : '') }}" required 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', $deliveryNote->notes) }}</textarea>
</div>
</div>
<div class="mt-6 flex items-center gap-3">
<button type="submit" class="btn-primary">Update Delivery Note</button>
<a href="{{ route('sales.delivery-notes.index') }}" class="btn-secondary">Cancel</a>
</div>
</form>
</div>
@endsection
@@ -0,0 +1,77 @@
@extends('layouts.app')
@section('title', 'Delivery Notes')
@section('content')
<div class="page-header">
<div>
<h1 class="page-title">Delivery Notes</h1>
<p class="page-subtitle">Manage goods dispatch to customers</p>
</div>
<a href="{{ route('sales.delivery-notes.create') }}" class="btn-primary">
+ New Delivery Note
</a>
</div>
<div class="table-wrapper overflow-x-auto">
<table class="table-base">
<thead>
<tr>
<th>DN #</th>
<th>Sales Order</th>
<th>Customer</th>
<th>Warehouse</th>
<th>Date</th>
<th>Status</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
@forelse($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="font-mono text-gray-600">{{ $dn->salesOrder->order_number ?? 'SO-' . str_pad($dn->sales_order_id, 5, '0', STR_PAD_LEFT) }}</td>
<td class="text-gray-800">{{ $dn->salesOrder->customer->name ?? '' }}</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>
@php
$badgeClass = match($dn->status ?? 'pending') {
'pending' => 'badge-yellow',
'dispatched' => 'badge-green',
'cancelled' => 'badge-red',
default => 'badge-gray',
};
@endphp
<span class="{{ $badgeClass }}">{{ ucfirst($dn->status ?? 'pending') }}</span>
</td>
<td>
<div class="flex items-center gap-2 flex-wrap">
@if($dn->status === 'pending')
<form action="{{ route('sales.delivery-notes.dispatch', $dn) }}" method="POST">
@csrf @method('PATCH')
<button type="submit" class="btn-success btn-sm">Dispatch</button>
</form>
@endif
<a href="{{ route('sales.delivery-notes.edit', $dn) }}" class="btn-secondary btn-sm">Edit</a>
<form action="{{ route('sales.delivery-notes.destroy', $dn) }}" method="POST"
onsubmit="confirmDelete(this,'Delete this delivery note?','This delivery note 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="7" class="px-4 py-8 text-center text-gray-400">No delivery notes found.</td>
</tr>
@endforelse
</tbody>
</table>
</div>
@if($deliveryNotes->hasPages())
<div class="mt-4">{{ $deliveryNotes->links() }}</div>
@endif
@endsection
@@ -0,0 +1,128 @@
@extends('layouts.app')
@section('title', 'New Sales Invoice')
@section('content')
<div class="mb-6">
<h1 class="page-title">New Sales Invoice</h1>
<p class="page-subtitle"><a href="{{ route('sales.invoices.index') }}" class="text-blue-600 hover:underline">Sales Invoices</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
<div class="card card-body max-w-2xl">
<form action="{{ route('sales.invoices.store') }}" method="POST">
@csrf
<div class="grid grid-cols-1 gap-5 sm:grid-cols-2">
<div>
<label class="form-label">Sales Order <span class="text-red-500">*</span></label>
<select name="sales_order_id" id="inv-so-select" required onchange="fillCustomer(this)" class="form-select">
<option value="">-- Select Dispatched Order --</option>
@foreach($salesOrders as $so)
<option value="{{ $so->id }}"
data-customer="{{ $so->customer_id }}"
data-total="{{ $so->total_amount }}"
{{ old('sales_order_id') == $so->id ? 'selected' : '' }}>
{{ $so->order_number ?? 'SO-' . str_pad($so->id, 5, '0', STR_PAD_LEFT) }} - {{ $so->customer->name ?? '' }}
</option>
@endforeach
</select>
</div>
<div>
<label class="form-label">Customer</label>
<select name="customer_id" id="inv-customer" class="form-select">
<option value="">-- Auto-filled from SO --</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">Invoice Date <span class="text-red-500">*</span></label>
<input type="date" name="invoice_date" value="{{ old('invoice_date', date('Y-m-d')) }}" required class="form-input">
</div>
<div>
<label class="form-label">Due Date</label>
<input type="date" name="due_date" value="{{ old('due_date') }}" class="form-input">
</div>
<div>
<label class="form-label">Subtotal <span class="text-red-500">*</span></label>
<input type="number" name="subtotal" id="inv-subtotal" value="{{ old('subtotal') }}"
min="0" step="0.01" required onchange="calcSalesInvoiceTotal()" class="form-input">
</div>
<div>
<label class="form-label">VAT Rate (%)</label>
<input type="number" name="vat_rate" id="inv-vat-rate" value="{{ old('vat_rate', 0) }}"
min="0" max="100" step="0.01" onchange="calcSalesInvoiceTotal()" class="form-input">
</div>
<div>
<label class="form-label">VAT Amount</label>
<input type="number" name="vat_amount" id="inv-vat-amount" value="{{ old('vat_amount', 0) }}"
min="0" step="0.01" readonly
class="w-full border border-gray-200 bg-gray-50 rounded-lg px-3 py-2 text-sm">
</div>
<div>
<label class="form-label">Total Amount</label>
<input type="number" name="total_amount" id="inv-total" value="{{ old('total_amount') }}"
readonly
class="w-full border border-gray-200 bg-gray-50 rounded-lg px-3 py-2 text-sm font-semibold text-gray-800">
</div>
</div>
<div class="mt-6 flex items-center gap-3">
<button type="submit" class="btn-primary">Save Invoice</button>
<a href="{{ route('sales.invoices.index') }}" class="btn-secondary">Cancel</a>
</div>
</form>
</div>
<script>
function fillCustomer(select) {
var option = select.options[select.selectedIndex];
var customerId = option.dataset.customer;
var total = option.dataset.total;
var customerSelect = document.getElementById('inv-customer');
// Auto-select customer
for (var i = 0; i < customerSelect.options.length; i++) {
if (customerSelect.options[i].value == customerId) {
customerSelect.selectedIndex = i;
break;
}
}
// Auto-fill subtotal from SO
if (total) {
document.getElementById('inv-subtotal').value = parseFloat(total).toFixed(2);
calcSalesInvoiceTotal();
}
}
function calcSalesInvoiceTotal() {
var subtotal = parseFloat(document.getElementById('inv-subtotal').value) || 0;
var vatRate = parseFloat(document.getElementById('inv-vat-rate').value) || 0;
var vatAmount = subtotal * vatRate / 100;
document.getElementById('inv-vat-amount').value = vatAmount.toFixed(2);
document.getElementById('inv-total').value = (subtotal + vatAmount).toFixed(2);
}
</script>
@endsection
@@ -0,0 +1,81 @@
@extends('layouts.app')
@section('title', 'Edit Sales Invoice')
@section('content')
<div class="mb-6">
<h1 class="page-title">Edit Sales Invoice</h1>
<p class="page-subtitle"><a href="{{ route('sales.invoices.index') }}" class="text-blue-600 hover:underline">Sales Invoices</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.invoices.update', $invoice) }}" method="POST">
@csrf
@method('PUT')
<div class="grid grid-cols-1 gap-5 sm:grid-cols-2">
<div>
<label class="form-label">Invoice Date <span class="text-red-500">*</span></label>
<input type="date" name="invoice_date" value="{{ old('invoice_date', $invoice->invoice_date ? \Carbon\Carbon::parse($invoice->invoice_date)->format('Y-m-d') : '') }}" required class="form-input">
</div>
<div>
<label class="form-label">Due Date</label>
<input type="date" name="due_date" value="{{ old('due_date', $invoice->due_date ? \Carbon\Carbon::parse($invoice->due_date)->format('Y-m-d') : '') }}" class="form-input">
</div>
<div>
<label class="form-label">Subtotal</label>
<input type="number" name="subtotal" id="edit-subtotal" value="{{ old('subtotal', $invoice->subtotal) }}"
min="0" step="0.01" onchange="calcEditTotal()" class="form-input">
</div>
<div>
<label class="form-label">VAT Amount</label>
<input type="number" name="vat_amount" id="edit-vat" value="{{ old('vat_amount', $invoice->vat_amount) }}"
min="0" step="0.01" onchange="calcEditTotal()" class="form-input">
</div>
<div class="sm:col-span-2">
<label class="form-label">Total Amount</label>
<input type="number" name="total_amount" id="edit-total" value="{{ old('total_amount', $invoice->total_amount) }}"
readonly
class="w-full border border-gray-200 bg-gray-50 rounded-lg px-3 py-2 text-sm font-semibold text-gray-800">
</div>
<div>
<label class="form-label">Status</label>
<select name="status" class="form-select">
<option value="unpaid" {{ old('status', $invoice->status) === 'unpaid' ? 'selected' : '' }}>Unpaid</option>
<option value="partial" {{ old('status', $invoice->status) === 'partial' ? 'selected' : '' }}>Partial</option>
<option value="paid" {{ old('status', $invoice->status) === 'paid' ? 'selected' : '' }}>Paid</option>
</select>
</div>
</div>
<div class="mt-6 flex items-center gap-3">
<button type="submit" class="btn-primary">Update Invoice</button>
<a href="{{ route('sales.invoices.index') }}" class="btn-secondary">Cancel</a>
</div>
</form>
</div>
<script>
function calcEditTotal() {
var sub = parseFloat(document.getElementById('edit-subtotal').value) || 0;
var vat = parseFloat(document.getElementById('edit-vat').value) || 0;
document.getElementById('edit-total').value = (sub + vat).toFixed(2);
}
</script>
@endsection
@@ -0,0 +1,77 @@
@extends('layouts.app')
@section('title', 'Sales Invoices')
@section('content')
<div class="page-header">
<div>
<h1 class="page-title">Sales Invoices</h1>
<p class="page-subtitle">Track customer invoices and payments</p>
</div>
<a href="{{ route('sales.invoices.create') }}" class="btn-primary">
+ New Invoice
</a>
</div>
<div class="table-wrapper overflow-x-auto">
<table class="table-base">
<thead>
<tr>
<th>Invoice #</th>
<th>Customer</th>
<th>SO #</th>
<th>Date</th>
<th class="text-right">Total</th>
<th class="text-right">Paid</th>
<th class="text-right">Outstanding</th>
<th>Status</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
@forelse($invoices as $invoice)
@php $outstanding = $invoice->total_amount - $invoice->paid_amount; @endphp
<tr>
<td class="font-mono text-gray-700">{{ $invoice->invoice_number }}</td>
<td class="text-gray-800">{{ $invoice->customer->name ?? '' }}</td>
<td class="font-mono text-gray-600">{{ $invoice->salesOrder->order_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 text-gray-800">{{ number_format($invoice->total_amount, 2) }}</td>
<td class="text-right text-green-700">{{ number_format($invoice->paid_amount, 2) }}</td>
<td class="text-right {{ $outstanding > 0 ? 'text-red-600 font-semibold' : 'text-gray-500' }}">{{ number_format($outstanding, 2) }}</td>
<td>
@php
$badgeClass = match($invoice->status ?? 'unpaid') {
'unpaid' => 'badge-red',
'partial' => 'badge-yellow',
'paid' => 'badge-green',
default => 'badge-gray',
};
@endphp
<span class="{{ $badgeClass }}">{{ ucfirst($invoice->status ?? 'unpaid') }}</span>
</td>
<td>
<div class="flex items-center gap-2 flex-wrap">
<a href="{{ route('sales.payments.create', ['invoice_id' => $invoice->id]) }}" class="btn-success btn-sm">Receive</a>
<a href="{{ route('sales.invoices.edit', $invoice) }}" class="btn-secondary btn-sm">Edit</a>
<form action="{{ route('sales.invoices.destroy', $invoice) }}" method="POST"
onsubmit="confirmDelete(this,'Delete this invoice?','This invoice 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="9" class="px-4 py-8 text-center text-gray-400">No invoices found.</td>
</tr>
@endforelse
</tbody>
</table>
</div>
@if($invoices->hasPages())
<div class="mt-4">{{ $invoices->links() }}</div>
@endif
@endsection
@@ -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
@@ -0,0 +1,78 @@
@extends('layouts.app')
@section('title', 'Record Customer Receipt')
@section('content')
<div class="mb-6">
<h1 class="page-title">Record Customer Receipt</h1>
<p class="page-subtitle"><a href="{{ route('sales.payments.index') }}" class="text-blue-600 hover:underline">Receipts</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
<div class="card card-body max-w-2xl">
<form action="{{ route('sales.payments.store') }}" method="POST">
@csrf
<div class="grid grid-cols-1 gap-5 sm:grid-cols-2">
<div class="sm:col-span-2">
<label class="form-label">Invoice <span class="text-red-500">*</span></label>
<select name="sales_invoice_id" required class="form-select">
<option value="">-- Select Unpaid Invoice --</option>
@foreach($unpaidInvoices as $invoice)
<option value="{{ $invoice->id }}" {{ (old('sales_invoice_id', request('invoice_id'))) == $invoice->id ? 'selected' : '' }}>
{{ $invoice->invoice_number }} - {{ $invoice->customer->name ?? '' }}
(Outstanding: {{ number_format($invoice->total_amount - $invoice->paid_amount, 2) }})
</option>
@endforeach
</select>
</div>
<div>
<label class="form-label">Receipt Date <span class="text-red-500">*</span></label>
<input type="date" name="receipt_date" value="{{ old('receipt_date', date('Y-m-d')) }}" required class="form-input">
</div>
<div>
<label class="form-label">Amount <span class="text-red-500">*</span></label>
<input type="number" name="amount" value="{{ old('amount') }}" min="0.01" step="0.01" required class="form-input">
</div>
<div>
<label class="form-label">Payment Method <span class="text-red-500">*</span></label>
<select name="payment_method" required class="form-select">
<option value="">-- Select Method --</option>
<option value="cash" {{ old('payment_method') === 'cash' ? 'selected' : '' }}>Cash</option>
<option value="bank_transfer" {{ old('payment_method') === 'bank_transfer' ? 'selected' : '' }}>Bank Transfer</option>
<option value="cheque" {{ old('payment_method') === 'cheque' ? 'selected' : '' }}>Cheque</option>
<option value="other" {{ old('payment_method') === 'other' ? 'selected' : '' }}>Other</option>
</select>
</div>
<div>
<label class="form-label">Reference Number</label>
<input type="text" name="reference_number" value="{{ old('reference_number') }}" class="form-input">
</div>
<div class="sm:col-span-2">
<label class="form-label">Notes</label>
<textarea name="notes" rows="2" class="form-textarea">{{ old('notes') }}</textarea>
</div>
</div>
<div class="mt-6 flex items-center gap-3">
<button type="submit" class="btn-primary">Record Receipt</button>
<a href="{{ route('sales.payments.index') }}" class="btn-secondary">Cancel</a>
</div>
</form>
</div>
@endsection
@@ -0,0 +1,50 @@
@extends('layouts.app')
@section('title', 'Customer Receipts')
@section('content')
<div class="page-header">
<div>
<h1 class="page-title">Customer Receipts</h1>
<p class="page-subtitle">Payment receipts from customers</p>
</div>
<a href="{{ route('sales.payments.create') }}" class="btn-primary">
+ Record Receipt
</a>
</div>
<div class="table-wrapper overflow-x-auto">
<table class="table-base">
<thead>
<tr>
<th>Customer</th>
<th>Invoice #</th>
<th>Date</th>
<th class="text-right">Amount</th>
<th>Method</th>
<th>Reference</th>
</tr>
</thead>
<tbody>
@forelse($payments as $payment)
<tr>
<td class="text-gray-800">{{ $payment->salesInvoice->customer->name ?? '' }}</td>
<td class="font-mono text-gray-700">{{ $payment->salesInvoice->invoice_number ?? '-' }}</td>
<td class="text-gray-600">{{ $payment->receipt_date ? \Carbon\Carbon::parse($payment->receipt_date)->format('d M Y') : '' }}</td>
<td class="text-right font-medium text-gray-800">{{ number_format($payment->amount, 2) }}</td>
<td class="text-gray-600 capitalize">{{ str_replace('_', ' ', $payment->payment_method) }}</td>
<td class="text-gray-600">{{ $payment->reference_number ?? '-' }}</td>
</tr>
@empty
<tr>
<td colspan="6" class="px-4 py-8 text-center text-gray-400">No receipts recorded.</td>
</tr>
@endforelse
</tbody>
</table>
</div>
@if($payments->hasPages())
<div class="mt-4">{{ $payments->links() }}</div>
@endif
@endsection
+277
View File
@@ -0,0 +1,277 @@
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{{ config('app.name', 'Laravel') }}</title>
<!-- Fonts -->
<link rel="preconnect" href="https://fonts.bunny.net">
<link href="https://fonts.bunny.net/css?family=instrument-sans:400,500,600" rel="stylesheet" />
<!-- Styles / Scripts -->
@if (file_exists(public_path('build/manifest.json')) || file_exists(public_path('hot')))
@vite(['resources/css/app.css', 'resources/js/app.js'])
@else
<style>
/*! tailwindcss v4.0.7 | MIT License | https://tailwindcss.com */@layer theme{:root,:host{--font-sans:'Instrument Sans',ui-sans-serif,system-ui,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";--font-serif:ui-serif,Georgia,Cambria,"Times New Roman",Times,serif;--font-mono:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;--color-red-50:oklch(.971 .013 17.38);--color-red-100:oklch(.936 .032 17.717);--color-red-200:oklch(.885 .062 18.334);--color-red-300:oklch(.808 .114 19.571);--color-red-400:oklch(.704 .191 22.216);--color-red-500:oklch(.637 .237 25.331);--color-red-600:oklch(.577 .245 27.325);--color-red-700:oklch(.505 .213 27.518);--color-red-800:oklch(.444 .177 26.899);--color-red-900:oklch(.396 .141 25.723);--color-red-950:oklch(.258 .092 26.042);--color-orange-50:oklch(.98 .016 73.684);--color-orange-100:oklch(.954 .038 75.164);--color-orange-200:oklch(.901 .076 70.697);--color-orange-300:oklch(.837 .128 66.29);--color-orange-400:oklch(.75 .183 55.934);--color-orange-500:oklch(.705 .213 47.604);--color-orange-600:oklch(.646 .222 41.116);--color-orange-700:oklch(.553 .195 38.402);--color-orange-800:oklch(.47 .157 37.304);--color-orange-900:oklch(.408 .123 38.172);--color-orange-950:oklch(.266 .079 36.259);--color-amber-50:oklch(.987 .022 95.277);--color-amber-100:oklch(.962 .059 95.617);--color-amber-200:oklch(.924 .12 95.746);--color-amber-300:oklch(.879 .169 91.605);--color-amber-400:oklch(.828 .189 84.429);--color-amber-500:oklch(.769 .188 70.08);--color-amber-600:oklch(.666 .179 58.318);--color-amber-700:oklch(.555 .163 48.998);--color-amber-800:oklch(.473 .137 46.201);--color-amber-900:oklch(.414 .112 45.904);--color-amber-950:oklch(.279 .077 45.635);--color-yellow-50:oklch(.987 .026 102.212);--color-yellow-100:oklch(.973 .071 103.193);--color-yellow-200:oklch(.945 .129 101.54);--color-yellow-300:oklch(.905 .182 98.111);--color-yellow-400:oklch(.852 .199 91.936);--color-yellow-500:oklch(.795 .184 86.047);--color-yellow-600:oklch(.681 .162 75.834);--color-yellow-700:oklch(.554 .135 66.442);--color-yellow-800:oklch(.476 .114 61.907);--color-yellow-900:oklch(.421 .095 57.708);--color-yellow-950:oklch(.286 .066 53.813);--color-lime-50:oklch(.986 .031 120.757);--color-lime-100:oklch(.967 .067 122.328);--color-lime-200:oklch(.938 .127 124.321);--color-lime-300:oklch(.897 .196 126.665);--color-lime-400:oklch(.841 .238 128.85);--color-lime-500:oklch(.768 .233 130.85);--color-lime-600:oklch(.648 .2 131.684);--color-lime-700:oklch(.532 .157 131.589);--color-lime-800:oklch(.453 .124 130.933);--color-lime-900:oklch(.405 .101 131.063);--color-lime-950:oklch(.274 .072 132.109);--color-green-50:oklch(.982 .018 155.826);--color-green-100:oklch(.962 .044 156.743);--color-green-200:oklch(.925 .084 155.995);--color-green-300:oklch(.871 .15 154.449);--color-green-400:oklch(.792 .209 151.711);--color-green-500:oklch(.723 .219 149.579);--color-green-600:oklch(.627 .194 149.214);--color-green-700:oklch(.527 .154 150.069);--color-green-800:oklch(.448 .119 151.328);--color-green-900:oklch(.393 .095 152.535);--color-green-950:oklch(.266 .065 152.934);--color-emerald-50:oklch(.979 .021 166.113);--color-emerald-100:oklch(.95 .052 163.051);--color-emerald-200:oklch(.905 .093 164.15);--color-emerald-300:oklch(.845 .143 164.978);--color-emerald-400:oklch(.765 .177 163.223);--color-emerald-500:oklch(.696 .17 162.48);--color-emerald-600:oklch(.596 .145 163.225);--color-emerald-700:oklch(.508 .118 165.612);--color-emerald-800:oklch(.432 .095 166.913);--color-emerald-900:oklch(.378 .077 168.94);--color-emerald-950:oklch(.262 .051 172.552);--color-teal-50:oklch(.984 .014 180.72);--color-teal-100:oklch(.953 .051 180.801);--color-teal-200:oklch(.91 .096 180.426);--color-teal-300:oklch(.855 .138 181.071);--color-teal-400:oklch(.777 .152 181.912);--color-teal-500:oklch(.704 .14 182.503);--color-teal-600:oklch(.6 .118 184.704);--color-teal-700:oklch(.511 .096 186.391);--color-teal-800:oklch(.437 .078 188.216);--color-teal-900:oklch(.386 .063 188.416);--color-teal-950:oklch(.277 .046 192.524);--color-cyan-50:oklch(.984 .019 200.873);--color-cyan-100:oklch(.956 .045 203.388);--color-cyan-200:oklch(.917 .08 205.041);--color-cyan-300:oklch(.865 .127 207.078);--color-cyan-400:oklch(.789 .154 211.53);--color-cyan-500:oklch(.715 .143 215.221);--color-cyan-600:oklch(.609 .126 221.723);--color-cyan-700:oklch(.52 .105 223.128);--color-cyan-800:oklch(.45 .085 224.283);--color-cyan-900:oklch(.398 .07 227.392);--color-cyan-950:oklch(.302 .056 229.695);--color-sky-50:oklch(.977 .013 236.62);--color-sky-100:oklch(.951 .026 236.824);--color-sky-200:oklch(.901 .058 230.902);--color-sky-300:oklch(.828 .111 230.318);--color-sky-400:oklch(.746 .16 232.661);--color-sky-500:oklch(.685 .169 237.323);--color-sky-600:oklch(.588 .158 241.966);--color-sky-700:oklch(.5 .134 242.749);--color-sky-800:oklch(.443 .11 240.79);--color-sky-900:oklch(.391 .09 240.876);--color-sky-Line truncated
</style>
@endif
</head>
<body class="bg-[#FDFDFC] dark:bg-[#0a0a0a] text-[#1b1b18] flex p-6 lg:p-8 items-center lg:justify-center min-h-screen flex-col">
<header class="w-full lg:max-w-4xl max-w-[335px] text-sm mb-6 not-has-[nav]:hidden">
@if (Route::has('login'))
<nav class="flex items-center justify-end gap-4">
@auth
<a
href="{{ url('/dashboard') }}"
class="inline-block px-5 py-1.5 dark:text-[#EDEDEC] border-[#19140035] hover:border-[#1915014a] border text-[#1b1b18] dark:border-[#3E3E3A] dark:hover:border-[#62605b] rounded-sm text-sm leading-normal"
>
Dashboard
</a>
@else
<a
href="{{ route('login') }}"
class="inline-block px-5 py-1.5 dark:text-[#EDEDEC] text-[#1b1b18] border border-transparent hover:border-[#19140035] dark:hover:border-[#3E3E3A] rounded-sm text-sm leading-normal"
>
Log in
</a>
@if (Route::has('register'))
<a
href="{{ route('register') }}"
class="inline-block px-5 py-1.5 dark:text-[#EDEDEC] border-[#19140035] hover:border-[#1915014a] border text-[#1b1b18] dark:border-[#3E3E3A] dark:hover:border-[#62605b] rounded-sm text-sm leading-normal">
Register
</a>
@endif
@endauth
</nav>
@endif
</header>
<div class="flex items-center justify-center w-full transition-opacity opacity-100 duration-750 lg:grow starting:opacity-0">
<main class="flex max-w-[335px] w-full flex-col-reverse lg:max-w-4xl lg:flex-row">
<div class="text-[13px] leading-[20px] flex-1 p-6 pb-12 lg:p-20 bg-white dark:bg-[#161615] dark:text-[#EDEDEC] shadow-[inset_0px_0px_0px_1px_rgba(26,26,0,0.16)] dark:shadow-[inset_0px_0px_0px_1px_#fffaed2d] rounded-bl-lg rounded-br-lg lg:rounded-tl-lg lg:rounded-br-none">
<h1 class="mb-1 font-medium">Let's get started</h1>
<p class="mb-2 text-[#706f6c] dark:text-[#A1A09A]">Laravel has an incredibly rich ecosystem. <br>We suggest starting with the following.</p>
<ul class="flex flex-col mb-4 lg:mb-6">
<li class="flex items-center gap-4 py-2 relative before:border-l before:border-[#e3e3e0] dark:before:border-[#3E3E3A] before:top-1/2 before:bottom-0 before:left-[0.4rem] before:absolute">
<span class="relative py-1 bg-white dark:bg-[#161615]">
<span class="flex items-center justify-center rounded-full bg-[#FDFDFC] dark:bg-[#161615] shadow-[0px_0px_1px_0px_rgba(0,0,0,0.03),0px_1px_2px_0px_rgba(0,0,0,0.06)] w-3.5 h-3.5 border dark:border-[#3E3E3A] border-[#e3e3e0]">
<span class="rounded-full bg-[#dbdbd7] dark:bg-[#3E3E3A] w-1.5 h-1.5"></span>
</span>
</span>
<span>
Read the
<a href="https://laravel.com/docs" target="_blank" class="inline-flex items-center space-x-1 font-medium underline underline-offset-4 text-[#f53003] dark:text-[#FF4433] ml-1">
<span>Documentation</span>
<svg
width="10"
height="11"
viewBox="0 0 10 11"
fill="none"
xmlns="http://www.w3.org/2000/svg"
class="w-2.5 h-2.5"
>
<path
d="M7.70833 6.95834V2.79167H3.54167M2.5 8L7.5 3.00001"
stroke="currentColor"
stroke-linecap="square"
/>
</svg>
</a>
</span>
</li>
<li class="flex items-center gap-4 py-2 relative before:border-l before:border-[#e3e3e0] dark:before:border-[#3E3E3A] before:bottom-1/2 before:top-0 before:left-[0.4rem] before:absolute">
<span class="relative py-1 bg-white dark:bg-[#161615]">
<span class="flex items-center justify-center rounded-full bg-[#FDFDFC] dark:bg-[#161615] shadow-[0px_0px_1px_0px_rgba(0,0,0,0.03),0px_1px_2px_0px_rgba(0,0,0,0.06)] w-3.5 h-3.5 border dark:border-[#3E3E3A] border-[#e3e3e0]">
<span class="rounded-full bg-[#dbdbd7] dark:bg-[#3E3E3A] w-1.5 h-1.5"></span>
</span>
</span>
<span>
Watch video tutorials at
<a href="https://laracasts.com" target="_blank" class="inline-flex items-center space-x-1 font-medium underline underline-offset-4 text-[#f53003] dark:text-[#FF4433] ml-1">
<span>Laracasts</span>
<svg
width="10"
height="11"
viewBox="0 0 10 11"
fill="none"
xmlns="http://www.w3.org/2000/svg"
class="w-2.5 h-2.5"
>
<path
d="M7.70833 6.95834V2.79167H3.54167M2.5 8L7.5 3.00001"
stroke="currentColor"
stroke-linecap="square"
/>
</svg>
</a>
</span>
</li>
</ul>
<ul class="flex gap-3 text-sm leading-normal">
<li>
<a href="https://cloud.laravel.com" target="_blank" class="inline-block dark:bg-[#eeeeec] dark:border-[#eeeeec] dark:text-[#1C1C1A] dark:hover:bg-white dark:hover:border-white hover:bg-black hover:border-black px-5 py-1.5 bg-[#1b1b18] rounded-sm border border-black text-white text-sm leading-normal">
Deploy now
</a>
</li>
</ul>
</div>
<div class="bg-[#fff2f2] dark:bg-[#1D0002] relative lg:-ml-px -mb-px lg:mb-0 rounded-t-lg lg:rounded-t-none lg:rounded-r-lg aspect-[335/376] lg:aspect-auto w-full lg:w-[438px] shrink-0 overflow-hidden">
{{-- Laravel Logo --}}
<svg class="w-full text-[#F53003] dark:text-[#F61500] transition-all translate-y-0 opacity-100 max-w-none duration-750 starting:opacity-0 starting:translate-y-6" viewBox="0 0 438 104" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M17.2036 -3H0V102.197H49.5189V86.7187H17.2036V-3Z" fill="currentColor" />
<path d="M110.256 41.6337C108.061 38.1275 104.945 35.3731 100.905 33.3681C96.8667 31.3647 92.8016 30.3618 88.7131 30.3618C83.4247 30.3618 78.5885 31.3389 74.201 33.2923C69.8111 35.2456 66.0474 37.928 62.9059 41.3333C59.7643 44.7401 57.3198 48.6726 55.5754 53.1293C53.8287 57.589 52.9572 62.274 52.9572 67.1813C52.9572 72.1925 53.8287 76.8995 55.5754 81.3069C57.3191 85.7173 59.7636 89.6241 62.9059 93.0293C66.0474 96.4361 69.8119 99.1155 74.201 101.069C78.5885 103.022 83.4247 103.999 88.7131 103.999C92.8016 103.999 96.8667 102.997 100.905 100.994C104.945 98.9911 108.061 96.2359 110.256 92.7282V102.195H126.563V32.1642H110.256V41.6337ZM108.76 75.7472C107.762 78.4531 106.366 80.8078 104.572 82.8112C102.776 84.8161 100.606 86.4183 98.0637 87.6206C95.5202 88.823 92.7004 89.4238 89.6103 89.4238C86.5178 89.4238 83.7252 88.823 81.2324 87.6206C78.7388 86.4183 76.5949 84.8161 74.7998 82.8112C73.004 80.8078 71.6319 78.4531 70.6856 75.7472C69.7356 73.0421 69.2644 70.1868 69.2644 67.1821C69.2644 64.1758 69.7356 61.3205 70.6856 58.6154C71.6319 55.9102 73.004 53.5571 74.7998 51.5522C76.5949 49.5495 78.738 47.9451 81.2324 46.7427C83.7252 45.5404 86.5178 44.9396 89.6103 44.9396C92.7012 44.9396 95.5202 45.5404 98.0637 46.7427C100.606 47.9451 102.776 49.5487 104.572 51.5522C106.367 53.5571 107.762 55.9102 108.76 58.6154C109.756 61.3205 110.256 64.1758 110.256 67.1821C110.256 70.1868 109.756 73.0421 108.76 75.7472Z" fill="currentColor" />
<path d="M242.805 41.6337C240.611 38.1275 237.494 35.3731 233.455 33.3681C229.416 31.3647 225.351 30.3618 221.262 30.3618C215.974 30.3618 211.138 31.3389 206.75 33.2923C202.36 35.2456 198.597 37.928 195.455 41.3333C192.314 44.7401 189.869 48.6726 188.125 53.1293C186.378 57.589 185.507 62.274 185.507 67.1813C185.507 72.1925 186.378 76.8995 188.125 81.3069C189.868 85.7173 192.313 89.6241 195.455 93.0293C198.597 96.4361 202.361 99.1155 206.75 101.069C211.138 103.022 215.974 103.999 221.262 103.999C225.351 103.999 229.416 102.997 233.455 100.994C237.494 98.9911 240.611 96.2359 242.805 92.7282V102.195H259.112V32.1642H242.805V41.6337ZM241.31 75.7472C240.312 78.4531 238.916 80.8078 237.122 82.8112C235.326 84.8161 233.156 86.4183 230.614 87.6206C228.07 88.823 225.251 89.4238 222.16 89.4238C219.068 89.4238 216.275 88.823 213.782 87.6206C211.289 86.4183 209.145 84.8161 207.35 82.8112C205.554 80.8078 204.182 78.4531 203.236 75.7472C202.286 73.0421 201.814 70.1868 201.814 67.1821C201.814 64.1758 202.286 61.3205 203.236 58.6154C204.182 55.9102 205.554 53.5571 207.35 51.5522C209.145 49.5495 211.288 47.9451 213.782 46.7427C216.275 45.5404 219.068 44.9396 222.16 44.9396C225.251 44.9396 228.07 45.5404 230.614 46.7427C233.156 47.9451 235.326 49.5487 237.122 51.5522C238.917 53.5571 240.312 55.9102 241.31 58.6154C242.306 61.3205 242.806 64.1758 242.806 67.1821C242.805 70.1868 242.305 73.0421 241.31 75.7472Z" fill="currentColor" />
<path d="M438 -3H421.694V102.197H438V-3Z" fill="currentColor" />
<path d="M139.43 102.197H155.735V48.2834H183.712V32.1665H139.43V102.197Z" fill="currentColor" />
<path d="M324.49 32.1665L303.995 85.794L283.498 32.1665H266.983L293.748 102.197H314.242L341.006 32.1665H324.49Z" fill="currentColor" />
<path d="M376.571 30.3656C356.603 30.3656 340.797 46.8497 340.797 67.1828C340.797 89.6597 356.094 104 378.661 104C391.29 104 399.354 99.1488 409.206 88.5848L398.189 80.0226C398.183 80.031 389.874 90.9895 377.468 90.9895C363.048 90.9895 356.977 79.3111 356.977 73.269H411.075C413.917 50.1328 398.775 30.3656 376.571 30.3656ZM357.02 61.0967C357.145 59.7487 359.023 43.3761 376.442 43.3761C393.861 43.3761 395.978 59.7464 396.099 61.0967H357.02Z" fill="currentColor" />
</svg>
{{-- Light Mode 12 SVG --}}
<svg class="w-[448px] max-w-none relative -mt-[4.9rem] -ml-8 lg:ml-0 lg:-mt-[6.6rem] dark:hidden" viewBox="0 0 440 376" fill="none" xmlns="http://www.w3.org/2000/svg">
<g class="transition-all delay-300 translate-y-0 opacity-100 duration-750 starting:opacity-0 starting:translate-y-4">
<path d="M188.263 355.73L188.595 355.73C195.441 348.845 205.766 339.761 219.569 328.477C232.93 317.193 242.978 308.205 249.714 301.511C256.34 294.626 260.867 287.358 263.296 279.708C265.725 272.058 264.565 264.121 259.816 255.896C254.516 246.716 247.062 239.352 237.454 233.805C227.957 228.067 217.908 225.198 207.307 225.198C196.927 225.197 190.136 227.97 186.934 233.516C183.621 238.872 184.726 246.331 190.247 255.894L125.647 255.891C116.371 239.825 112.395 225.481 113.72 212.858C115.265 200.235 121.559 190.481 132.602 183.596C143.754 176.52 158.607 172.982 177.159 172.983C196.594 172.984 215.863 176.523 234.968 183.6C253.961 190.486 271.299 200.241 286.98 212.864C302.661 225.488 315.14 239.833 324.416 255.899C333.03 270.817 336.841 283.918 335.847 295.203C335.075 306.487 331.376 316.336 324.75 324.751C318.346 333.167 308.408 343.494 294.936 355.734L377.094 355.737L405.917 405.656L217.087 405.649L188.263 355.73Z" fill="black" />
<path d="M9.11884 226.339L-13.7396 226.338L-42.7286 176.132L43.0733 176.135L175.595 405.649L112.651 405.647L9.11884 226.339Z" fill="black" />
<path d="M188.263 355.73L188.595 355.73C195.441 348.845 205.766 339.761 219.569 328.477C232.93 317.193 242.978 308.205 249.714 301.511C256.34 294.626 260.867 287.358 263.296 279.708C265.725 272.058 264.565 264.121 259.816 255.896C254.516 246.716 247.062 239.352 237.454 233.805C227.957 228.067 217.908 225.198 207.307 225.198C196.927 225.197 190.136 227.97 186.934 233.516C183.621 238.872 184.726 246.331 190.247 255.894L125.647 255.891C116.371 239.825 112.395 225.481 113.72 212.858C115.265 200.235 121.559 190.481 132.602 183.596C143.754 176.52 158.607 172.982 177.159 172.983C196.594 172.984 215.863 176.523 234.968 183.6C253.961 190.486 271.299 200.241 286.98 212.864C302.661 225.488 315.14 239.833 324.416 255.899C333.03 270.817 336.841 283.918 335.847 295.203C335.075 306.487 331.376 316.336 324.75 324.751C318.346 333.167 308.408 343.494 294.936 355.734L377.094 355.737L405.917 405.656L217.087 405.649L188.263 355.73Z" stroke="#1B1B18" stroke-width="1" />
<path d="M9.11884 226.339L-13.7396 226.338L-42.7286 176.132L43.0733 176.135L175.595 405.649L112.651 405.647L9.11884 226.339Z" stroke="#1B1B18" stroke-width="1" />
<path d="M204.592 327.449L204.923 327.449C211.769 320.564 222.094 311.479 235.897 300.196C249.258 288.912 259.306 279.923 266.042 273.23C272.668 266.345 277.195 259.077 279.624 251.427C282.053 243.777 280.893 235.839 276.145 227.615C270.844 218.435 263.39 211.071 253.782 205.524C244.285 199.786 234.236 196.917 223.635 196.916C213.255 196.916 206.464 199.689 203.262 205.235C199.949 210.59 201.054 218.049 206.575 227.612L141.975 227.61C132.699 211.544 128.723 197.2 130.048 184.577C131.593 171.954 137.887 162.2 148.93 155.315C160.083 148.239 174.935 144.701 193.487 144.702C212.922 144.703 232.192 148.242 251.296 155.319C270.289 162.205 287.627 171.96 303.308 184.583C318.989 197.207 331.468 211.552 340.745 227.618C349.358 242.536 353.169 255.637 352.175 266.921C351.403 278.205 347.704 288.055 341.078 296.47C334.674 304.885 324.736 315.213 311.264 327.453L393.422 327.456L422.246 377.375L233.415 377.368L204.592 327.449Z" fill="#F8B803" />
<path d="M25.447 198.058L2.58852 198.057L-26.4005 147.851L59.4015 147.854L191.923 377.368L128.979 377.365L25.447 198.058Z" fill="#F8B803" />
<path d="M204.592 327.449L204.923 327.449C211.769 320.564 222.094 311.479 235.897 300.196C249.258 288.912 259.306 279.923 266.042 273.23C272.668 266.345 277.195 259.077 279.624 251.427C282.053 243.777 280.893 235.839 276.145 227.615C270.844 218.435 263.39 211.071 253.782 205.524C244.285 199.786 234.236 196.917 223.635 196.916C213.255 196.916 206.464 199.689 203.262 205.235C199.949 210.59 201.054 218.049 206.575 227.612L141.975 227.61C132.699 211.544 128.723 197.2 130.048 184.577C131.593 171.954 137.887 162.2 148.93 155.315C160.083 148.239 174.935 144.701 193.487 144.702C212.922 144.703 232.192 148.242 251.296 155.319C270.289 162.205 287.627 171.96 303.308 184.583C318.989 197.207 331.468 211.552 340.745 227.618C349.358 242.536 353.169 255.637 352.175 266.921C351.403 278.205 347.704 288.055 341.078 296.47C334.674 304.885 324.736 315.213 311.264 327.453L393.422 327.456L422.246 377.375L233.415 377.368L204.592 327.449Z" stroke="#1B1B18" stroke-width="1" />
<path d="M25.447 198.058L2.58852 198.057L-26.4005 147.851L59.4015 147.854L191.923 377.368L128.979 377.365L25.447 198.058Z" stroke="#1B1B18" stroke-width="1" />
</g>
<g style="mix-blend-mode: hard-light" class="transition-all delay-300 translate-y-0 opacity-100 duration-750 starting:opacity-0 starting:translate-y-4">
<path d="M217.342 305.363L217.673 305.363C224.519 298.478 234.844 289.393 248.647 278.11C262.008 266.826 272.056 257.837 278.792 251.144C285.418 244.259 289.945 236.991 292.374 229.341C294.803 221.691 293.643 213.753 288.895 205.529C283.594 196.349 276.14 188.985 266.532 183.438C257.035 177.7 246.986 174.831 236.385 174.83C226.005 174.83 219.214 177.603 216.012 183.149C212.699 188.504 213.804 195.963 219.325 205.527L154.725 205.524C145.449 189.458 141.473 175.114 142.798 162.491C144.343 149.868 150.637 140.114 161.68 133.229C172.833 126.153 187.685 122.615 206.237 122.616C225.672 122.617 244.942 126.156 264.046 133.233C283.039 140.119 300.377 149.874 316.058 162.497C331.739 175.121 344.218 189.466 353.495 205.532C362.108 220.45 365.919 233.551 364.925 244.835C364.153 256.12 360.454 265.969 353.828 274.384C347.424 282.799 337.486 293.127 324.014 305.367L406.172 305.37L434.996 355.289L246.165 355.282L217.342 305.363Z" fill="#F0ACB8" />
<path d="M38.197 175.972L15.3385 175.971L-13.6505 125.765L72.1515 125.768L204.673 355.282L141.729 355.279L38.197 175.972Z" fill="#F0ACB8" />
<path d="M217.342 305.363L217.673 305.363C224.519 298.478 234.844 289.393 248.647 278.11C262.008 266.826 272.056 257.837 278.792 251.144C285.418 244.259 289.945 236.991 292.374 229.341C294.803 221.691 293.643 213.753 288.895 205.529C283.594 196.349 276.14 188.985 266.532 183.438C257.035 177.7 246.986 174.831 236.385 174.83C226.005 174.83 219.214 177.603 216.012 183.149C212.699 188.504 213.804 195.963 219.325 205.527L154.725 205.524C145.449 189.458 141.473 175.114 142.798 162.491C144.343 149.868 150.637 140.114 161.68 133.229C172.833 126.153 187.685 122.615 206.237 122.616C225.672 122.617 244.942 126.156 264.046 133.233C283.039 140.119 300.377 149.874 316.058 162.497C331.739 175.121 344.218 189.466 353.495 205.532C362.108 220.45 365.919 233.551 364.925 244.835C364.153 256.12 360.454 265.969 353.828 274.384C347.424 282.799 337.486 293.127 324.014 305.367L406.172 305.37L434.996 355.289L246.165 355.282L217.342 305.363Z" stroke="#1B1B18" stroke-width="1" />
<path d="M38.197 175.972L15.3385 175.971L-13.6505 125.765L72.1515 125.768L204.673 355.282L141.729 355.279L38.197 175.972Z" stroke="#1B1B18" stroke-width="1" />
</g>
<g style="mix-blend-mode: plus-darker" class="transition-all delay-300 translate-y-0 opacity-100 duration-750 starting:opacity-0 starting:translate-y-4">
<path d="M230.951 281.792L231.282 281.793C238.128 274.907 248.453 265.823 262.256 254.539C275.617 243.256 285.666 234.267 292.402 227.573C299.027 220.688 303.554 213.421 305.983 205.771C308.412 198.12 307.253 190.183 302.504 181.959C297.203 172.778 289.749 165.415 280.142 159.868C270.645 154.13 260.596 151.26 249.995 151.26C239.615 151.26 232.823 154.033 229.621 159.579C226.309 164.934 227.413 172.393 232.935 181.956L168.335 181.954C159.058 165.888 155.082 151.543 156.407 138.92C157.953 126.298 164.247 116.544 175.289 109.659C186.442 102.583 201.294 99.045 219.846 99.0457C239.281 99.0464 258.551 102.585 277.655 109.663C296.649 116.549 313.986 126.303 329.667 138.927C345.349 151.551 357.827 165.895 367.104 181.961C375.718 196.88 379.528 209.981 378.535 221.265C377.762 232.549 374.063 242.399 367.438 250.814C361.033 259.229 351.095 269.557 337.624 281.796L419.782 281.8L448.605 331.719L259.774 331.712L230.951 281.792Z" fill="#F3BEC7" />
<path d="M51.8063 152.402L28.9479 152.401L-0.0411453 102.195L85.7608 102.198L218.282 331.711L155.339 331.709L51.8063 152.402Z" fill="#F3BEC7" />
<path d="M230.951 281.792L231.282 281.793C238.128 274.907 248.453 265.823 262.256 254.539C275.617 243.256 285.666 234.267 292.402 227.573C299.027 220.688 303.554 213.421 305.983 205.771C308.412 198.12 307.253 190.183 302.504 181.959C297.203 172.778 289.749 165.415 280.142 159.868C270.645 154.13 260.596 151.26 249.995 151.26C239.615 151.26 232.823 154.033 229.621 159.579C226.309 164.934 227.413 172.393 232.935 181.956L168.335 181.954C159.058 165.888 155.082 151.543 156.407 138.92C157.953 126.298 164.247 116.544 175.289 109.659C186.442 102.583 201.294 99.045 219.846 99.0457C239.281 99.0464 258.551 102.585 277.655 109.663C296.649 116.549 313.986 126.303 329.667 138.927C345.349 151.551 357.827 165.895 367.104 181.961C375.718 196.88 379.528 209.981 378.535 221.265C377.762 232.549 374.063 242.399 367.438 250.814C361.033 259.229 351.095 269.557 337.624 281.796L419.782 281.8L448.605 331.719L259.774 331.712L230.951 281.792Z" stroke="#1B1B18" stroke-width="1" />
<path d="M51.8063 152.402L28.9479 152.401L-0.0411453 102.195L85.7608 102.198L218.282 331.711L155.339 331.709L51.8063 152.402Z" stroke="#1B1B18" stroke-width="1" />
</g>
<g class="transition-all delay-300 translate-y-0 opacity-100 duration-750 starting:opacity-0 starting:translate-y-4">
<path d="M188.467 355.363L188.798 355.363C195.644 348.478 205.969 339.393 219.772 328.11C233.133 316.826 243.181 307.837 249.917 301.144C253.696 297.217 256.792 293.166 259.205 288.991C261.024 285.845 262.455 282.628 263.499 279.341C265.928 271.691 264.768 263.753 260.02 255.529C254.719 246.349 247.265 238.985 237.657 233.438C228.16 227.7 218.111 224.831 207.51 224.83C197.13 224.83 190.339 227.603 187.137 233.149C183.824 238.504 184.929 245.963 190.45 255.527L125.851 255.524C116.574 239.458 112.598 225.114 113.923 212.491C114.615 206.836 116.261 201.756 118.859 197.253C122.061 191.704 126.709 187.03 132.805 183.229C143.958 176.153 158.81 172.615 177.362 172.616C196.797 172.617 216.067 176.156 235.171 183.233C254.164 190.119 271.502 199.874 287.183 212.497C302.864 225.121 315.343 239.466 324.62 255.532C333.233 270.45 337.044 283.551 336.05 294.835C335.46 303.459 333.16 311.245 329.151 318.194C327.915 320.337 326.515 322.4 324.953 324.384C318.549 332.799 308.611 343.127 295.139 355.367L377.297 355.37L406.121 405.289L217.29 405.282L188.467 355.363Z" stroke="#1B1B18" stroke-width="1" stroke-linejoin="bevel" />
<path d="M9.32197 225.972L-13.5365 225.971L-42.5255 175.765L43.2765 175.768L175.798 405.282L112.854 405.279L9.32197 225.972Z" stroke="#1B1B18" stroke-width="1" stroke-linejoin="bevel" />
<path d="M345.247 111.915C329.566 99.2919 312.229 89.5371 293.235 82.6512L235.167 183.228C254.161 190.114 271.498 199.869 287.179 212.492L345.247 111.915Z" stroke="#1B1B18" stroke-width="1" stroke-linejoin="bevel" />
<path d="M382.686 154.964C373.41 138.898 360.931 124.553 345.25 111.93L287.182 212.506C302.863 225.13 315.342 239.475 324.618 255.541L382.686 154.964Z" stroke="#1B1B18" stroke-width="1" stroke-linejoin="bevel" />
<path d="M293.243 82.6472C274.139 75.57 254.869 72.031 235.434 72.0303L177.366 172.607C196.801 172.608 216.071 176.147 235.175 183.224L293.243 82.6472Z" stroke="#1B1B18" stroke-width="1" stroke-linejoin="bevel" />
<path d="M394.118 194.257C395.112 182.973 391.301 169.872 382.688 154.953L324.619 255.53C333.233 270.448 337.044 283.55 336.05 294.834L394.118 194.257Z" stroke="#1B1B18" stroke-width="1" stroke-linejoin="bevel" />
<path d="M235.432 72.0311C216.88 72.0304 202.027 75.5681 190.875 82.6442L132.806 183.221C143.959 176.145 158.812 172.607 177.363 172.608L235.432 72.0311Z" stroke="#1B1B18" stroke-width="1" stroke-linejoin="bevel" />
<path d="M265.59 124.25C276.191 124.251 286.24 127.12 295.737 132.858L237.669 233.435C228.172 227.697 218.123 224.828 207.522 224.827L265.59 124.25Z" stroke="#1B1B18" stroke-width="1" stroke-linejoin="bevel" />
<path d="M295.719 132.859C305.326 138.406 312.78 145.77 318.081 154.95L260.013 255.527C254.712 246.347 247.258 238.983 237.651 233.436L295.719 132.859Z" stroke="#1B1B18" stroke-width="1" stroke-linejoin="bevel" />
<path d="M387.218 217.608C391.227 210.66 393.527 202.874 394.117 194.25L336.049 294.827C335.459 303.451 333.159 311.237 329.15 318.185L387.218 217.608Z" stroke="#1B1B18" stroke-width="1" stroke-linejoin="bevel" />
<path d="M245.211 132.577C248.413 127.03 255.204 124.257 265.584 124.258L207.516 224.835C197.136 224.834 190.345 227.607 187.143 233.154L245.211 132.577Z" stroke="#1B1B18" stroke-width="1" stroke-linejoin="bevel" />
<path d="M318.094 154.945C322.842 163.17 324.002 171.107 321.573 178.757L263.505 279.334C265.934 271.684 264.774 263.746 260.026 255.522L318.094 154.945Z" stroke="#1B1B18" stroke-width="1" stroke-linejoin="bevel" />
<path d="M176.925 96.6737C180.127 91.1249 184.776 86.4503 190.871 82.6499L132.803 183.227C126.708 187.027 122.059 191.702 118.857 197.25L176.925 96.6737Z" stroke="#1B1B18" stroke-width="1" stroke-linejoin="bevel" />
<path d="M387.226 217.606C385.989 219.749 384.59 221.813 383.028 223.797L324.96 324.373C326.522 322.39 327.921 320.326 329.157 318.183L387.226 217.606Z" stroke="#1B1B18" stroke-width="1" stroke-linejoin="bevel" />
<path d="M317.269 188.408C319.087 185.262 320.519 182.045 321.562 178.758L263.494 279.335C262.451 282.622 261.019 285.839 259.201 288.985L317.269 188.408Z" stroke="#1B1B18" stroke-width="1" stroke-linejoin="bevel" />
<path d="M245.208 132.573C241.895 137.928 243 145.387 248.522 154.95L190.454 255.527C184.932 245.964 183.827 238.505 187.14 233.15L245.208 132.573Z" stroke="#1B1B18" stroke-width="1" stroke-linejoin="bevel" />
<path d="M176.93 96.6719C174.331 101.175 172.686 106.255 171.993 111.91L113.925 212.487C114.618 206.831 116.263 201.752 118.862 197.249L176.93 96.6719Z" stroke="#1B1B18" stroke-width="1" stroke-linejoin="bevel" />
<path d="M317.266 188.413C314.853 192.589 311.757 196.64 307.978 200.566L249.91 301.143C253.689 297.216 256.785 293.166 259.198 288.99L317.266 188.413Z" stroke="#1B1B18" stroke-width="1" stroke-linejoin="bevel" />
<path d="M464.198 304.708L435.375 254.789L377.307 355.366L406.13 405.285L464.198 304.708Z" stroke="#1B1B18" stroke-width="1" stroke-linejoin="bevel" />
<path d="M353.209 254.787C366.68 242.548 376.618 232.22 383.023 223.805L324.955 324.382C318.55 332.797 308.612 343.124 295.141 355.364L353.209 254.787Z" stroke="#1B1B18" stroke-width="1" stroke-linejoin="bevel" />
<path d="M435.37 254.787L353.212 254.784L295.144 355.361L377.302 355.364L435.37 254.787Z" stroke="#1B1B18" stroke-width="1" stroke-linejoin="bevel" />
<path d="M183.921 154.947L248.521 154.95L190.453 255.527L125.853 255.524L183.921 154.947Z" stroke="#1B1B18" stroke-width="1" stroke-linejoin="bevel" />
<path d="M171.992 111.914C170.668 124.537 174.643 138.881 183.92 154.947L125.852 255.524C116.575 239.458 112.599 225.114 113.924 212.491L171.992 111.914Z" stroke="#1B1B18" stroke-width="1" stroke-linejoin="bevel" />
<path d="M307.987 200.562C301.251 207.256 291.203 216.244 277.842 227.528L219.774 328.105C233.135 316.821 243.183 307.832 249.919 301.139L307.987 200.562Z" stroke="#1B1B18" stroke-width="1" stroke-linejoin="bevel" />
<path d="M15.5469 75.1797L44.5359 125.386L-13.5321 225.963L-42.5212 175.756L15.5469 75.1797Z" stroke="#1B1B18" stroke-width="1" stroke-linejoin="bevel" />
<path d="M277.836 227.536C264.033 238.82 253.708 247.904 246.862 254.789L188.794 355.366C195.64 348.481 205.965 339.397 219.768 328.113L277.836 227.536Z" stroke="#1B1B18" stroke-width="1" stroke-linejoin="bevel" />
<path d="M275.358 304.706L464.189 304.713L406.12 405.29L217.29 405.283L275.358 304.706Z" stroke="#1B1B18" stroke-width="1" stroke-linejoin="bevel" />
<path d="M44.5279 125.39L67.3864 125.39L9.31834 225.967L-13.5401 225.966L44.5279 125.39Z" stroke="#1B1B18" stroke-width="1" stroke-linejoin="bevel" />
<path d="M101.341 75.1911L233.863 304.705L175.795 405.282L43.2733 175.768L101.341 75.1911ZM15.5431 75.19L-42.525 175.767L43.277 175.77L101.345 75.1932L15.5431 75.19Z" stroke="#1B1B18" stroke-width="1" stroke-linejoin="bevel" />
<path d="M246.866 254.784L246.534 254.784L188.466 355.361L188.798 355.361L246.866 254.784Z" stroke="#1B1B18" stroke-width="1" stroke-linejoin="bevel" />
<path d="M246.539 254.781L275.362 304.701L217.294 405.277L188.471 355.358L246.539 254.781Z" stroke="#1B1B18" stroke-width="1" stroke-linejoin="bevel" />
<path d="M67.3906 125.391L170.923 304.698L112.855 405.275L9.32257 225.967L67.3906 125.391Z" stroke="#1B1B18" stroke-width="1" stroke-linejoin="bevel" />
<path d="M170.921 304.699L233.865 304.701L175.797 405.278L112.853 405.276L170.921 304.699Z" stroke="#1B1B18" stroke-width="1" stroke-linejoin="bevel" />
</g>
<g style="mix-blend-mode: hard-light" class="transition-all delay-300 translate-y-0 opacity-100 duration-750 starting:opacity-0 starting:translate-y-4">
<path d="M246.544 254.79L246.875 254.79C253.722 247.905 264.046 238.82 277.849 227.537C291.21 216.253 301.259 207.264 307.995 200.57C314.62 193.685 319.147 186.418 321.577 178.768C324.006 171.117 322.846 163.18 318.097 154.956C312.796 145.775 305.342 138.412 295.735 132.865C286.238 127.127 276.189 124.258 265.588 124.257C255.208 124.257 248.416 127.03 245.214 132.576C241.902 137.931 243.006 145.39 248.528 154.953L183.928 154.951C174.652 138.885 170.676 124.541 172 111.918C173.546 99.2946 179.84 89.5408 190.882 82.6559C202.035 75.5798 216.887 72.0421 235.439 72.0428C254.874 72.0435 274.144 75.5825 293.248 82.6598C312.242 89.5457 329.579 99.3005 345.261 111.924C360.942 124.548 373.421 138.892 382.697 154.958C391.311 169.877 395.121 182.978 394.128 194.262C393.355 205.546 389.656 215.396 383.031 223.811C376.627 232.226 366.688 242.554 353.217 254.794L435.375 254.797L464.198 304.716L275.367 304.709L246.544 254.79Z" fill="#F0ACB8" />
<path d="M246.544 254.79L246.875 254.79C253.722 247.905 264.046 238.82 277.849 227.537C291.21 216.253 301.259 207.264 307.995 200.57C314.62 193.685 319.147 186.418 321.577 178.768C324.006 171.117 322.846 163.18 318.097 154.956C312.796 145.775 305.342 138.412 295.735 132.865C286.238 127.127 276.189 124.258 265.588 124.257C255.208 124.257 248.416 127.03 245.214 132.576C241.902 137.931 243.006 145.39 248.528 154.953L183.928 154.951C174.652 138.885 170.676 124.541 172 111.918C173.546 99.2946 179.84 89.5408 190.882 82.6559C202.035 75.5798 216.887 72.0421 235.439 72.0428C254.874 72.0435 274.144 75.5825 293.248 82.6598C312.242 89.5457 329.579 99.3005 345.261 111.924C360.942 124.548 373.421 138.892 382.697 154.958C391.311 169.877 395.121 182.978 394.128 194.262C393.355 205.546 389.656 215.396 383.031 223.811C376.627 232.226 366.688 242.554 353.217 254.794L435.375 254.797L464.198 304.716L275.367 304.709L246.544 254.79Z" stroke="#1B1B18" stroke-width="1" stroke-linejoin="round" />
</g>
<g style="mix-blend-mode: hard-light" class="transition-all delay-300 translate-y-0 opacity-100 duration-750 starting:opacity-0 starting:translate-y-4">
<path d="M67.41 125.402L44.5515 125.401L15.5625 75.1953L101.364 75.1985L233.886 304.712L170.942 304.71L67.41 125.402Z" fill="#F0ACB8" />
<path d="M67.41 125.402L44.5515 125.401L15.5625 75.1953L101.364 75.1985L233.886 304.712L170.942 304.71L67.41 125.402Z" stroke="#1B1B18" stroke-width="1" />
</g>
</svg>
{{-- Dark Mode 12 SVG --}}
<svg class="w-[448px] max-w-none relative -mt-[4.9rem] -ml-8 lg:ml-0 lg:-mt-[6.6rem] hidden dark:block" viewBox="0 0 440 376" fill="none" xmlns="http://www.w3.org/2000/svg">
<g class="transition-all delay-300 translate-y-0 opacity-100 duration-750 starting:opacity-0 starting:translate-y-4">
<path d="M188.263 355.73L188.595 355.73C195.441 348.845 205.766 339.761 219.569 328.477C232.93 317.193 242.978 308.205 249.714 301.511C256.34 294.626 260.867 287.358 263.296 279.708C265.725 272.058 264.565 264.121 259.816 255.896C254.516 246.716 247.062 239.352 237.454 233.805C227.957 228.067 217.908 225.198 207.307 225.198C196.927 225.197 190.136 227.97 186.934 233.516C183.621 238.872 184.726 246.331 190.247 255.894L125.647 255.891C116.371 239.825 112.395 225.481 113.72 212.858C115.265 200.235 121.559 190.481 132.602 183.596C143.754 176.52 158.607 172.982 177.159 172.983C196.594 172.984 215.863 176.523 234.968 183.6C253.961 190.486 271.299 200.241 286.98 212.864C302.661 225.488 315.14 239.833 324.416 255.899C333.03 270.817 336.841 283.918 335.847 295.203C335.075 306.487 331.376 316.336 324.75 324.751C318.346 333.167 308.408 343.494 294.936 355.734L377.094 355.737L405.917 405.656L217.087 405.649L188.263 355.73Z" fill="black"/>
<path d="M9.11884 226.339L-13.7396 226.338L-42.7286 176.132L43.0733 176.135L175.595 405.649L112.651 405.647L9.11884 226.339Z" fill="black"/>
<path d="M188.263 355.73L188.595 355.73C195.441 348.845 205.766 339.761 219.569 328.477C232.93 317.193 242.978 308.205 249.714 301.511C256.34 294.626 260.867 287.358 263.296 279.708C265.725 272.058 264.565 264.121 259.816 255.896C254.516 246.716 247.062 239.352 237.454 233.805C227.957 228.067 217.908 225.198 207.307 225.198C196.927 225.197 190.136 227.97 186.934 233.516C183.621 238.872 184.726 246.331 190.247 255.894L125.647 255.891C116.371 239.825 112.395 225.481 113.72 212.858C115.265 200.235 121.559 190.481 132.602 183.596C143.754 176.52 158.607 172.982 177.159 172.983C196.594 172.984 215.863 176.523 234.968 183.6C253.961 190.486 271.299 200.241 286.98 212.864C302.661 225.488 315.14 239.833 324.416 255.899C333.03 270.817 336.841 283.918 335.847 295.203C335.075 306.487 331.376 316.336 324.75 324.751C318.346 333.167 308.408 343.494 294.936 355.734L377.094 355.737L405.917 405.656L217.087 405.649L188.263 355.73Z" stroke="#FF750F" stroke-width="1"/>
<path d="M9.11884 226.339L-13.7396 226.338L-42.7286 176.132L43.0733 176.135L175.595 405.649L112.651 405.647L9.11884 226.339Z" stroke="#FF750F" stroke-width="1"/>
<path d="M204.592 327.449L204.923 327.449C211.769 320.564 222.094 311.479 235.897 300.196C249.258 288.912 259.306 279.923 266.042 273.23C272.668 266.345 277.195 259.077 279.624 251.427C282.053 243.777 280.893 235.839 276.145 227.615C270.844 218.435 263.39 211.071 253.782 205.524C244.285 199.786 234.236 196.917 223.635 196.916C213.255 196.916 206.464 199.689 203.262 205.235C199.949 210.59 201.054 218.049 206.575 227.612L141.975 227.61C132.699 211.544 128.723 197.2 130.048 184.577C131.593 171.954 137.887 162.2 148.93 155.315C160.083 148.239 174.935 144.701 193.487 144.702C212.922 144.703 232.192 148.242 251.296 155.319C270.289 162.205 287.627 171.96 303.308 184.583C318.989 197.207 331.468 211.552 340.745 227.618C349.358 242.536 353.169 255.637 352.175 266.921C351.403 278.205 347.704 288.055 341.078 296.47C334.674 304.885 324.736 315.213 311.264 327.453L393.422 327.456L422.246 377.375L233.415 377.368L204.592 327.449Z" fill="#391800"/>
<path d="M25.447 198.058L2.58852 198.057L-26.4005 147.851L59.4015 147.854L191.923 377.368L128.979 377.365L25.447 198.058Z" fill="#391800"/>
<path d="M204.592 327.449L204.923 327.449C211.769 320.564 222.094 311.479 235.897 300.196C249.258 288.912 259.306 279.923 266.042 273.23C272.668 266.345 277.195 259.077 279.624 251.427C282.053 243.777 280.893 235.839 276.145 227.615C270.844 218.435 263.39 211.071 253.782 205.524C244.285 199.786 234.236 196.917 223.635 196.916C213.255 196.916 206.464 199.689 203.262 205.235C199.949 210.59 201.054 218.049 206.575 227.612L141.975 227.61C132.699 211.544 128.723 197.2 130.048 184.577C131.593 171.954 137.887 162.2 148.93 155.315C160.083 148.239 174.935 144.701 193.487 144.702C212.922 144.703 232.192 148.242 251.296 155.319C270.289 162.205 287.627 171.96 303.308 184.583C318.989 197.207 331.468 211.552 340.745 227.618C349.358 242.536 353.169 255.637 352.175 266.921C351.403 278.205 347.704 288.055 341.078 296.47C334.674 304.885 324.736 315.213 311.264 327.453L393.422 327.456L422.246 377.375L233.415 377.368L204.592 327.449Z" stroke="#FF750F" stroke-width="1"/>
<path d="M25.447 198.058L2.58852 198.057L-26.4005 147.851L59.4015 147.854L191.923 377.368L128.979 377.365L25.447 198.058Z" stroke="#FF750F" stroke-width="1"/>
</g>
<g class="transition-all delay-300 translate-y-0 opacity-100 duration-750 starting:opacity-0 starting:translate-y-4" style="mix-blend-mode:hard-light">
<path d="M217.342 305.363L217.673 305.363C224.519 298.478 234.844 289.393 248.647 278.11C262.008 266.826 272.056 257.837 278.792 251.144C285.418 244.259 289.945 236.991 292.374 229.341C294.803 221.691 293.643 213.753 288.895 205.529C283.594 196.349 276.14 188.985 266.532 183.438C257.035 177.7 246.986 174.831 236.385 174.83C226.005 174.83 219.214 177.603 216.012 183.149C212.699 188.504 213.804 195.963 219.325 205.527L154.725 205.524C145.449 189.458 141.473 175.114 142.798 162.491C144.343 149.868 150.637 140.114 161.68 133.229C172.833 126.153 187.685 122.615 206.237 122.616C225.672 122.617 244.942 126.156 264.046 133.233C283.039 140.119 300.377 149.874 316.058 162.497C331.739 175.121 344.218 189.466 353.495 205.532C362.108 220.45 365.919 233.551 364.925 244.835C364.153 256.12 360.454 265.969 353.828 274.384C347.424 282.799 337.486 293.127 324.014 305.367L406.172 305.37L434.996 355.289L246.165 355.282L217.342 305.363Z" fill="#733000"/>
<path d="M38.197 175.972L15.3385 175.971L-13.6505 125.765L72.1515 125.768L204.673 355.282L141.729 355.279L38.197 175.972Z" fill="#733000"/>
<path d="M217.342 305.363L217.673 305.363C224.519 298.478 234.844 289.393 248.647 278.11C262.008 266.826 272.056 257.837 278.792 251.144C285.418 244.259 289.945 236.991 292.374 229.341C294.803 221.691 293.643 213.753 288.895 205.529C283.594 196.349 276.14 188.985 266.532 183.438C257.035 177.7 246.986 174.831 236.385 174.83C226.005 174.83 219.214 177.603 216.012 183.149C212.699 188.504 213.804 195.963 219.325 205.527L154.725 205.524C145.449 189.458 141.473 175.114 142.798 162.491C144.343 149.868 150.637 140.114 161.68 133.229C172.833 126.153 187.685 122.615 206.237 122.616C225.672 122.617 244.942 126.156 264.046 133.233C283.039 140.119 300.377 149.874 316.058 162.497C331.739 175.121 344.218 189.466 353.495 205.532C362.108 220.45 365.919 233.551 364.925 244.835C364.153 256.12 360.454 265.969 353.828 274.384C347.424 282.799 337.486 293.127 324.014 305.367L406.172 305.37L434.996 355.289L246.165 355.282L217.342 305.363Z" stroke="#FF750F" stroke-width="1"/>
<path d="M38.197 175.972L15.3385 175.971L-13.6505 125.765L72.1515 125.768L204.673 355.282L141.729 355.279L38.197 175.972Z" stroke="#FF750F" stroke-width="1"/>
</g>
<g class="transition-all delay-300 translate-y-0 opacity-100 duration-750 starting:opacity-0 starting:translate-y-4">
<path d="M217.342 305.363L217.673 305.363C224.519 298.478 234.844 289.393 248.647 278.11C262.008 266.826 272.056 257.837 278.792 251.144C285.418 244.259 289.945 236.991 292.374 229.341C294.803 221.691 293.643 213.753 288.895 205.529C283.594 196.349 276.14 188.985 266.532 183.438C257.035 177.7 246.986 174.831 236.385 174.83C226.005 174.83 219.214 177.603 216.012 183.149C212.699 188.504 213.804 195.963 219.325 205.527L154.726 205.524C145.449 189.458 141.473 175.114 142.798 162.491C144.343 149.868 150.637 140.114 161.68 133.229C172.833 126.153 187.685 122.615 206.237 122.616C225.672 122.617 244.942 126.156 264.046 133.233C283.039 140.119 300.377 149.874 316.058 162.497C331.739 175.121 344.218 189.466 353.495 205.532C362.108 220.45 365.919 233.551 364.925 244.835C364.153 256.12 360.454 265.969 353.828 274.384C347.424 282.799 337.486 293.127 324.014 305.367L406.172 305.37L434.996 355.289L246.165 355.282L217.342 305.363Z" stroke="#FF750F" stroke-width="1"/>
<path d="M38.197 175.972L15.3385 175.971L-13.6505 125.765L72.1515 125.768L204.673 355.282L141.729 355.279L38.197 175.972Z" stroke="#FF750F" stroke-width="1"/>
</g>
<g class="transition-all delay-300 translate-y-0 opacity-100 duration-750 starting:opacity-0 starting:translate-y-4">
<path d="M188.467 355.363L188.798 355.363C195.644 348.478 205.969 339.393 219.772 328.11C233.133 316.826 243.181 307.837 249.917 301.144C253.696 297.217 256.792 293.166 259.205 288.991C261.024 285.845 262.455 282.628 263.499 279.341C265.928 271.691 264.768 263.753 260.02 255.529C254.719 246.349 247.265 238.985 237.657 233.438C228.16 227.7 218.111 224.831 207.51 224.83C197.13 224.83 190.339 227.603 187.137 233.149C183.824 238.504 184.929 245.963 190.45 255.527L125.851 255.524C116.574 239.458 112.598 225.114 113.923 212.491C114.615 206.836 116.261 201.756 118.859 197.253C122.061 191.704 126.709 187.03 132.805 183.229C143.958 176.153 158.81 172.615 177.362 172.616C196.797 172.617 216.067 176.156 235.171 183.233C254.164 190.119 271.502 199.874 287.183 212.497C302.864 225.121 315.343 239.466 324.62 255.532C333.233 270.45 337.044 283.551 336.05 294.835C335.46 303.459 333.16 311.245 329.151 318.194C327.915 320.337 326.515 322.4 324.953 324.384C318.549 332.799 308.611 343.127 295.139 355.367L377.297 355.37L406.121 405.289L217.29 405.282L188.467 355.363Z" stroke="#FF750F" stroke-width="1" stroke-linejoin="bevel"/>
<path d="M9.32197 225.972L-13.5365 225.971L-42.5255 175.765L43.2765 175.768L175.798 405.282L112.854 405.279L9.32197 225.972Z" stroke="#FF750F" stroke-width="1" stroke-linejoin="bevel"/>
<path d="M345.247 111.915C329.566 99.2919 312.229 89.5371 293.235 82.6512L235.167 183.228C254.161 190.114 271.498 199.869 287.179 212.492L345.247 111.915Z" stroke="#FF750F" stroke-width="1" stroke-linejoin="bevel"/>
<path d="M382.686 154.964C373.41 138.898 360.931 124.553 345.25 111.93L287.182 212.506C302.863 225.13 315.342 239.475 324.618 255.541L382.686 154.964Z" stroke="#FF750F" stroke-width="1" stroke-linejoin="bevel"/>
<path d="M293.243 82.6472C274.139 75.57 254.869 72.031 235.434 72.0303L177.366 172.607C196.801 172.608 216.071 176.147 235.175 183.224L293.243 82.6472Z" stroke="#FF750F" stroke-width="1" stroke-linejoin="bevel"/>
<path d="M394.118 194.257C395.112 182.973 391.301 169.872 382.688 154.953L324.619 255.53C333.233 270.448 337.044 283.55 336.05 294.834L394.118 194.257Z" stroke="#FF750F" stroke-width="1" stroke-linejoin="bevel"/>
<path d="M235.432 72.0311C216.88 72.0304 202.027 75.5681 190.875 82.6442L132.806 183.221C143.959 176.145 158.812 172.607 177.363 172.608L235.432 72.0311Z" stroke="#FF750F" stroke-width="1" stroke-linejoin="bevel"/>
<path d="M265.59 124.25C276.191 124.251 286.24 127.12 295.737 132.858L237.669 233.435C228.172 227.697 218.123 224.828 207.522 224.827L265.59 124.25Z" stroke="#FF750F" stroke-width="1" stroke-linejoin="bevel"/>
<path d="M295.719 132.859C305.326 138.406 312.78 145.77 318.081 154.95L260.013 255.527C254.712 246.347 247.258 238.983 237.651 233.436L295.719 132.859Z" stroke="#FF750F" stroke-width="1" stroke-linejoin="bevel"/>
<path d="M387.218 217.608C391.227 210.66 393.527 202.874 394.117 194.25L336.049 294.827C335.459 303.451 333.159 311.237 329.15 318.185L387.218 217.608Z" stroke="#FF750F" stroke-width="1" stroke-linejoin="bevel"/>
<path d="M245.211 132.577C248.413 127.03 255.204 124.257 265.584 124.258L207.516 224.835C197.136 224.834 190.345 227.607 187.143 233.154L245.211 132.577Z" stroke="#FF750F" stroke-width="1" stroke-linejoin="bevel"/>
<path d="M318.094 154.945C322.842 163.17 324.002 171.107 321.573 178.757L263.505 279.334C265.934 271.684 264.774 263.746 260.026 255.522L318.094 154.945Z" stroke="#FF750F" stroke-width="1" stroke-linejoin="bevel"/>
<path d="M176.925 96.6737C180.127 91.1249 184.776 86.4503 190.871 82.6499L132.803 183.227C126.708 187.027 122.059 191.702 118.857 197.25L176.925 96.6737Z" stroke="#FF750F" stroke-width="1" stroke-linejoin="bevel"/>
<path d="M387.226 217.606C385.989 219.749 384.59 221.813 383.028 223.797L324.96 324.373C326.522 322.39 327.921 320.326 329.157 318.183L387.226 217.606Z" stroke="#FF750F" stroke-width="1" stroke-linejoin="bevel"/>
<path d="M317.269 188.408C319.087 185.262 320.519 182.045 321.562 178.758L263.494 279.335C262.451 282.622 261.019 285.839 259.201 288.985L317.269 188.408Z" stroke="#FF750F" stroke-width="1" stroke-linejoin="bevel"/>
<path d="M245.208 132.573C241.895 137.928 243 145.387 248.522 154.95L190.454 255.527C184.932 245.964 183.827 238.505 187.14 233.15L245.208 132.573Z" stroke="#FF750F" stroke-width="1" stroke-linejoin="bevel"/>
<path d="M176.93 96.6719C174.331 101.175 172.686 106.255 171.993 111.91L113.925 212.487C114.618 206.831 116.263 201.752 118.862 197.249L176.93 96.6719Z" stroke="#FF750F" stroke-width="1" stroke-linejoin="bevel"/>
<path d="M317.266 188.413C314.853 192.589 311.757 196.64 307.978 200.566L249.91 301.143C253.689 297.216 256.785 293.166 259.198 288.99L317.266 188.413Z" stroke="#FF750F" stroke-width="1" stroke-linejoin="bevel"/>
<path d="M464.198 304.708L435.375 254.789L377.307 355.366L406.13 405.285L464.198 304.708Z" stroke="#FF750F" stroke-width="1" stroke-linejoin="bevel"/>
<path d="M353.209 254.787C366.68 242.548 376.618 232.22 383.023 223.805L324.955 324.382C318.55 332.797 308.612 343.124 295.141 355.364L353.209 254.787Z" stroke="#FF750F" stroke-width="1" stroke-linejoin="bevel"/>
<path d="M435.37 254.787L353.212 254.784L295.144 355.361L377.302 355.364L435.37 254.787Z" stroke="#FF750F" stroke-width="1" stroke-linejoin="bevel"/>
<path d="M183.921 154.947L248.521 154.95L190.453 255.527L125.853 255.524L183.921 154.947Z" stroke="#FF750F" stroke-width="1" stroke-linejoin="bevel"/>
<path d="M171.992 111.914C170.668 124.537 174.643 138.881 183.92 154.947L125.852 255.524C116.575 239.458 112.599 225.114 113.924 212.491L171.992 111.914Z" stroke="#FF750F" stroke-width="1" stroke-linejoin="bevel"/>
<path d="M307.987 200.562C301.251 207.256 291.203 216.244 277.842 227.528L219.774 328.105C233.135 316.821 243.183 307.832 249.919 301.139L307.987 200.562Z" stroke="#FF750F" stroke-width="1" stroke-linejoin="bevel"/>
<path d="M15.5469 75.1797L44.5359 125.386L-13.5321 225.963L-42.5212 175.756L15.5469 75.1797Z" stroke="#FF750F" stroke-width="1" stroke-linejoin="bevel"/>
<path d="M277.836 227.536C264.033 238.82 253.708 247.904 246.862 254.789L188.794 355.366C195.64 348.481 205.965 339.397 219.768 328.113L277.836 227.536Z" stroke="#FF750F" stroke-width="1" stroke-linejoin="bevel"/>
<path d="M275.358 304.706L464.189 304.713L406.12 405.29L217.29 405.283L275.358 304.706Z" stroke="#FF750F" stroke-width="1" stroke-linejoin="bevel"/>
<path d="M44.5279 125.39L67.3864 125.39L9.31834 225.967L-13.5401 225.966L44.5279 125.39Z" stroke="#FF750F" stroke-width="1" stroke-linejoin="bevel"/>
<path d="M101.341 75.1911L233.863 304.705L175.795 405.282L43.2733 175.768L101.341 75.1911ZM15.5431 75.19L-42.525 175.767L43.277 175.77L101.345 75.1932L15.5431 75.19Z" stroke="#FF750F" stroke-width="1" stroke-linejoin="bevel"/>
<path d="M246.866 254.784L246.534 254.784L188.466 355.361L188.798 355.361L246.866 254.784Z" stroke="#FF750F" stroke-width="1" stroke-linejoin="bevel"/>
<path d="M246.539 254.781L275.362 304.701L217.294 405.277L188.471 355.358L246.539 254.781Z" stroke="#FF750F" stroke-width="1" stroke-linejoin="bevel"/>
<path d="M67.3906 125.391L170.923 304.698L112.855 405.275L9.32257 225.967L67.3906 125.391Z" stroke="#FF750F" stroke-width="1" stroke-linejoin="bevel"/>
<path d="M170.921 304.699L233.865 304.701L175.797 405.278L112.853 405.276L170.921 304.699Z" stroke="#FF750F" stroke-width="1" stroke-linejoin="bevel"/>
</g>
<g class="transition-all delay-300 translate-y-0 opacity-100 duration-750 starting:opacity-0 starting:translate-y-4" style="mix-blend-mode:hard-light">
<path d="M246.544 254.79L246.875 254.79C253.722 247.905 264.046 238.82 277.849 227.537C291.21 216.253 301.259 207.264 307.995 200.57C314.62 193.685 319.147 186.418 321.577 178.768C324.006 171.117 322.846 163.18 318.097 154.956C312.796 145.775 305.342 138.412 295.735 132.865C286.238 127.127 276.189 124.258 265.588 124.257C255.208 124.257 248.416 127.03 245.214 132.576C241.902 137.931 243.006 145.39 248.528 154.953L183.928 154.951C174.652 138.885 170.676 124.541 172 111.918C173.546 99.2946 179.84 89.5408 190.882 82.6559C202.035 75.5798 216.887 72.0421 235.439 72.0428C254.874 72.0435 274.144 75.5825 293.248 82.6598C312.242 89.5457 329.579 99.3005 345.261 111.924C360.942 124.548 373.421 138.892 382.697 154.958C391.311 169.877 395.121 182.978 394.128 194.262C393.355 205.546 389.656 215.396 383.031 223.811C376.627 232.226 366.688 242.554 353.217 254.794L435.375 254.797L464.198 304.716L275.367 304.709L246.544 254.79Z" fill="#4B0600"/>
<path d="M246.544 254.79L246.875 254.79C253.722 247.905 264.046 238.82 277.849 227.537C291.21 216.253 301.259 207.264 307.995 200.57C314.62 193.685 319.147 186.418 321.577 178.768C324.006 171.117 322.846 163.18 318.097 154.956C312.796 145.775 305.342 138.412 295.735 132.865C286.238 127.127 276.189 124.258 265.588 124.257C255.208 124.257 248.416 127.03 245.214 132.576C241.902 137.931 243.006 145.39 248.528 154.953L183.928 154.951C174.652 138.885 170.676 124.541 172 111.918C173.546 99.2946 179.84 89.5408 190.882 82.6559C202.035 75.5798 216.887 72.0421 235.439 72.0428C254.874 72.0435 274.144 75.5825 293.248 82.6598C312.242 89.5457 329.579 99.3005 345.261 111.924C360.942 124.548 373.421 138.892 382.697 154.958C391.311 169.877 395.121 182.978 394.128 194.262C393.355 205.546 389.656 215.396 383.031 223.811C376.627 232.226 366.688 242.554 353.217 254.794L435.375 254.797L464.198 304.716L275.367 304.709L246.544 254.79Z" stroke="#FF750F" stroke-width="1" stroke-linejoin="round"/>
</g>
<g class="transition-all delay-300 translate-y-0 opacity-100 duration-750 starting:opacity-0 starting:translate-y-4" style="mix-blend-mode:hard-light">
<path d="M67.41 125.402L44.5515 125.401L15.5625 75.1953L101.364 75.1985L233.886 304.712L170.942 304.71L67.41 125.402Z" fill="#4B0600"/>
<path d="M67.41 125.402L44.5515 125.401L15.5625 75.1953L101.364 75.1985L233.886 304.712L170.942 304.71L67.41 125.402Z" stroke="#FF750F" stroke-width="1"/>
</g>
</svg>
<div class="absolute inset-0 rounded-t-lg lg:rounded-t-none lg:rounded-r-lg shadow-[inset_0px_0px_0px_1px_rgba(26,26,0,0.16)] dark:shadow-[inset_0px_0px_0px_1px_#fffaed2d]"></div>
</div>
</main>
</div>
@if (Route::has('login'))
<div class="h-14.5 hidden lg:block"></div>
@endif
</body>
</html>