Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(feat) Credit note export & filtering #1930

Merged
merged 28 commits into from
Dec 23, 2024
Merged
Show file tree
Hide file tree
Changes from 25 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
1a90f57
feat(filters): create filter item for invoice numbers
stephenlago99 Dec 21, 2024
64b7e7e
feat(filters): create filter item for credit note refund status
stephenlago99 Dec 21, 2024
2c530ce
feat(filters): create filter item for credit note reason
stephenlago99 Dec 21, 2024
7dd1927
feat(filters): create filter item for credit note credit status
stephenlago99 Dec 21, 2024
84a8ce1
feat(filters): create filter item for amount
stephenlago99 Dec 21, 2024
8089423
feat(exports): create reusable ExportDialog
stephenlago99 Dec 21, 2024
e9304d2
delete(exports): remove old dialog for exporting invoices
stephenlago99 Dec 21, 2024
6b60a2f
refactor(CreateCreditNote): extract reason map
stephenlago99 Dec 21, 2024
521d201
refactor(InvoicesPage): include new export dialog
stephenlago99 Dec 21, 2024
0270ee7
refactor(filters): abstract filter logic
stephenlago99 Dec 21, 2024
7f10fb0
refactor(filters): include credit note filters
stephenlago99 Dec 21, 2024
b680a5c
refactor(filters): refactor filters panel switch
stephenlago99 Dec 21, 2024
1e600d4
feat(CreditNotesTable): add filters
stephenlago99 Dec 21, 2024
4a6c1dc
chore(graphql): generate types
stephenlago99 Dec 21, 2024
4706c3c
chore(translations): add translations
stephenlago99 Dec 21, 2024
7b935fb
fix: linting
stephenlago99 Dec 21, 2024
4202127
feat(filters): extract parse amount util, amounts as ints, translatio…
stephenlago99 Dec 23, 2024
f1eca19
feat(filters): extract AmountFilterInterval type
stephenlago99 Dec 23, 2024
09791ac
feat(filters): update amount filter values
stephenlago99 Dec 23, 2024
608136d
fix(filters): flatten object filter values
stephenlago99 Dec 23, 2024
f94c768
chore(graphql): generate types
stephenlago99 Dec 23, 2024
596ca2b
feat(filters): pass filters to credit note query
stephenlago99 Dec 23, 2024
c75f6fb
feat(filters): map specific keys to general ones
stephenlago99 Dec 23, 2024
e10778a
refactor(filters): save invoice number directly
stephenlago99 Dec 23, 2024
db29d05
refactor(filters): update filter formatting for credit notes
stephenlago99 Dec 23, 2024
b57ddcb
fix(ExportDialog): remove typo
stephenlago99 Dec 23, 2024
1a49a10
temporary-fix(filters): fix amount serialization
stephenlago99 Dec 23, 2024
3d25eae
fix(tests): fix passing falsy value
stephenlago99 Dec 23, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
354 changes: 189 additions & 165 deletions src/components/creditNote/CreditNotesTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import { generatePath, useNavigate } from 'react-router-dom'
import styled, { css } from 'styled-components'

