(squash) init

feat: filters, settings, backups

fix: ts compile errors

feat: new dashboard, webp previews and settings

feat: use webp for pdfs

feat: use webp

fix: analyze resets old data

fix: switch to corsproxy

fix: switch to free cors

fix: max upload limit

fix: currency conversion

feat: transaction export

fix: currency conversion

feat: refactor settings actions

feat: new loader

feat: README + LICENSE

doc: update readme

doc: update readme

doc: update readme

doc: update screenshots

ci: bump prisma
This commit is contained in:
Vasily Zubarev
2025-03-13 00:30:47 +01:00
commit 0b98a2c307
153 changed files with 17271 additions and 0 deletions

View File

@@ -0,0 +1,129 @@
"use client"
import { createTransactionAction } from "@/app/transactions/actions"
import { FormSelectCategory } from "@/components/forms/select-category"
import { FormSelectCurrency } from "@/components/forms/select-currency"
import { FormSelectProject } from "@/components/forms/select-project"
import { FormSelectType } from "@/components/forms/select-type"
import { FormInput, FormTextarea } from "@/components/forms/simple"
import { Button } from "@/components/ui/button"
import { Category, Currency, Project } from "@prisma/client"
import { format } from "date-fns"
import { Loader2 } from "lucide-react"
import { useRouter } from "next/navigation"
import { useActionState, useEffect, useState } from "react"
export default function TransactionCreateForm({
categories,
projects,
currencies,
settings,
}: {
categories: Category[]
projects: Project[]
currencies: Currency[]
settings: Record<string, string>
}) {
const router = useRouter()
const [createState, createAction, isCreating] = useActionState(createTransactionAction, null)
const [formData, setFormData] = useState({
name: "",
merchant: "",
description: "",
total: 0.0,
convertedTotal: 0.0,
currencyCode: settings.default_currency,
convertedCurrencyCode: settings.default_currency,
type: settings.default_type,
categoryCode: settings.default_category,
projectCode: settings.default_project,
issuedAt: format(new Date(), "yyyy-MM-dd"),
note: "",
})
useEffect(() => {
if (createState?.success) {
router.push(`/transactions/${createState.transactionId}`)
}
}, [createState, router])
return (
<form action={createAction} className="space-y-4">
<FormInput title="Name" name="name" defaultValue={formData.name} />
<FormInput title="Merchant" name="merchant" defaultValue={formData.merchant} />
<FormInput title="Description" name="description" defaultValue={formData.description} />
<div className="flex flex-row gap-4">
<FormInput title="Total" type="number" step="0.01" name="total" defaultValue={formData.total.toFixed(2)} />
<FormSelectCurrency
title="Currency"
name="currencyCode"
currencies={currencies}
placeholder="Select Currency"
value={formData.currencyCode}
onValueChange={(value) => {
setFormData({ ...formData, currencyCode: value })
}}
/>
<FormSelectType title="Type" name="type" defaultValue={formData.type} />
</div>
{formData.currencyCode !== settings.default_currency ? (
<div className="flex flex-row gap-4">
<FormInput
title={`Converted to ${settings.default_currency}`}
type="number"
step="0.01"
name="convertedTotal"
defaultValue={formData.convertedTotal.toFixed(2)}
/>
</div>
) : (
<></>
)}
<div className="flex flex-row flex-grow gap-4">
<FormInput title="Issued At" type="date" name="issuedAt" defaultValue={formData.issuedAt} />
</div>
<div className="flex flex-row gap-4">
<FormSelectCategory
title="Category"
categories={categories}
name="categoryCode"
defaultValue={formData.categoryCode}
placeholder="Select Category"
/>
<FormSelectProject
title="Project"
projects={projects}
name="projectCode"
defaultValue={formData.projectCode}
placeholder="Select Project"
/>
</div>
<FormTextarea title="Note" name="note" defaultValue={formData.note} />
<div className="flex justify-end space-x-4 pt-6">
<Button type="submit" disabled={isCreating}>
{isCreating ? (
<>
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
Creating...
</>
) : (
"Create and Add Files"
)}
</Button>
{createState?.error && <span className="text-red-500"> {createState.error}</span>}
</div>
</form>
)
}