Skip to content

Commit c1fcffc

Browse files
authored
Merge pull request #1007 from PayButton/feat/invoice
Feat/invoice
2 parents 8a6cc55 + 6f61d12 commit c1fcffc

15 files changed

Lines changed: 878 additions & 4 deletions

File tree

assets/file-text.png

281 Bytes
Loading

assets/pencil.png

366 Bytes
Loading
Lines changed: 240 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,240 @@
1+
import React, { useState, useEffect, ReactElement } from 'react'
2+
import style from './transaction.module.css'
3+
import Button from 'components/Button'
4+
import { CreateInvoicePOSTParameters } from 'utils/validators'
5+
import axios from 'axios'
6+
import { Prisma } from '@prisma/client'
7+
8+
export interface InvoiceData {
9+
id?: string
10+
invoiceNumber: Prisma.Decimal
11+
amount: number
12+
recipientName: string
13+
recipientAddress: string
14+
description: string
15+
customerName: string
16+
customerAddress: string
17+
}
18+
19+
interface InvoiceModalProps {
20+
isOpen: boolean
21+
onClose: () => void
22+
transaction: any
23+
invoiceData: InvoiceData | null
24+
mode: 'create' | 'edit' | 'view'
25+
}
26+
27+
export default function InvoiceModal ({
28+
isOpen,
29+
onClose,
30+
invoiceData,
31+
transaction,
32+
mode
33+
}: InvoiceModalProps): ReactElement | null {
34+
const [formData, setFormData] = useState<InvoiceData>({
35+
invoiceNumber: '',
36+
amount: Number(transaction?.amount),
37+
recipientName: '',
38+
recipientAddress: transaction?.address?.address,
39+
description: '',
40+
customerName: '',
41+
customerAddress: ''
42+
})
43+
44+
useEffect(() => {
45+
setFormData(invoiceData ?? {
46+
invoiceNumber: '',
47+
amount: Number(transaction?.amount),
48+
recipientName: '',
49+
recipientAddress: transaction?.address?.address,
50+
description: '',
51+
customerName: '',
52+
customerAddress: ''
53+
})
54+
}, [transaction, mode, invoiceData])
55+
56+
if (!isOpen) return null
57+
58+
const handleChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>): void => {
59+
const { name, value } = e.target
60+
setFormData(prev => ({ ...prev, [name]: value }))
61+
}
62+
63+
const handleModalClose = (): void => {
64+
setFormData({
65+
invoiceNumber: '',
66+
amount: 0,
67+
recipientName: '',
68+
recipientAddress: '',
69+
description: '',
70+
customerName: '',
71+
customerAddress: ''
72+
})
73+
onClose()
74+
}
75+
76+
async function handleSubmit (e: React.FormEvent): Promise<void> {
77+
e.preventDefault()
78+
79+
if (mode === 'edit') {
80+
await updateInvoice()
81+
} else {
82+
await createInvoice()
83+
}
84+
onClose()
85+
}
86+
async function createInvoice (): Promise<void> {
87+
const payload: CreateInvoicePOSTParameters = {
88+
...formData,
89+
transactionId: transaction?.id
90+
}
91+
92+
try {
93+
await axios.post('/api/invoices', payload)
94+
} catch (err: any) {
95+
console.error('Invoice submission error:', err)
96+
}
97+
}
98+
99+
async function updateInvoice (): Promise<void> {
100+
const payload: CreateInvoicePOSTParameters = {
101+
...formData,
102+
transactionId: transaction?.id
103+
}
104+
105+
try {
106+
await axios.put(`/api/invoices/?invoiceId=${invoiceData?.id ?? ''}`, payload)
107+
onClose()
108+
} catch (err: any) {
109+
console.error('Invoice update error:', err)
110+
}
111+
}
112+
const isReadOnly = mode === 'view'
113+
114+
return (
115+
<div className={style.form_ctn_outer}>
116+
<div className={style.form_ctn_inner}>
117+
<h4>{mode === 'edit' ? 'Edit Invoice' : mode === 'view' ? 'View Invoice' : 'Create Invoice'}</h4>
118+
<div className={style.form_ctn}>
119+
{!isReadOnly
120+
? <form onSubmit={(e) => {
121+
void handleSubmit(e)
122+
}} method="post">
123+
<div style={{ display: 'flex', gap: '1rem' }}>
124+
<div style={{ display: 'flex', flexDirection: 'column', width: '100%' }}>
125+
<label htmlFor="invoiceNumber">Invoice Number</label>
126+
<input
127+
type="text"
128+
id="invoiceNumber"
129+
name="invoiceNumber"
130+
value={formData.invoiceNumber}
131+
onChange={handleChange}
132+
autoFocus
133+
/>
134+
</div>
135+
<div style={{ display: 'flex', flexDirection: 'column' }}>
136+
<label htmlFor="amount">Amount</label>
137+
<input
138+
type="number"
139+
id="amount"
140+
name="amount"
141+
value={formData.amount ?? ''}
142+
onChange={handleChange}
143+
disabled={true}
144+
/>
145+
</div>
146+
</div>
147+
148+
<label htmlFor="recipientName">Recipient Name</label>
149+
<input
150+
type="text"
151+
id="recipientName"
152+
name="recipientName"
153+
value={formData.recipientName}
154+
onChange={handleChange}
155+
/>
156+
157+
<label htmlFor="recipientAddress">Recipient Address</label>
158+
<input
159+
type="text"
160+
id="recipientAddress"
161+
name="recipientAddress"
162+
value={formData.recipientAddress}
163+
onChange={handleChange}
164+
disabled={true}
165+
/>
166+
167+
<label htmlFor="description">Description</label>
168+
<textarea
169+
id="description"
170+
name="description"
171+
value={formData.description}
172+
onChange={handleChange}
173+
></textarea>
174+
175+
<label htmlFor="customerName">Customer Name</label>
176+
<input
177+
type="text"
178+
id="customerName"
179+
name="customerName"
180+
value={formData.customerName}
181+
onChange={handleChange}
182+
disabled={isReadOnly}
183+
/>
184+
185+
<label htmlFor="customerAddress">Customer Address</label>
186+
<input
187+
type="text"
188+
id="customerAddress"
189+
name="customerAddress"
190+
value={formData.customerAddress}
191+
onChange={handleChange}
192+
disabled={isReadOnly}
193+
/>
194+
<div style={{ display: 'flex', justifyContent: 'space-between', marginTop: '10px' }}>
195+
<div className="mt-2">
196+
<Button type="button" onClick={handleModalClose}>
197+
{isReadOnly ? 'Close' : 'Cancel'}
198+
</Button>
199+
</div>
200+
{!isReadOnly && (
201+
<div className="mt-2">
202+
<Button type="submit">Submit</Button>
203+
</div>
204+
)}
205+
</div>
206+
</form>
207+
: <div>
208+
<div className={style.invoice_view}>
209+
<div className={style.invoice_view_item}>
210+
<strong>Invoice Number:</strong> {formData.invoiceNumber}
211+
</div>
212+
<div className={style.invoice_view_item}>
213+
<strong>Amount:</strong> {formData.amount}
214+
</div>
215+
<div className={style.invoice_view_item}>
216+
<strong>Recipient Name:</strong> {formData.recipientName}
217+
</div>
218+
<div className={style.invoice_view_item}>
219+
<strong>Recipient Address:</strong> {formData.recipientAddress}
220+
</div>
221+
<div className={style.invoice_view_item}>
222+
<strong>Description:</strong> {formData.description}
223+
</div>
224+
<div className={style.invoice_view_item}>
225+
<strong>Customer Name:</strong> {formData.customerName}
226+
</div>
227+
<div className={style.invoice_view_item}>
228+
<strong>Customer Address:</strong> {formData.customerAddress}
229+
</div>
230+
</div>
231+
<div style={{ marginTop: '20px', display: 'flex', justifyContent: 'flex-end' }}>
232+
<Button type="button" onClick={handleModalClose}>Close</Button>
233+
</div>
234+
</div>
235+
}
236+
</div>
237+
</div>
238+
</div>
239+
)
240+
}

0 commit comments

Comments
 (0)