feat: cache ai results on server + show success banner

This commit is contained in:
vas3k
2025-05-20 22:32:38 +02:00
parent c352f5eadd
commit f5c5bf75f6
11 changed files with 142 additions and 84 deletions

View File

@@ -2,22 +2,9 @@
import { bulkDeleteTransactionsAction } from "@/app/(app)/transactions/actions"
import { Button } from "@/components/ui/button"
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@/components/ui/dropdown-menu"
import { ChevronUp, Trash2 } from "lucide-react"
import { Trash2 } from "lucide-react"
import { useState } from "react"
const bulkActions = [
{
id: "delete",
label: "Bulk Delete",
icon: Trash2,
variant: "destructive" as const,
action: bulkDeleteTransactionsAction,
confirmMessage:
"Are you sure you want to delete these transactions and all their files? This action cannot be undone.",
},
]
interface BulkActionsMenuProps {
selectedIds: string[]
onActionComplete?: () => void
@@ -26,24 +13,21 @@ interface BulkActionsMenuProps {
export function BulkActionsMenu({ selectedIds, onActionComplete }: BulkActionsMenuProps) {
const [isLoading, setIsLoading] = useState(false)
const handleAction = async (actionId: string) => {
const action = bulkActions.find((a) => a.id === actionId)
if (!action) return
if (action.confirmMessage) {
if (!confirm(action.confirmMessage)) return
}
const handleDelete = async () => {
const confirmMessage =
"Are you sure you want to delete these transactions and all their files? This action cannot be undone."
if (!confirm(confirmMessage)) return
try {
setIsLoading(true)
const result = await action.action(selectedIds)
const result = await bulkDeleteTransactionsAction(selectedIds)
if (!result.success) {
throw new Error(result.error)
}
onActionComplete?.()
} catch (error) {
console.error(`Failed to execute bulk action ${actionId}:`, error)
alert(`Failed to execute action: ${error}`)
console.error("Failed to delete transactions:", error)
alert(`Failed to delete transactions: ${error}`)
} finally {
setIsLoading(false)
}
@@ -51,27 +35,10 @@ export function BulkActionsMenu({ selectedIds, onActionComplete }: BulkActionsMe
return (
<div className="fixed bottom-4 right-4 z-50">
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button className="min-w-48" disabled={isLoading}>
{selectedIds.length} transactions
<ChevronUp className="h-4 w-4" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
{bulkActions.map((action) => (
<DropdownMenuItem
key={action.id}
onClick={() => handleAction(action.id)}
className="gap-2"
disabled={isLoading}
>
<action.icon className="h-4 w-4" />
{action.label}
</DropdownMenuItem>
))}
</DropdownMenuContent>
</DropdownMenu>
<Button variant="destructive" className="min-w-48 gap-2" disabled={isLoading} onClick={handleDelete}>
<Trash2 className="h-4 w-4" />
Delete {selectedIds.length} transactions
</Button>
</div>
)
}

View File

@@ -10,7 +10,7 @@ import { FormInput, FormTextarea } from "@/components/forms/simple"
import { Button } from "@/components/ui/button"
import { Category, Currency, Field, Project, Transaction } from "@/prisma/client"
import { format } from "date-fns"
import { Loader2 } from "lucide-react"
import { Loader2, Save, Trash2 } from "lucide-react"
import { useRouter } from "next/navigation"
import { startTransition, useActionState, useEffect, useMemo, useState } from "react"
@@ -212,17 +212,23 @@ export default function TransactionEditForm({
<div className="flex justify-between space-x-4 pt-6">
<Button type="button" onClick={handleDelete} variant="destructive" disabled={isDeleting}>
{isDeleting ? "⏳ Deleting..." : "Delete "}
<>
<Trash2 className="h-4 w-4" />
{isDeleting ? "⏳ Deleting..." : "Delete "}
</>
</Button>
<Button type="submit" disabled={isSaving}>
{isSaving ? (
<>
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
<Loader2 className="h-4 w-4 animate-spin" />
Saving...
</>
) : (
"Save Transaction"
<>
<Save className="h-4 w-4" />
Save Transaction
</>
)}
</Button>