barsa cdfad9d036 Enhance caching infrastructure and improve SIM management features
- Updated CacheModule and CacheService with detailed documentation and new methods for better cache management, including pattern deletion and memory usage tracking.
- Refactored CatalogCacheService and OrdersCacheService to utilize CDC-driven cache invalidation, improving data freshness and reducing unnecessary API calls.
- Introduced SIM plan options and updated related components to leverage new domain utilities for better plan management and user experience.
- Enhanced error handling and validation in TopUpModal for improved user feedback during SIM top-up operations.
- Removed obsolete plan formatting utilities to streamline codebase and improve maintainability.
2025-11-18 18:18:25 +09:00

68 lines
2.8 KiB
TypeScript

"use client";
import Link from "next/link";
import { CalendarDaysIcon, ChevronRightIcon } from "@heroicons/react/24/outline";
import { format, formatDistanceToNow } from "date-fns";
import { useFormatCurrency } from "@/lib/hooks/useFormatCurrency";
import type { NextInvoice } from "@customer-portal/domain/dashboard";
interface UpcomingPaymentBannerProps {
invoice: NextInvoice;
onPay?: (invoiceId: number) => void;
loading?: boolean;
}
export function UpcomingPaymentBanner({ invoice, onPay, loading }: UpcomingPaymentBannerProps) {
const { formatCurrency } = useFormatCurrency();
return (
<div id="attention" className="bg-white rounded-xl border border-orange-200 shadow-sm p-4">
<div className="flex items-center gap-4">
<div className="flex-shrink-0">
<div className="w-10 h-10 rounded-md bg-gradient-to-r from-amber-500 to-orange-500 flex items-center justify-center">
<CalendarDaysIcon className="h-5 w-5 text-white" />
</div>
</div>
<div className="flex-1 min-w-0">
<div className="flex flex-wrap items-center gap-2 text-sm text-gray-700">
<span className="font-semibold text-gray-900">Upcoming Payment</span>
<span className="text-gray-400"></span>
<span>Invoice #{invoice.id}</span>
<span className="text-gray-400"></span>
<span title={format(new Date(invoice.dueDate), "MMMM d, yyyy")}>
Due {formatDistanceToNow(new Date(invoice.dueDate), { addSuffix: true })}
</span>
</div>
<div className="mt-1 text-2xl font-bold text-gray-900">
{formatCurrency(invoice.amount, { currency: invoice.currency })}
</div>
<div className="mt-1 text-xs text-gray-500">
Exact due date: {format(new Date(invoice.dueDate), "MMMM d, yyyy")}
</div>
</div>
<div className="flex flex-col items-end gap-2">
{onPay && (
<button
onClick={() => onPay(invoice.id)}
disabled={loading}
className="inline-flex items-center px-4 py-2 border border-transparent text-sm font-medium rounded-md text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 transition-all disabled:opacity-50 disabled:cursor-not-allowed"
>
{loading ? "Opening Payment..." : "Pay Now"}
{!loading && <ChevronRightIcon className="ml-2 h-4 w-4" />}
</button>
)}
<Link
href={`/billing/invoices/${invoice.id}`}
className="text-blue-600 hover:text-blue-700 font-medium text-sm"
>
View invoice
</Link>
</div>
</div>
</div>
);
}
export type { UpcomingPaymentBannerProps };