Skip to content

Commit f5c2f19

Browse files
committed
chore: ttl form
1 parent 2273d0c commit f5c2f19

File tree

2 files changed

+163
-69
lines changed

2 files changed

+163
-69
lines changed

src/components/GrossToNetDiscountForm.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,13 @@ const INPUT_DAYS_MAX_VALUE = 360
6060

6161
type GrossToNetFormValues = FormValues
6262

63-
const GrossToNetDiscountForm = ({ startDate, endDate, gross, onSubmit, submitButtonText = "Submit" }: GrossToNetProps) => {
63+
const GrossToNetDiscountForm = ({
64+
startDate,
65+
endDate,
66+
gross,
67+
onSubmit,
68+
submitButtonText = "Submit",
69+
}: GrossToNetProps) => {
6470
const {
6571
watch,
6672
register,

src/pages/quotes/QuotePage.tsx

Lines changed: 156 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import { BaseDrawer, ConfirmDrawer } from "@/components/Drawers"
2525
import { GrossToNetDiscountForm } from "@/components/GrossToNetDiscountForm"
2626
import Big from "big.js"
2727
import { toast } from "sonner"
28+
import { useForm } from "react-hook-form"
2829

2930
function Loader() {
3031
return (
@@ -34,8 +35,78 @@ function Loader() {
3435
)
3536
}
3637

38+
interface TimeToLiveFormValues {
39+
ttl?: Date
40+
}
41+
42+
interface TimeToLiveFormResult {
43+
ttl: Date
44+
}
45+
46+
interface TimeToLiveFormProps {
47+
submitButtonText?: string
48+
onSubmit: (values: TimeToLiveFormResult) => void
49+
}
50+
51+
const TimeToLiveForm = ({ onSubmit, submitButtonText = "Submit" }: TimeToLiveFormProps) => {
52+
const {
53+
watch,
54+
register,
55+
handleSubmit,
56+
formState: { isValid, errors },
57+
} = useForm<TimeToLiveFormValues>({
58+
mode: "all",
59+
})
60+
61+
const { ttl } = watch()
62+
63+
return (
64+
<form
65+
className="flex flex-col gap-2 min-w-[8rem]"
66+
onSubmit={(e) => {
67+
handleSubmit(() => {
68+
if (errors.root !== undefined || ttl === undefined) return
69+
70+
onSubmit({
71+
ttl,
72+
})
73+
})(e).catch(() => {
74+
// TODO
75+
})
76+
}}
77+
>
78+
<div className="flex flex-col">
79+
<div
80+
className={cn(
81+
"flex gap-2 justify-between items-center font-semibold",
82+
"peer flex h-[58px] w-full rounded-[8px] border bg-elevation-200 px-4 text-sm transition-all duration-200 ease-in-out outline-none focus:outline-none",
83+
"file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:ring-0",
84+
)}
85+
>
86+
<label htmlFor={"ttl"}>Valid until</label>
87+
88+
<input
89+
id="ttl"
90+
step="1"
91+
type="number"
92+
className="bg-transparent text-right focus:outline-none [appearance:textfield] [&::-webkit-outer-spin-button]:appearance-none [&::-webkit-inner-spin-button]:appearance-none"
93+
{...register("ttl", {
94+
required: true,
95+
})}
96+
/>
97+
</div>
98+
</div>
99+
100+
<Button type="submit" size="sm" className="my-[16px]" disabled={!isValid}>
101+
{submitButtonText}
102+
</Button>
103+
</form>
104+
)
105+
}
106+
37107
interface OfferFormResult {
38108
discount: Parameters<Parameters<typeof GrossToNetDiscountForm>[0]["onSubmit"]>[0]
109+
ttl: Parameters<Parameters<typeof TimeToLiveForm>[0]["onSubmit"]>[0]
39110
}
40111

41112
interface OfferFormProps {
@@ -44,16 +115,32 @@ interface OfferFormProps {
44115
}
45116

46117
function OfferForm({ onSubmit, discount }: OfferFormProps) {
118+
const [discountResult, setDiscountResult] = useState<OfferFormResult["discount"]>()
47119
return (
48120
<>
49-
<GrossToNetDiscountForm
50-
{...discount}
51-
startDate={discount.startDate ?? new Date(Date.now())}
52-
onSubmit={(values) => onSubmit({
53-
discount: values
54-
})}
55-
submitButtonText="Next"
56-
/>
121+
{!discountResult ? (
122+
<>
123+
<GrossToNetDiscountForm
124+
{...discount}
125+
startDate={discount.startDate ?? new Date(Date.now())}
126+
onSubmit={setDiscountResult}
127+
submitButtonText="Next"
128+
/>
129+
</>
130+
) : (
131+
<>
132+
<TimeToLiveForm
133+
{...discount}
134+
onSubmit={(ttlResult) =>
135+
onSubmit({
136+
discount: discountResult,
137+
ttl: ttlResult,
138+
})
139+
}
140+
submitButtonText="Next"
141+
/>
142+
</>
143+
)}
57144
</>
58145
)
59146
}
@@ -203,67 +290,68 @@ function QuoteActions({ value, isFetching }: { value: InfoReply; isFetching: boo
203290
}
204291

205292
return (
206-
<div className="flex items-center gap-2">
207-
<DenyConfirmDrawer
208-
title="Confirm denying quote"
209-
open={denyConfirmDrawerOpen}
210-
onOpenChange={setDenyConfirmDrawerOpen}
211-
onSubmit={() => {
212-
onDenyQuote()
213-
setDenyConfirmDrawerOpen(false)
214-
}}
215-
>
216-
<Button
217-
className="flex-1"
218-
disabled={isFetching || denyQuote.isPending || value.status !== "pending"}
219-
variant={value.status !== "pending" ? "outline" : "destructive"}
220-
>
221-
Deny {denyQuote.isPending && <LoaderIcon className="stroke-1 animate-spin" />}
222-
</Button>
223-
</DenyConfirmDrawer>
224-
<OfferFormDrawer
225-
title="Offer quote"
226-
description="Make an offer to the current holder of this bill"
227-
value={value}
228-
open={offerFormDrawerOpen}
229-
onOpenChange={setOfferFormDrawerOpen}
230-
onSubmit={(data) => {
231-
setOfferFormData(data)
232-
setOfferConfirmDrawerOpen(true)
233-
setOfferFormDrawerOpen(false)
234-
}}
235-
>
236-
<Button className="flex-1" disabled={isFetching || offerQuote.isPending || value.status !== "pending"}>
237-
Offer {offerQuote.isPending && <LoaderIcon className="stroke-1 animate-spin" />}
238-
</Button>
239-
</OfferFormDrawer>
240-
<OfferConfirmDrawer
241-
title="Confirm offering quote"
242-
description="Review your inputs and confirm the offer"
243-
open={offerConfirmDrawerOpen}
244-
onOpenChange={setOfferConfirmDrawerOpen}
245-
onSubmit={() => {
246-
if (!offerFormData) return
247-
onOfferQuote(offerFormData)
248-
setOfferConfirmDrawerOpen(false)
249-
}}
293+
<div className="flex items-center gap-2">
294+
<DenyConfirmDrawer
295+
title="Confirm denying quote"
296+
open={denyConfirmDrawerOpen}
297+
onOpenChange={setDenyConfirmDrawerOpen}
298+
onSubmit={() => {
299+
onDenyQuote()
300+
setDenyConfirmDrawerOpen(false)
301+
}}
302+
>
303+
<Button
304+
className="flex-1"
305+
disabled={isFetching || denyQuote.isPending || value.status !== "pending"}
306+
variant={value.status !== "pending" ? "outline" : "destructive"}
250307
>
251-
<div className="flex flex-col justify-center gap-1 py-8 mb-8">
252-
<span>
253-
<span className="font-bold">Effective discount (relative):</span>{" "}
254-
{effectiveDiscount?.mul(new Big("100")).toFixed(2)}%
255-
</span>
256-
<span>
257-
<span className="font-bold">Effective discount (absolute):</span>{" "}
258-
{offerFormData?.discount.gross.value.minus(offerFormData?.discount.net.value).toFixed(0)} {offerFormData?.discount.net.currency}
259-
</span>
260-
<span>
261-
<span className="font-bold">Net amount:</span> {offerFormData?.discount.net.value.round(0).toFixed(0)}{" "}
262-
{offerFormData?.discount.net.currency}
263-
</span>
264-
</div>
265-
</OfferConfirmDrawer>
266-
</div>
308+
Deny {denyQuote.isPending && <LoaderIcon className="stroke-1 animate-spin" />}
309+
</Button>
310+
</DenyConfirmDrawer>
311+
<OfferFormDrawer
312+
title="Offer quote"
313+
description="Make an offer to the current holder of this bill"
314+
value={value}
315+
open={offerFormDrawerOpen}
316+
onOpenChange={setOfferFormDrawerOpen}
317+
onSubmit={(data) => {
318+
setOfferFormData(data)
319+
setOfferConfirmDrawerOpen(true)
320+
setOfferFormDrawerOpen(false)
321+
}}
322+
>
323+
<Button className="flex-1" disabled={isFetching || offerQuote.isPending || value.status !== "pending"}>
324+
Offer {offerQuote.isPending && <LoaderIcon className="stroke-1 animate-spin" />}
325+
</Button>
326+
</OfferFormDrawer>
327+
<OfferConfirmDrawer
328+
title="Confirm offering quote"
329+
description="Review your inputs and confirm the offer"
330+
open={offerConfirmDrawerOpen}
331+
onOpenChange={setOfferConfirmDrawerOpen}
332+
onSubmit={() => {
333+
if (!offerFormData) return
334+
onOfferQuote(offerFormData)
335+
setOfferConfirmDrawerOpen(false)
336+
}}
337+
>
338+
<div className="flex flex-col justify-center gap-1 py-8 mb-8">
339+
<span>
340+
<span className="font-bold">Effective discount (relative):</span>{" "}
341+
{effectiveDiscount?.mul(new Big("100")).toFixed(2)}%
342+
</span>
343+
<span>
344+
<span className="font-bold">Effective discount (absolute):</span>{" "}
345+
{offerFormData?.discount.gross.value.minus(offerFormData?.discount.net.value).toFixed(0)}{" "}
346+
{offerFormData?.discount.net.currency}
347+
</span>
348+
<span>
349+
<span className="font-bold">Net amount:</span> {offerFormData?.discount.net.value.round(0).toFixed(0)}{" "}
350+
{offerFormData?.discount.net.currency}
351+
</span>
352+
</div>
353+
</OfferConfirmDrawer>
354+
</div>
267355
)
268356
}
269357

0 commit comments

Comments
 (0)