initial: standalone repo from monorepo split

This commit is contained in:
kevin-asprec
2026-04-13 09:36:37 +08:00
commit 3f5bf0e118
88 changed files with 10997 additions and 0 deletions

View File

@@ -0,0 +1,60 @@
'use client';
import { useState, useEffect } from 'react';
import { api } from '@/lib/api';
import { FormModal } from '@/components/ui/form-modal';
import { Button } from '@/components/ui/button';
import { useToast } from '@/components/ui/toast';
interface TransferModalProps {
open: boolean;
onClose: () => void;
onSuccess: () => void;
}
export function TransferModal({ open, onClose, onSuccess }: TransferModalProps) {
const { toast } = useToast();
const [accounts, setAccounts] = useState<any[]>([]);
const [form, setForm] = useState({ fromAccountId: '', toAccountId: '', amount: 0, description: '' });
const [submitting, setSubmitting] = useState(false);
const ic = 'mt-1 block w-full rounded-lg border border-surface-200 dark:border-surface-600 bg-white dark:bg-surface-800 px-3.5 py-2.5 text-sm text-surface-900 dark:text-surface-100 placeholder:text-surface-400 dark:placeholder:text-surface-500 focus:border-primary-500 dark:focus:border-primary-400 focus:outline-none focus:ring-2 focus:ring-primary-500/20 transition-all duration-200';
useEffect(() => {
if (open) {
setForm({ fromAccountId: '', toAccountId: '', amount: 0, description: '' });
api.get('/accounts').then((r) => setAccounts(r.data.data)).catch(() => {});
}
}, [open]);
const fromAccount = accounts.find((a: any) => a.id === form.fromAccountId);
async function handleSubmit(e: React.FormEvent) {
e.preventDefault(); setSubmitting(true);
try { await api.post('/accounts/transfer', { ...form, description: form.description || undefined }); toast('Transfer completed', 'success'); onSuccess(); onClose(); }
catch (err: any) { toast(err.response?.data?.error || 'Failed', 'error'); }
finally { setSubmitting(false); }
}
return (
<FormModal open={open} onClose={onClose} title="Transfer Funds" description="Move funds between company accounts. A journal entry will be created automatically.">
<form onSubmit={handleSubmit} className="space-y-4">
<div><label className="block text-sm font-medium text-surface-700 dark:text-surface-300">From Account</label>
<select required value={form.fromAccountId} onChange={(e) => setForm({ ...form, fromAccountId: e.target.value })} className={ic}>
<option value="">Select source...</option>
{accounts.map((a: any) => <option key={a.id} value={a.id}>{a.name} (PHP {Number(a.balance).toLocaleString()})</option>)}
</select></div>
<div><label className="block text-sm font-medium text-surface-700 dark:text-surface-300">To Account</label>
<select required value={form.toAccountId} onChange={(e) => setForm({ ...form, toAccountId: e.target.value })} className={ic}>
<option value="">Select destination...</option>
{accounts.filter((a: any) => a.id !== form.fromAccountId).map((a: any) => <option key={a.id} value={a.id}>{a.name}</option>)}
</select></div>
<div><label className="block text-sm font-medium text-surface-700 dark:text-surface-300">Amount (PHP) {fromAccount && <span className="text-surface-400 font-normal">Available: {Number(fromAccount.balance).toLocaleString()}</span>}</label>
<input type="number" required min={0.01} step={0.01} value={form.amount || ''} onChange={(e) => setForm({ ...form, amount: parseFloat(e.target.value) || 0 })} className={ic} /></div>
<div><label className="block text-sm font-medium text-surface-700 dark:text-surface-300">Description <span className="text-surface-400 font-normal">(optional)</span></label>
<input type="text" value={form.description} onChange={(e) => setForm({ ...form, description: e.target.value })} className={ic} placeholder="e.g. Weekly GCash to bank transfer" /></div>
<div className="flex justify-end gap-3 pt-2"><Button type="button" variant="secondary" onClick={onClose}>Cancel</Button>
<Button type="submit" loading={submitting} disabled={!form.fromAccountId || !form.toAccountId || form.amount <= 0}>Transfer</Button></div>
</form>
</FormModal>
);
}