| svelte | |
| <script> | |
| import { createEventDispatcher } from 'svelte'; | |
| import { LucideSave, LucideX } from 'lucide-svelte'; | |
| const dispatch = createEventDispatcher(); | |
| export let transaction = { | |
| date: new Date().toISOString().split('T')[0], | |
| type: 'income', | |
| description: '', | |
| category: '', | |
| amount: 0, | |
| paymentMethod: 'cash' | |
| }; | |
| export let categories = []; | |
| const paymentMethods = [ | |
| { value: 'cash', label: 'Cash' }, | |
| { value: 'bank', label: 'Bank Transfer' }, | |
| { value: 'card', label: 'Credit Card' }, | |
| { value: 'ewallet', label: 'E-Wallet' } | |
| ]; | |
| function handleSubmit() { | |
| if (!transaction.amount || transaction.amount <= 0) return; | |
| if (!transaction.category) return; | |
| dispatch('submit', transaction); | |
| } | |
| </script> | |
| <form on:submit|preventDefault={handleSubmit} class="space-y-4"> | |
| <div class="grid grid-cols-1 md:grid-cols-2 gap-4"> | |
| <div> | |
| <label for="date" class="block text-sm font-medium text-slate-700 mb-1">Date</label> | |
| <input | |
| id="date" | |
| type="date" | |
| bind:value={transaction.date} | |
| class="input-field" | |
| required | |
| /> | |
| </div> | |
| <div> | |
| <label for="type" class="block text-sm font-medium text-slate-700 mb-1">Type</label> | |
| <select | |
| id="type" | |
| bind:value={transaction.type} | |
| class="input-field" | |
| > | |
| <option value="income">Income</option> | |
| <option value="expense">Expense</option> | |
| </select> | |
| </div> | |
| </div> | |
| <div> | |
| <label for="description" class="block text-sm font-medium text-slate-700 mb-1">Description</label> | |
| <input | |
| id="description" | |
| type="text" | |
| bind:value={transaction.description} | |
| class="input-field" | |
| placeholder="Repair iPhone screen, Buy spare parts..." | |
| /> | |
| </div> | |
| <div class="grid grid-cols-1 md:grid-cols-2 gap-4"> | |
| <div> | |
| <label for="category" class="block text-sm font-medium text-slate-700 mb-1">Category</label> | |
| <select | |
| id="category" | |
| bind:value={transaction.category} | |
| class="input-field" | |
| required | |
| > | |
| <option value="">Select a category</option> | |
| {#each categories as category} | |
| <option value={category}>{category}</option> | |
| {/each} | |
| </select> | |
| </div> | |
| <div> | |
| <label for="amount" class="block text-sm font-medium text-slate-700 mb-1">Amount</label> | |
| <div class="relative"> | |
| <span class="absolute left-3 top-1/2 -translate-y-1/2 text-slate-500">Rp</span> | |
| <input | |
| id="amount" | |
| type="number" | |
| bind:value={transaction.amount} | |
| class="input-field pl-10" | |
| min="0" | |
| step="1000" | |
| required | |
| /> | |
| </div> | |
| </div> | |
| </div> | |
| <div> | |
| <label for="paymentMethod" class="block text-sm font-medium text-slate-700 mb-1">Payment Method</label> | |
| <select | |
| id="paymentMethod" | |
| bind:value={transaction.paymentMethod} | |
| class="input-field" | |
| > | |
| {#each paymentMethods as method} | |
| <option value={method.value}>{method.label}</option> | |
| {/each} | |
| </select> | |
| </div> | |
| <div class="flex justify-end space-x-3 pt-4"> | |
| <button | |
| type="button" | |
| on:click={() => dispatch('cancel')} | |
| class="btn-secondary flex items-center space-x-1" | |
| > | |
| <LucideX size={18} /> | |
| <span>Cancel</span> | |
| </button> | |
| <button | |
| type="submit" | |
| class="btn-primary flex items-center space-x-1" | |
| > | |
| <LucideSave size={18} /> | |
| <span>Save</span> | |
| </button> | |
| </div> | |
| </form> | |
| </html> |