Forms

Bilko Forms Documentation

Current State: Native HTML forms with React state Validation: Client-side JavaScript validation (no schema validation yet) Future State: Zod schemas for validation, react-hook-form for form management


Invoice Creation Wizard (6-Step Multi-Page Form)

Route: /invoices/new File: app/(dashboard)/invoices/new/page.tsx

Form Structure

Step 1: Customer Selection

Fields:

Add Customer Dialog:

Validation:


Step 2: Invoice Details

Fields:

Behavior:


Step 3: Line Items

Repeating Fields (Line Items):

Each line item contains:

Actions:

Totals Display (read-only):

Validation:


Step 4: Customization

Fields:

Behavior:


Step 5: Preview (Read-Only)

No form fields. Displays formatted invoice preview with all data from previous steps.

Preview Elements:

No validation. Step is purely visual review.


Step 6: Send/Save

Email Form:

Action Buttons:

Validation:


Form State Management

Local State:

const [step, setStep] = useState(1)
const [customer, setCustomer] = useState<Contact | null>(null)
const [showAddCustomer, setShowAddCustomer] = useState(false)
const [invoiceDetails, setInvoiceDetails] = useState<InvoiceDetails>({
  number: 'INV-2026-009',
  issueDate: '2026-02-20',
  dueDate: '2026-03-22',
  currency: 'EUR',
})
const [lineItems, setLineItems] = useState<LineItem[]>([
  { description: '', quantity: 1, unitPrice: 0, vatRate: 20, total: 0 },
])
const [notes, setNotes] = useState('Thank you for your business!')
const [terms, setTerms] = useState('Payment due within 30 days.')
const [emailData, setEmailData] = useState({
  to: '',
  subject: '',
  message: '',
  sendCopy: false,
})

No persistence: All state lost on page refresh or navigation away.

No Zod schemas: Validation is inline JavaScript (alert boxes).


Expense Form (Dialog)

Route: /expenses Component: Dialog triggered by "Add Expense" button

Form Fields

Form Actions

No API submission. Form data not persisted.

No Zod schemas. Validation is JavaScript logic in form submit handler.


Settings Forms

Route: /settings File: app/(dashboard)/settings/page.tsx

Company Profile Form

Fields:

Action:

Validation: None (no required fields enforced)


Tax & Compliance Form

Fields:

Compliance Reminders:

Action:

Validation: None


Notification Preferences

Email Notifications:

In-App Notifications:

Action:

Validation: None


Security Settings

Two-Factor Authentication:

Session Timeout:

Password Policy:

Actions:

Validation: None


Future Form Enhancements (Phase 2)

Zod Schema Validation

Planned: Replace inline validation with Zod schemas

Example (Invoice Wizard Step 1):

import { z } from 'zod'

const customerSchema = z.object({
  id: z.string(),
  name: z.string().min(1, 'Customer name required'),
  email: z.string().email('Valid email required'),
  phone: z.string().optional(),
  taxId: z.string().optional(),
})

Benefits:


react-hook-form Integration

Planned: Replace useState with react-hook-form

Example (Expense Form):

import { useForm } from 'react-hook-form'
import { zodResolver } from '@hookform/resolvers/zod'

const expenseSchema = z.object({
  amount: z.number().positive('Amount must be positive'),
  currency: z.enum(['EUR', 'RSD', 'BAM']),
  category: z.string().min(1, 'Category required'),
  date: z.string(),
  vendor: z.string().optional(),
  paymentMethod: z.string().optional(),
  description: z.string().optional(),
})

const {
  register,
  handleSubmit,
  formState: { errors },
} = useForm({
  resolver: zodResolver(expenseSchema),
})

Benefits:


Field-Level Validation

Planned: Real-time validation as user types

Example (Email Field):

<Input
  {...register("email")}
  type="email"
  error={errors.email?.message}
/>
{errors.email && (
  <span className="text-error text-sm">{errors.email.message}</span>
)}

Current State: No real-time validation, only on form submit.


Form Persistence

Planned: Save draft forms to localStorage

Use Cases:

Implementation:

// Save to localStorage on every state change
useEffect(() => {
  localStorage.setItem('invoice-draft', JSON.stringify(invoiceState))
}, [invoiceState])

// Load from localStorage on mount
useEffect(() => {
  const draft = localStorage.getItem('invoice-draft')
  if (draft) setInvoiceState(JSON.parse(draft))
}, [])

File Upload (Receipt Attachment)

Current State: Placeholder UI only (dashed border div)

Future Implementation:

API Endpoint (planned):

POST /api/expenses/:id/receipt
Content-Type: multipart/form-data

Autocomplete/Search Fields

Current State: Plain text inputs

Future Enhancement (Vendor Field):

Library: Radix UI Combobox or react-select


Multi-Currency Conversion

Current State: User manually selects currency

Future Enhancement:


Summary

Current Forms:

  1. Invoice Wizard (6-step) — Customer, Details, Line Items, Customization, Preview, Send
  2. Expense Form (dialog) — Amount, Category, Date, Vendor, Receipt, etc.
  3. Company Profile — All company settings
  4. Tax & Compliance — VAT settings
  5. Notification Preferences — Email/in-app notification toggles
  6. Security Settings — 2FA, session timeout, password policy

Validation:

State Management:

Future (Phase 2):


Revision #7
Created 2026-02-23 15:27:27 UTC by John
Updated 2026-06-21 20:02:18 UTC by John