import CreditNoteBadge from '~/components/creditNote/CreditNoteBadge'
import { Filters } from '~/components/designSystem/Filters/Filters'
import { AvailableFiltersEnum } from '~/components/designSystem/Filters/types'
import { addToast } from '~/core/apolloClient'
import { intlFormatNumber } from '~/core/formats/intlFormatNumber'
import {
Expand Down Expand Up @@ -172,188 +174,210 @@ const CreditNotesTable = ({
const showCustomerName = !customerTimezone

return (
<ScrollContainer ref={listContainerElementRef} role="grid" tabIndex={-1} onKeyDown={onKeyDown}>
<div>
{isLoading && !!variables?.searchTerm ? (
<>
{[1, 2, 3, 4].map((i) => (
<CreditNoteTableItemSkeleton key={`key-initial-loading-skeleton-line-${i}`} />
))}
</>
) : !isLoading && !!variables?.searchTerm && !creditNotes?.length ? (
<GenericPlaceholder
title={translate('text_63c6edd80c57d0dfaae389a4')}
subtitle={translate('text_63c6edd80c57d0dfaae389a8')}
image={<EmptyImage width="136" height="104" />}
/>
) : (
<InfiniteScroll
onBottom={() => {
const { currentPage = 0, totalPages = 0 } = metadata || {}
<>
<div className="box-border flex w-full flex-col gap-3 p-4 shadow-b md:px-12 md:py-3">
<Filters
filters={[
AvailableFiltersEnum.amount,
AvailableFiltersEnum.creditNoteCreditStatus,
AvailableFiltersEnum.currency,
AvailableFiltersEnum.customerExternalId,
AvailableFiltersEnum.invoiceNumber,
AvailableFiltersEnum.issuingDate,
AvailableFiltersEnum.creditNoteReason,
AvailableFiltersEnum.creditNoteRefundStatus,
]}
/>
</div>

currentPage < totalPages &&
!isLoading &&
fetchMore({
variables: { page: currentPage + 1 },
})
}}
>
<Table
name="credit-notes-list"
data={creditNotes || []}
containerSize={
tableContainerSize || {
default: 0,
}
}
isLoading={isLoading}
hasError={!!error}
placeholder={{
emptyState: {
title: translate('text_6663014df0a6be0098264dd9'),
subtitle: translate('text_6663014df0a6be0098264dda'),
},
<ScrollContainer
ref={listContainerElementRef}
role="grid"
tabIndex={-1}
onKeyDown={onKeyDown}
>
<div>
{isLoading && !!variables?.searchTerm ? (
<>
{[1, 2, 3, 4].map((i) => (
<CreditNoteTableItemSkeleton key={`key-initial-loading-skeleton-line-${i}`} />
))}
</>
) : !isLoading && !!variables?.searchTerm && !creditNotes?.length ? (
<GenericPlaceholder
title={translate('text_63c6edd80c57d0dfaae389a4')}
subtitle={translate('text_63c6edd80c57d0dfaae389a8')}
image={<EmptyImage width="136" height="104" />}
/>
) : (
<InfiniteScroll
onBottom={() => {
const { currentPage = 0, totalPages = 0 } = metadata || {}

currentPage < totalPages &&
!isLoading &&
fetchMore({
variables: { page: currentPage + 1 },
})
}}
actionColumnTooltip={(creditNote) =>
translate(
creditNote.canBeVoided && hasPermissions(['creditNotesVoid'])
? 'text_63728c6434e1344aea76347d'
: 'text_63728c6434e1344aea76347f',
)
}
actionColumn={(creditNote) => {
let actions: ActionItem<CreditNoteTableItemFragment>[] = []
>
<Table
name="credit-notes-list"
data={creditNotes || []}
containerSize={
tableContainerSize || {
default: 0,
}
}
isLoading={isLoading}
hasError={!!error}
placeholder={{
emptyState: {
title: translate('text_6663014df0a6be0098264dd9'),
subtitle: translate('text_6663014df0a6be0098264dda'),
},
}}
actionColumnTooltip={(creditNote) =>
translate(
creditNote.canBeVoided && hasPermissions(['creditNotesVoid'])
? 'text_63728c6434e1344aea76347d'
: 'text_63728c6434e1344aea76347f',
)
}
actionColumn={(creditNote) => {
let actions: ActionItem<CreditNoteTableItemFragment>[] = []

const canDownload = hasPermissions(['creditNotesView'])
const canVoid = creditNote.canBeVoided && hasPermissions(['creditNotesVoid'])
const canDownload = hasPermissions(['creditNotesView'])
const canVoid = creditNote.canBeVoided && hasPermissions(['creditNotesVoid'])

if (canDownload) {
actions = [
...actions,
{
title: translate('text_636d12ce54c41fccdf0ef72d'),
disabled: loadingCreditNoteDownload,
onAction: async ({ id }: { id: string }) => {
await downloadCreditNote({
variables: { input: { id } },
})
if (canDownload) {
actions = [
...actions,
{
title: translate('text_636d12ce54c41fccdf0ef72d'),
disabled: loadingCreditNoteDownload,
onAction: async ({ id }: { id: string }) => {
await downloadCreditNote({
variables: { input: { id } },
})
},
},
},
]
}
]
}

if (canVoid) {
actions = [
...actions,
{
title: translate('text_636d12ce54c41fccdf0ef72f'),
onAction: async ({ id, totalAmountCents, currency }) => {
voidCreditNoteDialogRef.current?.openDialog({
id,
totalAmountCents,
currency,
})
},
},
]
}

if (canVoid) {
actions = [
...actions,
{
title: translate('text_636d12ce54c41fccdf0ef72f'),
onAction: async ({ id, totalAmountCents, currency }) => {
voidCreditNoteDialogRef.current?.openDialog({
id,
totalAmountCents,
currency,
title: translate('text_636d12ce54c41fccdf0ef731'),
onAction: async ({ id }: { id: string }) => {
copyToClipboard(id)

addToast({
severity: 'info',
translateKey: 'text_63720bd734e1344aea75b82d',
})
},
},
]
}

actions = [
...actions,
return actions
}}
onRowAction={(creditNote) => {
navigate(
generatePath(CUSTOMER_INVOICE_CREDIT_NOTE_DETAILS_ROUTE, {
customerId: creditNote?.invoice?.customer?.id as string,
invoiceId: creditNote?.invoice?.id as string,
creditNoteId: creditNote?.id as string,
}),
)
}}
columns={[
{
title: translate('text_636d12ce54c41fccdf0ef731'),
onAction: async ({ id }: { id: string }) => {
copyToClipboard(id)

addToast({
severity: 'info',
translateKey: 'text_63720bd734e1344aea75b82d',
})
},
key: 'totalAmountCents',
title: translate('text_1727078012568v9460bmnh8a'),
content: (creditNote) => <CreditNoteBadge creditNote={creditNote} />,
},
]

return actions
}}
onRowAction={(creditNote) => {
navigate(
generatePath(CUSTOMER_INVOICE_CREDIT_NOTE_DETAILS_ROUTE, {
customerId: creditNote?.invoice?.customer?.id as string,
invoiceId: creditNote?.invoice?.id as string,
creditNoteId: creditNote?.id as string,
}),
)
}}
columns={[
{
key: 'totalAmountCents',
title: translate('text_1727078012568v9460bmnh8a'),
content: (creditNote) => <CreditNoteBadge creditNote={creditNote} />,
},
{
key: 'number',
title: translate('text_64188b3d9735d5007d71227f'),
minWidth: 160,
content: ({ number }) => (
<Typography variant="body" noWrap>
{number}
</Typography>
),
},
{
key: 'totalAmountCents',
title: translate('text_62544c1db13ca10187214d85'),
content: ({ totalAmountCents, currency }) => (
<Typography
className="font-medium"
variant="body"
color={showCustomerName ? 'grey700' : 'success600'}
align="right"
noWrap
>
{intlFormatNumber(deserializeAmount(totalAmountCents || 0, currency), {
currencyDisplay: 'symbol',
currency,
})}
</Typography>
),
maxSpace: !showCustomerName,
textAlign: 'right',
},
...(showCustomerName
? [
{
key: 'invoice.customer.displayName',
title: translate('text_63ac86d797f728a87b2f9fb3'),
content: (creditNote: CreditNoteTableItemFragment) => (
<Typography variant="body" color="grey600" noWrap>
{creditNote.invoice?.customer.displayName}
</Typography>
),
maxSpace: true,
tdCellClassName: 'hidden md:table-cell',
} as TableColumn<CreditNoteTableItemFragment>,
]
: []),
{
key: 'createdAt',
title: translate('text_62544c1db13ca10187214d7f'),
content: ({ createdAt }) => (
<Typography variant="body" color="grey600" noWrap>
{customerTimezone
? formatDateToTZ(createdAt, customerTimezone)
: formatTimeOrgaTZ(createdAt)}
</Typography>
),
},
]}
/>
</InfiniteScroll>
)}
</div>
{
key: 'number',
title: translate('text_64188b3d9735d5007d71227f'),
minWidth: 160,
content: ({ number }) => (
<Typography variant="body" noWrap>
{number}
</Typography>
),
},
{
key: 'totalAmountCents',
title: translate('text_62544c1db13ca10187214d85'),
content: ({ totalAmountCents, currency }) => (
<Typography
className="font-medium"
variant="body"
color={showCustomerName ? 'grey700' : 'success600'}
align="right"
noWrap
>
{intlFormatNumber(deserializeAmount(totalAmountCents || 0, currency), {
currencyDisplay: 'symbol',
currency,
})}
</Typography>
),
maxSpace: !showCustomerName,
textAlign: 'right',
},
...(showCustomerName
? [
{
key: 'invoice.customer.displayName',
title: translate('text_63ac86d797f728a87b2f9fb3'),
content: (creditNote: CreditNoteTableItemFragment) => (
<Typography variant="body" color="grey600" noWrap>
{creditNote.invoice?.customer.displayName}
</Typography>
),
maxSpace: true,
tdCellClassName: 'hidden md:table-cell',
} as TableColumn<CreditNoteTableItemFragment>,
]
: []),
{
key: 'createdAt',
title: translate('text_62544c1db13ca10187214d7f'),
content: ({ createdAt }) => (
<Typography variant="body" color="grey600" noWrap>
{customerTimezone
? formatDateToTZ(createdAt, customerTimezone)
: formatTimeOrgaTZ(createdAt)}
</Typography>
),
},
]}
/>
</InfiniteScroll>
)}
</div>

<VoidCreditNoteDialog ref={voidCreditNoteDialogRef} />
</ScrollContainer>
<VoidCreditNoteDialog ref={voidCreditNoteDialogRef} />
</ScrollContainer>
</>
)
}

Expand Down
2 changes: 1 addition & 1 deletion src/components/designSystem/Filters/ActiveFiltersList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export const ActiveFiltersList = ({ filters }: ActiveFiltersListProps) => {
...acc,
{
label: translate(mapFilterToTranslationKey(key)),
value: formatActiveFilterValueDisplay(key, value),
value: formatActiveFilterValueDisplay(key, value, translate),
},
]
},
Expand Down
Loading
Loading