Files
isletmem-kovakcrm/src/app/(dashboard)/invoices/[id]/components/header-actions.tsx
T
kovakmedya 1299cd10ce feat: fatura PDF, hizmet/yazılım atama dosya ekleri
- /print/invoices/[id] sayfası: A4 fatura yazdırma/PDF (AutoPrint + PrintActionBar)
- Fatura detayı header'ına PDF butonu eklendi (Yazdır yerine)
- Appwrite Storage: entity-attachments bucket (20MB, şifreli)
- Appwrite Tables: attachments collection (tenantId, entityType, entityId, fileId, name, size, mimeType)
- attachment-actions.ts: fetchAttachmentsAction, uploadAttachmentAction, deleteAttachmentAction
- AttachmentsPanel bileşeni: dosya yükleme/listeleme/silme, edit modunda görünür
- Hizmet ve yazılım atama form sheet'lerine AttachmentsPanel entegrasyonu
- /api/files/[attachmentId]: güvenli proxy indirme (tenant doğrulama + admin key ile Appwrite'a istek)
2026-05-07 20:22:17 +03:00

100 lines
2.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"use client";
import { useState, useTransition } from "react";
import { useRouter } from "next/navigation";
import { FileDown, Loader2, Pencil, Trash2 } from "lucide-react";
import { toast } from "sonner";
import { Button } from "@/components/ui/button";
import {
Dialog,
DialogContent,
DialogDescription,
DialogFooter,
DialogHeader,
DialogTitle,
} from "@/components/ui/dialog";
import { deleteInvoiceAction } from "@/lib/appwrite/invoice-actions";
import { InvoiceFormSheet } from "../../components/invoice-form-sheet";
import type { Customer, InvoiceRow } from "../../components/types";
type Props = { invoice: InvoiceRow; customers: Customer[] };
export function InvoiceHeaderActions({ invoice, customers }: Props) {
const router = useRouter();
const [editOpen, setEditOpen] = useState(false);
const [deleting, setDeleting] = useState(false);
const [busy, startTransition] = useTransition();
const handleDelete = () => {
startTransition(async () => {
const fd = new FormData();
fd.set("id", invoice.id);
const result = await deleteInvoiceAction(fd);
if (result.ok) {
toast.success("Fatura silindi.");
router.push("/invoices");
} else {
toast.error(result.error ?? "Silme başarısız.");
setDeleting(false);
}
});
};
return (
<>
<div className="flex flex-wrap gap-2">
<Button
variant="outline"
size="sm"
onClick={() => window.open(`/print/invoices/${invoice.id}`, "_blank")}
>
<FileDown className="size-3.5" />
PDF
</Button>
<Button variant="outline" size="sm" onClick={() => setEditOpen(true)}>
<Pencil className="size-3.5" />
Düzenle
</Button>
<Button
variant="ghost"
size="sm"
className="text-destructive hover:text-destructive"
onClick={() => setDeleting(true)}
>
<Trash2 className="size-3.5" />
Sil
</Button>
</div>
<InvoiceFormSheet
open={editOpen}
onOpenChange={setEditOpen}
invoice={invoice}
customers={customers}
/>
<Dialog open={deleting} onOpenChange={setDeleting}>
<DialogContent>
<DialogHeader>
<DialogTitle>Faturayı sil</DialogTitle>
<DialogDescription>
<strong>{invoice.number}</strong> ve tüm kalemleri silinecek. Bu işlem geri alınamaz.
</DialogDescription>
</DialogHeader>
<DialogFooter>
<Button variant="outline" onClick={() => setDeleting(false)} disabled={busy}>
Vazgeç
</Button>
<Button variant="destructive" onClick={handleDelete} disabled={busy}>
{busy ? <Loader2 className="size-4 animate-spin" /> : <Trash2 className="size-4" />}
Sil
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
</>
);
}