initial commit
This commit is contained in:
170
resources/views/invoices/create.blade.php
Normal file
170
resources/views/invoices/create.blade.php
Normal file
@@ -0,0 +1,170 @@
|
||||
@extends('layouts.app')
|
||||
|
||||
@section('title', 'New Invoice')
|
||||
@section('page-title', 'Create Invoice 🧾')
|
||||
@section('page-subtitle', 'Fill in the invoice details')
|
||||
|
||||
@section('content')
|
||||
<div class="max-w-3xl mx-auto">
|
||||
<div class="cat-card p-4 lg:p-8" x-data="invoiceForm()">
|
||||
<form action="{{ route('invoices.store') }}" method="POST">
|
||||
@csrf
|
||||
|
||||
{{-- 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 <span class="text-pink-400">*</span></label>
|
||||
<input type="text" name="title" value="{{ old('title') }}" class="input-soft" required
|
||||
placeholder="e.g. Instagram Collaboration - Brand X">
|
||||
</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 }}" {{ request('client') == $client->id ? 'selected' : '' }}>
|
||||
{{ $client->name }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-sm font-semibold text-gray-700 mb-1">Currency</label>
|
||||
<select name="currency" class="input-soft">
|
||||
<option value="IDR" selected>IDR (Rupiah)</option>
|
||||
<option value="USD">USD (Dollar)</option>
|
||||
<option value="SGD">SGD</option>
|
||||
</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', today()->toDateString()) }}"
|
||||
class="input-soft" required>
|
||||
</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', today()->addDays(14)->toDateString()) }}"
|
||||
class="input-soft" required>
|
||||
</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', 11) }}" 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">
|
||||
{{-- Description full width --}}
|
||||
<input type="text" :name="`items[${index}][description]`"
|
||||
x-model="item.description"
|
||||
class="input-soft text-sm mb-2 bg-white"
|
||||
placeholder="Service description" required>
|
||||
{{-- Qty / Price / Total in a row --}}
|
||||
<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"
|
||||
placeholder="0">
|
||||
</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>
|
||||
</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</span>
|
||||
<span class="font-semibold">Rp <span x-text="formatNum(subtotal)"></span></span>
|
||||
</div>
|
||||
<div class="flex justify-between text-sm py-1">
|
||||
<span class="text-gray-500">Tax (<span x-text="taxRate"></span>%)</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">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-3 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') }}"
|
||||
class="input-soft text-sm" placeholder="BCA, Mandiri...">
|
||||
</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') }}"
|
||||
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') }}"
|
||||
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"
|
||||
placeholder="Payment terms, etc.">{{ old('notes') }}</textarea>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col sm:flex-row gap-3">
|
||||
<button type="submit" class="btn-sakura flex-1 py-3">🧾 Create Invoice</button>
|
||||
<a href="{{ route('invoices.index') }}"
|
||||
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 invoiceForm() {
|
||||
return {
|
||||
items: [{ description: '', quantity: 1, unit_price: 0 }],
|
||||
taxRate: 11,
|
||||
get subtotal() { return this.items.reduce((s, i) => s + (i.quantity * i.unit_price), 0); },
|
||||
get taxAmount() { return Math.round(this.subtotal * this.taxRate / 100); },
|
||||
get total() { return this.subtotal + this.taxAmount; },
|
||||
addItem() { this.items.push({ description: '', quantity: 1, unit_price: 0 }); },
|
||||
removeItem(i) { if (this.items.length > 1) this.items.splice(i, 1); },
|
||||
recalc() {},
|
||||
formatNum(n) { return Number(n).toLocaleString('id-ID'); },
|
||||
};
|
||||
}
|
||||
</script>
|
||||
@endpush
|
||||
@endsection
|
||||
Reference in New Issue
Block a user