Files
Kol-Manager/resources/views/invoices/edit.blade.php
I-Fun 1b90f730a6 feat: add notes management feature with CRUD operations
- Created Note model and controller for handling notes.
- Added routes for notes CRUD operations.
- Implemented notes index view with search and pinning functionality.
- Created migration for notes table with necessary fields.
- Developed event creation form with client and contract options.
2026-04-02 23:24:38 +07:00

194 lines
11 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
@extends('layouts.app')
@section('title', 'Edit Invoice')
@section('page-title', 'Edit Invoice 🧾')
@section('page-subtitle', $invoice->invoice_number)
@section('content')
<div class="max-w-3xl mx-auto">
<div class="cat-card p-4 lg:p-8" x-data="invoiceEditForm()">
<form action="{{ route('invoices.update', $invoice) }}" method="POST">
@csrf @method('PUT')
{{-- Header fields --}}
<div class="grid grid-cols-1 sm:grid-cols-2 gap-3 mb-5">
<div class="sm:col-span-2">
<label class="block text-sm font-semibold text-gray-700 mb-1">Invoice Title</label>
<input type="text" name="title" value="{{ old('title', $invoice->title) }}" class="input-soft" required>
</div>
<div>
<label class="block text-sm font-semibold text-gray-700 mb-1">Client</label>
<select name="client_id" class="input-soft">
<option value="">— No client —</option>
@foreach($clients as $client)
<option value="{{ $client->id }}" {{ old('client_id', $invoice->client_id) == $client->id ? 'selected' : '' }}>{{ $client->name }}</option>
@endforeach
</select>
</div>
<div>
<label class="block text-sm font-semibold text-gray-700 mb-1">Status</label>
<select name="status" class="input-soft">
@foreach($statuses as $s)
<option value="{{ $s->value }}" {{ old('status', $invoice->status->value) === $s->value ? 'selected' : '' }}>{{ $s->label() }}</option>
@endforeach
</select>
</div>
<div>
<label class="block text-sm font-semibold text-gray-700 mb-1">Issue Date</label>
<input type="date" name="issue_date" value="{{ old('issue_date', $invoice->issue_date->toDateString()) }}" class="input-soft">
</div>
<div>
<label class="block text-sm font-semibold text-gray-700 mb-1">Due Date</label>
<input type="date" name="due_date" value="{{ old('due_date', $invoice->due_date->toDateString()) }}" class="input-soft">
</div>
<div>
<label class="block text-sm font-semibold text-gray-700 mb-1">Paid Date</label>
<input type="date" name="paid_date" value="{{ old('paid_date', $invoice->paid_date?->toDateString()) }}" class="input-soft">
</div>
<div>
<label class="block text-sm font-semibold text-gray-700 mb-1">Tax Rate (%)</label>
<input type="number" name="tax_rate" x-model="taxRate" @input="recalc()"
value="{{ old('tax_rate', $invoice->tax_rate) }}" class="input-soft" step="0.01">
</div>
</div>
{{-- Line Items --}}
<div class="mb-5">
<div class="flex items-center justify-between mb-3">
<h3 class="text-sm font-bold text-gray-700">Line Items</h3>
<button type="button" @click="addItem()" class="btn-sakura text-xs py-1.5 px-4">+ Add Item</button>
</div>
<div class="space-y-2">
<template x-for="(item, index) in items" :key="index">
<div class="bg-pink-50 rounded-2xl p-3">
<input type="text" :name="`items[${index}][description]`"
x-model="item.description"
class="input-soft text-sm mb-2 bg-white"
placeholder="Service description" required>
<div class="flex gap-2 items-center">
<div class="w-16 shrink-0">
<label class="text-xs text-gray-400 block mb-0.5">Qty</label>
<input type="number" :name="`items[${index}][quantity]`"
x-model.number="item.quantity" @input="recalc()"
class="input-soft text-sm text-center bg-white px-2" min="1">
</div>
<div class="flex-1">
<label class="text-xs text-gray-400 block mb-0.5">Unit Price</label>
<input type="number" :name="`items[${index}][unit_price]`"
x-model.number="item.unit_price" @input="recalc()"
class="input-soft text-sm bg-white" min="0" step="1000">
</div>
<div class="w-24 shrink-0 text-right">
<label class="text-xs text-gray-400 block mb-0.5">Total</label>
<div class="text-sm font-bold text-gray-700 pt-2">
Rp <span x-text="formatNum(item.quantity * item.unit_price)"></span>
</div>
</div>
<button type="button" @click="removeItem(index)"
class="text-red-400 hover:text-red-600 text-lg shrink-0 pt-4">✕</button>
</div>
{{-- Taxable checkbox --}}
<div class="mt-2 flex items-center gap-2">
<input type="hidden" :name="`items[${index}][is_taxable]`" value="0">
<input type="checkbox" :name="`items[${index}][is_taxable]`"
:id="`taxable_${index}`"
x-model="item.is_taxable" @change="recalc()"
value="1"
class="w-3.5 h-3.5 accent-pink-400">
<label :for="`taxable_${index}`" class="text-xs text-gray-500 cursor-pointer select-none">
Taxable
</label>
</div>
</div>
</template>
</div>
</div>
{{-- Totals --}}
<div class="bg-pink-50 rounded-2xl p-4 mb-5">
<div class="flex justify-between text-sm py-1">
<span class="text-gray-500">Subtotal (all items)</span>
<span class="font-semibold">Rp <span x-text="formatNum(subtotal)"></span></span>
</div>
<div class="flex justify-between text-sm py-1 text-amber-600">
<span>Tax (<span x-text="taxRate"></span>% — paid by client)</span>
<span class="font-semibold">Rp <span x-text="formatNum(taxAmount)"></span></span>
</div>
<div class="flex justify-between text-base font-bold py-2 mt-1 border-t border-pink-200">
<span class="text-gray-700">Grand Total</span>
<span class="text-pink-600">Rp <span x-text="formatNum(total)"></span></span>
</div>
</div>
{{-- Bank Info --}}
<div class="grid grid-cols-1 sm:grid-cols-2 gap-3 mb-5">
<div>
<label class="block text-xs font-semibold text-gray-600 mb-1">Bank Name</label>
<input type="text" name="bank_name" value="{{ old('bank_name', $invoice->bank_name) }}"
class="input-soft text-sm">
</div>
<div>
<label class="block text-xs font-semibold text-gray-600 mb-1">Branch Name</label>
<input type="text" name="branch_name" value="{{ old('branch_name', $invoice->branch_name) }}"
class="input-soft text-sm" placeholder="e.g. KCP Sudirman">
</div>
<div>
<label class="block text-xs font-semibold text-gray-600 mb-1">Account Number</label>
<input type="text" name="bank_account" value="{{ old('bank_account', $invoice->bank_account) }}"
class="input-soft text-sm">
</div>
<div>
<label class="block text-xs font-semibold text-gray-600 mb-1">Account Holder</label>
<input type="text" name="bank_holder" value="{{ old('bank_holder', $invoice->bank_holder) }}"
class="input-soft text-sm">
</div>
</div>
<div class="mb-5">
<label class="block text-sm font-semibold text-gray-700 mb-1">Notes</label>
<textarea name="notes" rows="2" class="input-soft resize-none">{{ old('notes', $invoice->notes) }}</textarea>
</div>
<div class="flex flex-col sm:flex-row gap-3">
<button type="submit" class="btn-sakura flex-1 py-3">💾 Save Changes</button>
<a href="{{ route('invoices.show', $invoice) }}"
class="flex-1 py-3 text-center rounded-full border border-pink-200 text-gray-600 hover:bg-pink-50 font-semibold text-sm">
Cancel
</a>
</div>
</form>
</div>
</div>
@push('scripts')
<script>
function invoiceEditForm() {
@php
$itemsJson = $invoice->items->map(function($i) {
return ['description' => $i->description, 'quantity' => $i->quantity, 'unit_price' => (float)$i->unit_price, 'is_taxable' => (bool)$i->is_taxable];
});
@endphp
return {
items: @json($itemsJson),
taxRate: {{ $invoice->tax_rate }},
get subtotal() {
return this.items.reduce((s, i) => s + (i.quantity * i.unit_price), 0);
},
get taxAmount() {
const taxable = this.items
.filter(i => i.is_taxable)
.reduce((s, i) => s + (i.quantity * i.unit_price), 0);
return Math.round(taxable * this.taxRate / 100);
},
get total() { return this.subtotal; },
addItem() { this.items.push({ description: '', quantity: 1, unit_price: 0, is_taxable: true }); },
removeItem(i) { if (this.items.length > 1) this.items.splice(i, 1); },
recalc() {},
formatNum(n) { return Number(n).toLocaleString('id-ID'); },
};
}
</script>
@endpush
@endsection