- Added InvoiceIndex component for displaying a list of invoices with pagination and search capabilities. - Created InvoiceShareButton component for sharing invoice details via WhatsApp. - Developed InvoicePrint component for printing invoice details. - Defined routes for invoice management including share and print functionalities. - Implemented InvoiceTest to cover authentication, authorization, and CRUD operations for invoices.
128 lines
3.7 KiB
TypeScript
128 lines
3.7 KiB
TypeScript
import { Share2 } from 'lucide-react';
|
|
import { useState } from 'react';
|
|
import { Button } from '@/components/ui/button';
|
|
import {
|
|
Popover,
|
|
PopoverContent,
|
|
PopoverTrigger,
|
|
} from '@/components/ui/popover';
|
|
import {
|
|
Tooltip,
|
|
TooltipContent,
|
|
TooltipTrigger,
|
|
} from '@/components/ui/tooltip';
|
|
import { share as invoiceShare } from '@/routes/admin/manage/invoices';
|
|
|
|
export type ShareableInvoice = {
|
|
id: number;
|
|
invoice_number: string;
|
|
customer_name: string;
|
|
formatted_amount: string;
|
|
formatted_date: string;
|
|
formatted_due_date?: string | null;
|
|
status_label: string;
|
|
};
|
|
|
|
function generateShareLink(invoiceId: number): string {
|
|
return window.location.origin + invoiceShare.url(invoiceId);
|
|
}
|
|
|
|
function generateWhatsappText(invoice: ShareableInvoice): string {
|
|
const shareLink = generateShareLink(invoice.id);
|
|
|
|
let text = `*Invoice ${invoice.invoice_number}*\n`;
|
|
text += `Kepada: ${invoice.customer_name}\n`;
|
|
text += `Tanggal: ${invoice.formatted_date}\n`;
|
|
|
|
if (invoice.formatted_due_date) {
|
|
text += `Jatuh Tempo: ${invoice.formatted_due_date}\n`;
|
|
}
|
|
|
|
text += `Status: ${invoice.status_label}\n`;
|
|
text += `Total: ${invoice.formatted_amount}\n`;
|
|
text += `\nLihat detail invoice:\n${shareLink}`;
|
|
|
|
return text;
|
|
}
|
|
|
|
function openWAWeb(text: string) {
|
|
const encoded = encodeURIComponent(text);
|
|
window.open(`https://wa.me/?text=${encoded}`, '_blank');
|
|
}
|
|
|
|
function openWAAndroid(text: string, pkg: string) {
|
|
const encoded = encodeURIComponent(text);
|
|
window.open(
|
|
`intent://send?text=${encoded}#Intent;scheme=whatsapp;package=${pkg};end`,
|
|
'_blank',
|
|
);
|
|
}
|
|
|
|
function shareViaWhatsAppMessenger(text: string) {
|
|
if (/android/i.test(navigator.userAgent)) {
|
|
openWAAndroid(text, 'com.whatsapp');
|
|
} else {
|
|
openWAWeb(text);
|
|
}
|
|
}
|
|
|
|
function shareViaWhatsAppBusiness(text: string) {
|
|
if (/android/i.test(navigator.userAgent)) {
|
|
openWAAndroid(text, 'com.whatsapp.w4b');
|
|
} else {
|
|
openWAWeb(text);
|
|
}
|
|
}
|
|
|
|
type Props = {
|
|
invoice: ShareableInvoice;
|
|
};
|
|
|
|
export function InvoiceShareButton({ invoice }: Props) {
|
|
const [shareOpen, setShareOpen] = useState(false);
|
|
const shareText = generateWhatsappText(invoice);
|
|
|
|
function handleShareWAMessenger() {
|
|
shareViaWhatsAppMessenger(shareText);
|
|
setShareOpen(false);
|
|
}
|
|
|
|
function handleShareWABusiness() {
|
|
shareViaWhatsAppBusiness(shareText);
|
|
setShareOpen(false);
|
|
}
|
|
|
|
return (
|
|
<Tooltip>
|
|
<Popover open={shareOpen} onOpenChange={setShareOpen}>
|
|
<TooltipTrigger asChild>
|
|
<PopoverTrigger asChild>
|
|
<Button variant="ghost" size="icon" className="h-8 w-8">
|
|
<Share2 className="h-4 w-4" />
|
|
</Button>
|
|
</PopoverTrigger>
|
|
</TooltipTrigger>
|
|
<TooltipContent>Bagikan ke WhatsApp</TooltipContent>
|
|
<PopoverContent align="end" className="w-48 space-y-1 p-2">
|
|
<Button
|
|
variant="ghost"
|
|
size="sm"
|
|
className="w-full justify-start gap-2"
|
|
onClick={handleShareWAMessenger}
|
|
>
|
|
WhatsApp Messenger
|
|
</Button>
|
|
<Button
|
|
variant="ghost"
|
|
size="sm"
|
|
className="w-full justify-start gap-2"
|
|
onClick={handleShareWABusiness}
|
|
>
|
|
WhatsApp Business
|
|
</Button>
|
|
</PopoverContent>
|
|
</Popover>
|
|
</Tooltip>
|
|
);
|
|
}
|