3import React, { useState, useEffect } from "react";
4import { apiClient } from "@/lib/client/apiClient";
5import { fieldpineApi } from "@/lib/client/fieldpineApi";
7interface StatementAccount {
17interface StatementRun {
22 TotalCredits?: number;
24 accounts?: StatementAccount[];
28export default function ViewStatementsPage() {
29 const [statementRuns, setStatementRuns] = useState<StatementRun[]>([]);
30 const [loading, setLoading] = useState(true);
36 const loadStatementRuns = async () => {
39 const response = await fieldpineApi({
40 endpoint: "/buck?3=retailmax.elink.account.statementrun&201=1",
44 const runs: StatementRun[] = response.DATS.map((run: any) => ({
50 setStatementRuns(runs);
52 // Load account details for runs with fewer than 5000 accounts
53 runs.forEach((run, index) => {
54 if (run.Count < 5000) {
55 loadAccountDetails(run.Jobid, index);
60 console.error("Failed to load statement runs:", error);
66 const loadAccountDetails = async (jobId: number, runIndex: number) => {
68 setStatementRuns(prev => {
69 const updated = [...prev];
70 updated[runIndex] = { ...updated[runIndex], loading: true };
75 const response = await fieldpineApi({
76 endpoint: `/buck?3=retailmax.elink.account.statementrun&201=2&9=f106,0,${jobId}`,
80 setStatementRuns(prev => {
81 const updated = [...prev];
84 accounts: response.DATS,
91 console.error("Failed to load account details:", error);
92 setStatementRuns(prev => {
93 const updated = [...prev];
94 updated[runIndex] = { ...updated[runIndex], loading: false };
100 const viewPdf = (billingRun: number) => {
102 `/openapi/documentrequest?options=2&formatid=-108&output=pdf&billingrun=${billingRun}`,
107 const formatCurrency = (value: number) => {
108 return new Intl.NumberFormat("en-US", {
111 minimumFractionDigits: 2,
115 const formatDateTime = (dateStr: string) => {
116 if (!dateStr) return "";
117 return new Date(dateStr).toLocaleString("en-US", {
128 <div className="p-6 max-w-[1600px] mx-auto">
130 <div className="mb-6">
131 <h1 className="text-3xl font-bold text-text mb-2">Historic Statements</h1>
132 <p className="text-sm text-muted">
133 This screen reviews statements that have been issued. It only shows statements generated
134 using the web page option. Statements created directly in PosGreen cannot be shown here.
136 <p className="text-sm text-muted mt-2">
137 If you need bulk access to individual statements, these are stored in the folder
138 <code className="bg-surface-2 px-2 py-1 rounded text-xs mx-1">\fieldpine\pos\statements</code>
139 for direct access by external programs.
143 {/* Loading State */}
145 <div className="text-center py-12">
146 <div className="inline-block animate-spin rounded-full h-8 w-8 border-b-2 border-gray-900"></div>
147 <p className="mt-4 text-muted">Loading statement runs...</p>
151 {/* Statement Runs */}
152 {!loading && statementRuns.length === 0 && (
153 <div className="bg-surface rounded-lg shadow p-8 text-center">
154 <p className="text-muted">No statement runs found</p>
158 {!loading && statementRuns.length > 0 && (
159 <div className="space-y-8">
160 {statementRuns.map((run, index) => (
161 <div key={run.Jobid} className="bg-surface rounded-lg shadow">
163 <div className="border-b-2 border-border p-6">
164 <h2 className="text-2xl font-bold text-text mb-2">
165 {formatDateTime(run.MinDt)}
167 <p className="text-text">
168 Number of Accounts in Run: <span className="font-bold">{run.Count}</span>
172 {/* Account Details */}
173 <div className="p-6">
174 {run.Count >= 5000 ? (
175 <p className="text-sm text-muted italic">
176 Too many statements to show details. View Windows folder directly to see statements.
179 <div className="flex items-center justify-center py-8">
180 <div className="inline-block animate-spin rounded-full h-6 w-6 border-b-2 border-gray-900 mr-3"></div>
181 <span className="text-muted">Loading account details...</span>
183 ) : run.accounts && run.accounts.length > 0 ? (
184 <div className="overflow-x-auto">
185 <table className="w-full">
186 <thead className="bg-surface-2 border-b border-border">
188 <th className="px-4 py-3 text-left text-xs font-medium text-muted uppercase">
191 <th className="px-4 py-3 text-left text-xs font-medium text-muted uppercase">
194 <th className="px-4 py-3 text-left text-xs font-medium text-muted uppercase">
197 <th className="px-4 py-3 text-right text-xs font-medium text-muted uppercase">
200 <th className="px-4 py-3 text-right text-xs font-medium text-muted uppercase">
203 <th className="px-4 py-3 text-right text-xs font-medium text-muted uppercase">
206 <th className="px-4 py-3 text-center text-xs font-medium text-muted uppercase">
211 <tbody className="bg-surface divide-y divide-gray-200">
212 {run.accounts.map((account) => (
213 <tr key={`${run.Jobid}-${account.Accid}`} className="hover:bg-surface-2">
214 <td className="px-4 py-3 text-sm font-medium text-text">
217 <td className="px-4 py-3 text-sm text-text">
221 <span className="text-red-500">No Name</span>
224 <td className="px-4 py-3 text-sm text-muted">
225 {account.Terms || ""}
227 <td className="px-4 py-3 text-sm text-right text-text">
228 {formatCurrency(account.TotalDebits)}
230 <td className="px-4 py-3 text-sm text-right text-text">
231 {formatCurrency(account.TotalCredits)}
233 <td className="px-4 py-3 text-sm text-right font-medium text-text">
234 {formatCurrency(account.BalanceDue)}
236 <td className="px-4 py-3 text-center">
238 onClick={() => viewPdf(account.BillingRun)}
239 className="px-4 py-1.5 bg-[#00543b] text-white text-sm rounded hover:bg-[#003d2b] transition-colors"
250 <p className="text-sm text-muted">No accounts found in this run</p>