-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
B 2740 create manual invoice panel (#653)
- Loading branch information
Showing
12 changed files
with
324 additions
and
17 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
45 changes: 45 additions & 0 deletions
45
shared/panels/_flows/request-payment-flow/_panels/_components/upload-file/upload-file.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
import { CloudUpload } from "lucide-react"; | ||
import { ChangeEvent, useRef } from "react"; | ||
|
||
import { Icon } from "@/design-system/atoms/icon"; | ||
import { Paper } from "@/design-system/atoms/paper"; | ||
import { Typo } from "@/design-system/atoms/typo"; | ||
import { toast } from "@/design-system/molecules/toaster"; | ||
|
||
import { UploadFileProps } from "@/shared/panels/_flows/request-payment-flow/_panels/_components/upload-file/upload-file.types"; | ||
import { Translate } from "@/shared/translation/components/translate/translate"; | ||
|
||
export function UploadFile({ setSelectedFile }: UploadFileProps) { | ||
const inputRef = useRef<HTMLInputElement | null>(null); | ||
|
||
function handleOnChange(event: ChangeEvent<HTMLInputElement>): void { | ||
if (event.target.files) { | ||
if (event.target.files[0].size > 3000000) { | ||
toast.error(<Translate token={"panels:uploadInvoice.errorMaxSizeFile"} />); | ||
} else { | ||
setSelectedFile(event.target.files[0]); | ||
} | ||
} | ||
} | ||
|
||
return ( | ||
<Paper | ||
classNames={{ | ||
base: "relative z-[0] flex flex-col items-center gap-lg border-border-primary border border-dashed !py-10", | ||
}} | ||
> | ||
<Icon component={CloudUpload} /> | ||
<div className="flex flex-col gap-1 text-center"> | ||
<Typo color="brand-secondary-alt" translate={{ token: "panels:uploadInvoice.clickToUpload" }} /> | ||
<Typo size="sm" color="secondary" translate={{ token: "panels:uploadInvoice.fileType" }} /> | ||
</div> | ||
<input | ||
type="file" | ||
ref={inputRef} | ||
onChange={handleOnChange} | ||
className="absolute h-full w-full cursor-pointer opacity-0" | ||
accept="application/pdf" | ||
/> | ||
</Paper> | ||
); | ||
} |
3 changes: 3 additions & 0 deletions
3
.../panels/_flows/request-payment-flow/_panels/_components/upload-file/upload-file.types.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
export interface UploadFileProps { | ||
setSelectedFile: (file: File) => void; | ||
} |
31 changes: 31 additions & 0 deletions
31
.../request-payment-flow/_panels/_components/uploaded-file-display/uploaded-file-display.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
import { FileMinus, Trash2 } from "lucide-react"; | ||
|
||
import { Avatar } from "@/design-system/atoms/avatar"; | ||
import { Button } from "@/design-system/atoms/button/variants/button-default"; | ||
import { Paper } from "@/design-system/atoms/paper"; | ||
import { Typo } from "@/design-system/atoms/typo"; | ||
|
||
import { UploadedFileDisplayProps } from "@/shared/panels/_flows/request-payment-flow/_panels/_components/uploaded-file-display/uploaded-file-display.types"; | ||
|
||
export function UploadedFileDisplay({ fileName, onRemoveFile }: UploadedFileDisplayProps) { | ||
return ( | ||
<Paper | ||
background={"primary-alt"} | ||
border={"primary"} | ||
classNames={{ base: "relative z-[0] flex flex-row items-center gap-4" }} | ||
> | ||
<Avatar shape="squared" size="lg" iconProps={{ component: FileMinus }} /> | ||
<Typo size="sm" weight="medium" classNames={{ base: "flex-1" }}> | ||
{fileName} | ||
</Typo> | ||
<Button | ||
variant="secondary" | ||
onClick={onRemoveFile} | ||
isDisabled={!fileName} | ||
iconOnly | ||
theme="destructive" | ||
startIcon={{ component: Trash2 }} | ||
/> | ||
</Paper> | ||
); | ||
} |
4 changes: 4 additions & 0 deletions
4
...est-payment-flow/_panels/_components/uploaded-file-display/uploaded-file-display.types.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
export interface UploadedFileDisplayProps { | ||
fileName: string; | ||
onRemoveFile: () => void; | ||
} |
38 changes: 38 additions & 0 deletions
38
...s/_flows/request-payment-flow/_panels/upload-invoice/_translations/upload-invoice.en.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
{ | ||
"guidelinesTitle": "Guidelines", | ||
"guidelineAll": { | ||
"subtitle": "Please, make sure your invoice includes the following:", | ||
"rule_1": "Total amount should be 1200 USDC excluding taxes", | ||
"rule_2": "Make sure to send your invoice in US Dollars ($)", | ||
"rule_3": "Apply taxes according to your local legislation" | ||
}, | ||
"guidelineSender": { | ||
"subtitle": "Sender", | ||
"rule_1": "First name, Last name", | ||
"rule_2": "10 Rue voltaire,75002, Paris, France" | ||
}, | ||
"guidelineRecipient": { | ||
"subtitle": "Recipient", | ||
"rule_1": "Wagmi", | ||
"rule_2": "60 rue François 1er, 75008 Paris, France" | ||
}, | ||
"sample_to_download": "We highly recommend downloading our invoice sample to help you create your own correctly.", | ||
"sample_link_label": "Download invoice sample", | ||
"clickToUpload": "Click to upload or drag and drop", | ||
"errorMaxSizeFile": "The file is too large, please upload a file smaller than 3MB", | ||
"fileType": "Only PDF files smaller than 3MB are accepted", | ||
"alert": { | ||
"title": "Find some guidlines on how to properly generate your invoice", | ||
"description": "We’ll have to reject it if it doesn’t match requirements." | ||
}, | ||
"submission": { | ||
"alert": { | ||
"title": "Please double check that everything is correct", | ||
"description": "Once approved it will trigger the payment process and can’t be edited." | ||
}, | ||
"error": { | ||
"title": "An error occurred", | ||
"description": "We were unable to generate the invoice, please try again later" | ||
} | ||
} | ||
} |
5 changes: 5 additions & 0 deletions
5
shared/panels/_flows/request-payment-flow/_panels/upload-invoice/upload-invoice.hooks.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
import { useSinglePanelContext } from "@/shared/features/side-panels/side-panel/side-panel"; | ||
|
||
export function useUploadInvoice() { | ||
return useSinglePanelContext("upload-invoice"); | ||
} |
185 changes: 185 additions & 0 deletions
185
shared/panels/_flows/request-payment-flow/_panels/upload-invoice/upload-invoice.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,185 @@ | ||
import { Download, Info } from "lucide-react"; | ||
import { useMemo, useState } from "react"; | ||
import { useTranslation } from "react-i18next"; | ||
|
||
import { Button } from "@/design-system/atoms/button/variants/button-default"; | ||
import { Icon } from "@/design-system/atoms/icon"; | ||
import { Paper } from "@/design-system/atoms/paper"; | ||
import { Spinner } from "@/design-system/atoms/spinner"; | ||
import { Typo } from "@/design-system/atoms/typo"; | ||
import { Alert } from "@/design-system/molecules/alert"; | ||
|
||
import { useInvoicePreview } from "@/shared/features/invoice/hooks/use-invoice-preview/use-invoice-preview"; | ||
import { useInvoiceUpload } from "@/shared/features/invoice/hooks/use-invoice-upload/use-invoice-upload"; | ||
import { SidePanelBody } from "@/shared/features/side-panels/side-panel-body/side-panel-body"; | ||
import { SidePanelFooter } from "@/shared/features/side-panels/side-panel-footer/side-panel-footer"; | ||
import { SidePanelHeader } from "@/shared/features/side-panels/side-panel-header/side-panel-header"; | ||
import { useSidePanel } from "@/shared/features/side-panels/side-panel/side-panel"; | ||
import { UploadFile } from "@/shared/panels/_flows/request-payment-flow/_panels/_components/upload-file/upload-file"; | ||
import { UploadedFileDisplay } from "@/shared/panels/_flows/request-payment-flow/_panels/_components/uploaded-file-display/uploaded-file-display"; | ||
import { useUploadInvoice } from "@/shared/panels/_flows/request-payment-flow/_panels/upload-invoice/upload-invoice.hooks"; | ||
import { useRequestPaymentFlow } from "@/shared/panels/_flows/request-payment-flow/request-payment-flow.context"; | ||
import { Translate } from "@/shared/translation/components/translate/translate"; | ||
|
||
function Content() { | ||
const [selectedFileBlob, setSelectedFileBlob] = useState<File>(); | ||
const { t } = useTranslation(); | ||
const { billingProfileId = "", rewardIds } = useRequestPaymentFlow(); | ||
|
||
const { | ||
isLoading: isLoadingInvoicePreview, | ||
fileUrl, | ||
invoiceId, | ||
} = useInvoicePreview({ | ||
rewardIds, | ||
billingProfileId, | ||
isSample: true, | ||
}); | ||
|
||
const { isPendingUploadInvoice, handleSendInvoice } = useInvoiceUpload({ | ||
billingProfileId, | ||
invoiceId, | ||
}); | ||
|
||
function removeFile() { | ||
setSelectedFileBlob(undefined); | ||
} | ||
|
||
const renderGuidelineAll = useMemo( | ||
() => ( | ||
<> | ||
<Typo size="xs" color="secondary" translate={{ token: "panels:uploadInvoice.guidelineAll.subtitle" }} /> | ||
<Typo size="xs" color="secondary"> | ||
<ul> | ||
{Array.from({ length: 3 }, (_, index) => { | ||
const token = `panels:uploadInvoice.guidelineAll.rule_${index + 1}`; | ||
return <li key={token}>{t(token)}</li>; | ||
})} | ||
</ul> | ||
</Typo> | ||
</> | ||
), | ||
[] | ||
); | ||
|
||
const renderGuidelineSender = useMemo( | ||
() => ( | ||
<> | ||
<Typo size="xs" color="secondary" translate={{ token: "panels:uploadInvoice.guidelineSender.subtitle" }} /> | ||
<Typo size="xs" color="secondary"> | ||
<ul> | ||
{Array.from({ length: 2 }, (_, index) => { | ||
const token = `panels:uploadInvoice.guidelineSender.rule_${index + 1}`; | ||
return <li key={token}>{t(token)}</li>; | ||
})} | ||
</ul> | ||
</Typo> | ||
</> | ||
), | ||
[] | ||
); | ||
|
||
const renderGuidelineRecipient = useMemo( | ||
() => ( | ||
<> | ||
<Typo size="xs" color="secondary" translate={{ token: "panels:uploadInvoice.guidelineRecipient.subtitle" }} /> | ||
<Typo size="xs" color="secondary"> | ||
<ul> | ||
{Array.from({ length: 2 }, (_, index) => { | ||
const token = `panels:uploadInvoice.guidelineRecipient.rule_${index + 1}`; | ||
return <li key={token}>{t(token)}</li>; | ||
})} | ||
</ul> | ||
</Typo> | ||
</> | ||
), | ||
[] | ||
); | ||
|
||
function renderUploadFile() { | ||
if (selectedFileBlob) { | ||
return <UploadedFileDisplay fileName={selectedFileBlob.name} onRemoveFile={removeFile} />; | ||
} | ||
|
||
return <UploadFile setSelectedFile={setSelectedFileBlob} />; | ||
} | ||
|
||
return ( | ||
<> | ||
<SidePanelHeader | ||
title={{ | ||
translate: { | ||
// TODO update title | ||
token: "panels:singleContributionSelection.title", | ||
}, | ||
}} | ||
canClose | ||
/> | ||
|
||
<SidePanelBody> | ||
<Alert | ||
color="grey" | ||
title={<Translate token="panels:uploadInvoice.alert.title" />} | ||
description={<Translate token="panels:uploadInvoice.alert.description" />} | ||
icon={{ component: Info }} | ||
/> | ||
<Paper | ||
background={"primary-alt"} | ||
border={"primary"} | ||
classNames={{ base: "prose leading-normal flex flex-col gap-lg" }} | ||
> | ||
<Typo as={"div"} size="sm" weight="medium" translate={{ token: "panels:uploadInvoice.guidelinesTitle" }} /> | ||
<div> | ||
{renderGuidelineAll} | ||
{renderGuidelineSender} | ||
{renderGuidelineRecipient} | ||
</div> | ||
<Typo size="xs" color="secondary" translate={{ token: "panels:uploadInvoice.sample_to_download" }} /> | ||
</Paper> | ||
{renderUploadFile()} | ||
</SidePanelBody> | ||
<SidePanelFooter> | ||
<div className="flex w-full items-center justify-between"> | ||
{isLoadingInvoicePreview ? <Spinner /> : null} | ||
{fileUrl ? ( | ||
<a | ||
className="flex cursor-pointer items-center gap-md rounded-md border border-border-primary px-lg py-md effect-box-shadow-xs" | ||
href={fileUrl} | ||
download="invoice-sample.pdf" | ||
> | ||
<Icon component={Download} size="sm" /> | ||
<Typo size="sm" color="secondary" translate={{ token: "panels:uploadInvoice.sample_link_label" }} /> | ||
</a> | ||
) : ( | ||
<div /> | ||
)} | ||
<Button | ||
variant={"secondary"} | ||
size={"md"} | ||
onClick={() => | ||
handleSendInvoice({ | ||
fileBlob: selectedFileBlob as Blob, | ||
isManualUpload: true, | ||
fileName: selectedFileBlob?.name, | ||
}) | ||
} | ||
isDisabled={!selectedFileBlob} | ||
isLoading={isPendingUploadInvoice} | ||
translate={{ token: "common:form.send" }} | ||
/> | ||
</div> | ||
</SidePanelFooter> | ||
</> | ||
); | ||
} | ||
|
||
export function UploadInvoice() { | ||
const { name } = useUploadInvoice(); | ||
const { Panel } = useSidePanel({ name }); | ||
|
||
return ( | ||
<Panel> | ||
<Content /> | ||
</Panel> | ||
); | ||
} |
1 change: 1 addition & 0 deletions
1
shared/panels/_flows/request-payment-flow/_panels/upload-invoice/upload-invoice.types.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
export interface UploadInvoiceProps {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters