From ab1af1e6eaf920ae8298fa3b8d41a62636f5ec3e Mon Sep 17 00:00:00 2001 From: Alexandre Monjol Date: Tue, 31 Dec 2024 11:45:55 +0100 Subject: [PATCH] misc: use Table component for billable metrics list (#1956) --- .../billableMetrics/BillableMetricItem.tsx | 141 ----------- src/generated/graphql.tsx | 22 +- src/pages/BillableMetricsList.tsx | 233 +++++++++--------- 3 files changed, 127 insertions(+), 269 deletions(-) delete mode 100644 src/components/billableMetrics/BillableMetricItem.tsx diff --git a/src/components/billableMetrics/BillableMetricItem.tsx b/src/components/billableMetrics/BillableMetricItem.tsx deleted file mode 100644 index c77b8fd16..000000000 --- a/src/components/billableMetrics/BillableMetricItem.tsx +++ /dev/null @@ -1,141 +0,0 @@ -import { gql } from '@apollo/client' -import { memo, RefObject } from 'react' -import { generatePath } from 'react-router-dom' - -import { - Avatar, - Button, - ButtonLink, - Icon, - Popper, - Skeleton, - Tooltip, - Typography, -} from '~/components/designSystem' -import { UPDATE_BILLABLE_METRIC_ROUTE } from '~/core/router' -import { - BillableMetricItemFragment, - DeleteBillableMetricDialogFragmentDoc, -} from '~/generated/graphql' -import { useInternationalization } from '~/hooks/core/useInternationalization' -import { ListKeyNavigationItemProps } from '~/hooks/ui/useListKeyNavigation' -import { useOrganizationInfos } from '~/hooks/useOrganizationInfos' -import { usePermissions } from '~/hooks/usePermissions' -import { BaseListItem, ItemContainer, ListItemLink, MenuPopper, PopperOpener } from '~/styles' - -import { DeleteBillableMetricDialogRef } from './DeleteBillableMetricDialog' - -gql` - fragment BillableMetricItem on BillableMetric { - id - name - code - createdAt - } - - ${DeleteBillableMetricDialogFragmentDoc} -` - -interface BillableMetricItemProps { - billableMetric: BillableMetricItemFragment - deleteDialogRef: RefObject - navigationProps?: ListKeyNavigationItemProps -} - -export const BillableMetricItem = memo( - ({ billableMetric, deleteDialogRef, navigationProps }: BillableMetricItemProps) => { - const { id: billableMetricId, name, code, createdAt } = billableMetric - const { translate } = useInternationalization() - const { formatTimeOrgaTZ } = useOrganizationInfos() - const { hasPermissions } = usePermissions() - - return ( - - -
- - - -
- - {name} - - - {code} - -
-
- - {formatTimeOrgaTZ(createdAt)} - -
- - {(hasPermissions(['billableMetricsUpdate']) || - hasPermissions(['billableMetricsDelete'])) && ( - ( - - - - )} - - )} - - )} - - ) - }, -) - -export const BillableMetricItemSkeleton = () => { - return ( - - - - - - ) -} - -BillableMetricItem.displayName = 'BillableMetricItem' diff --git a/src/generated/graphql.tsx b/src/generated/graphql.tsx index 9c1ac9b0e..279985a99 100644 --- a/src/generated/graphql.tsx +++ b/src/generated/graphql.tsx @@ -4326,6 +4326,8 @@ export type Permissions = { organizationView: Scalars['Boolean']['output']; paymentRequestsCreate: Scalars['Boolean']['output']; paymentRequestsView: Scalars['Boolean']['output']; + paymentsCreate: Scalars['Boolean']['output']; + paymentsView: Scalars['Boolean']['output']; plansCreate: Scalars['Boolean']['output']; plansDelete: Scalars['Boolean']['output']; plansUpdate: Scalars['Boolean']['output']; @@ -6401,8 +6403,6 @@ export type GetGoogleAuthUrlQueryVariables = Exact<{ [key: string]: never; }>; export type GetGoogleAuthUrlQuery = { __typename?: 'Query', googleAuthUrl: { __typename?: 'AuthUrl', url: string } }; -export type BillableMetricItemFragment = { __typename?: 'BillableMetric', id: string, name: string, code: string, createdAt: any }; - export type DeleteBillableMetricDialogFragment = { __typename?: 'BillableMetric', id: string, name: string, draftInvoicesCount: number, activeSubscriptionsCount: number }; export type GetBillableMetricToDeleteQueryVariables = Exact<{ @@ -8378,6 +8378,8 @@ export type AddOnsQueryVariables = Exact<{ export type AddOnsQuery = { __typename?: 'Query', addOns: { __typename?: 'AddOnCollection', metadata: { __typename?: 'CollectionMetadata', currentPage: number, totalPages: number }, collection: Array<{ __typename?: 'AddOn', id: string, name: string, amountCurrency: CurrencyEnum, amountCents: any, customersCount: number, createdAt: any }> } }; +export type BillableMetricItemFragment = { __typename?: 'BillableMetric', id: string, name: string, code: string, createdAt: any }; + export type BillableMetricsQueryVariables = Exact<{ page?: InputMaybe; limit?: InputMaybe; @@ -9176,14 +9178,6 @@ export const DeleteAddOnFragmentDoc = gql` name } `; -export const BillableMetricItemFragmentDoc = gql` - fragment BillableMetricItem on BillableMetric { - id - name - code - createdAt -} - `; export const DeleteBillableMetricDialogFragmentDoc = gql` fragment DeleteBillableMetricDialog on BillableMetric { id @@ -11232,6 +11226,14 @@ ${InvoiceForFinalizeInvoiceFragmentDoc} ${InvoiceForUpdateInvoicePaymentStatusFragmentDoc} ${InvoiceMetadatasForInvoiceOverviewFragmentDoc} ${InvoiceMetadatasForMetadataDrawerFragmentDoc}`; +export const BillableMetricItemFragmentDoc = gql` + fragment BillableMetricItem on BillableMetric { + id + name + code + createdAt +} + `; export const EditBillableMetricFragmentDoc = gql` fragment EditBillableMetric on BillableMetric { id diff --git a/src/pages/BillableMetricsList.tsx b/src/pages/BillableMetricsList.tsx index f0d1d34d8..1bde17521 100644 --- a/src/pages/BillableMetricsList.tsx +++ b/src/pages/BillableMetricsList.tsx @@ -3,28 +3,28 @@ import { useRef } from 'react' import { generatePath, useNavigate } from 'react-router-dom' import styled from 'styled-components' -import { - BillableMetricItem, - BillableMetricItemSkeleton, -} from '~/components/billableMetrics/BillableMetricItem' import { DeleteBillableMetricDialog, DeleteBillableMetricDialogRef, } from '~/components/billableMetrics/DeleteBillableMetricDialog' -import { ButtonLink, InfiniteScroll, Typography } from '~/components/designSystem' -import { GenericPlaceholder } from '~/components/GenericPlaceholder' +import { ButtonLink, InfiniteScroll, Table, Typography } from '~/components/designSystem' import { SearchInput } from '~/components/SearchInput' import { CREATE_BILLABLE_METRIC_ROUTE, UPDATE_BILLABLE_METRIC_ROUTE } from '~/core/router' -import { BillableMetricItemFragmentDoc, useBillableMetricsLazyQuery } from '~/generated/graphql' +import { useBillableMetricsLazyQuery } from '~/generated/graphql' import { useInternationalization } from '~/hooks/core/useInternationalization' -import { useListKeysNavigation } from '~/hooks/ui/useListKeyNavigation' import { useDebouncedSearch } from '~/hooks/useDebouncedSearch' +import { useOrganizationInfos } from '~/hooks/useOrganizationInfos' import { usePermissions } from '~/hooks/usePermissions' -import EmptyImage from '~/public/images/maneki/empty.svg' -import ErrorImage from '~/public/images/maneki/error.svg' -import { ListContainer, ListHeader, PageHeader, theme } from '~/styles' +import { PageHeader, theme } from '~/styles' gql` + fragment BillableMetricItem on BillableMetric { + id + name + code + createdAt + } + query billableMetrics($page: Int, $limit: Int, $searchTerm: String) { billableMetrics(page: $page, limit: $limit, searchTerm: $searchTerm) { metadata { @@ -36,14 +36,13 @@ gql` } } } - - ${BillableMetricItemFragmentDoc} ` const BillableMetricsList = () => { const { translate } = useInternationalization() const navigate = useNavigate() const { hasPermissions } = usePermissions() + const { formatTimeOrgaTZ } = useOrganizationInfos() const deleteDialogRef = useRef(null) const [getBillableMetrics, { data, error, loading, fetchMore, variables }] = useBillableMetricsLazyQuery({ @@ -54,15 +53,9 @@ const BillableMetricsList = () => { }) const { debouncedSearch, isLoading } = useDebouncedSearch(getBillableMetrics, loading) const list = data?.billableMetrics?.collection || [] - const { onKeyDown } = useListKeysNavigation({ - getElmId: (i) => `billable-metric-item-${i}`, - navigate: (id) => - navigate(generatePath(UPDATE_BILLABLE_METRIC_ROUTE, { billableMetricId: String(id) })), - }) - let index = -1 return ( -
+ <>
{translate('text_623b497ad05b960101be3438')} @@ -80,98 +73,111 @@ const BillableMetricsList = () => {
- - - - {translate('text_623b497ad05b960101be343e')} - - - {translate('text_623b497ad05b960101be3440')} - - - {!!isLoading && variables?.searchTerm ? ( - <> - {isLoading && - [0, 1, 2].map((i) => ( - - ))} - - ) : !isLoading && !!error ? ( - <> - {!!variables?.searchTerm ? ( - } - /> - ) : ( - location.reload()} - image={} - /> - )} - - ) : !isLoading && (!list || !list.length) ? ( - <> - {!!variables?.searchTerm ? ( - } - /> - ) : ( - navigate(CREATE_BILLABLE_METRIC_ROUTE)} - image={} - /> - )} - - ) : ( - { - const { currentPage = 0, totalPages = 0 } = data?.billableMetrics?.metadata || {} - - currentPage < totalPages && - !isLoading && - fetchMore({ - variables: { page: currentPage + 1 }, - }) - }} - > - {!!list && - list.map((billableMetric) => { - index += 1 + { + const { currentPage = 0, totalPages = 0 } = data?.billableMetrics?.metadata || {} - return ( - - ) - })} - {isLoading && - [0, 1, 2].map((i) => ( - - ))} - - )} - + currentPage < totalPages && + !isLoading && + fetchMore({ + variables: { page: currentPage + 1 }, + }) + }} + > + + navigate(generatePath(UPDATE_BILLABLE_METRIC_ROUTE, { billableMetricId: id })) + } + columns={[ + { + key: 'name', + title: translate('text_623b497ad05b960101be343e'), + minWidth: 200, + maxSpace: true, + content: ({ name, code }) => ( + <> + + {name} + + + {code} + + + ), + }, + { + key: 'createdAt', + title: translate('text_623b497ad05b960101be3440'), + minWidth: 140, + content: ({ createdAt }) => ( + + {formatTimeOrgaTZ(createdAt)} + + ), + }, + ]} + actionColumnTooltip={() => translate('text_6256de3bba111e00b3bfa51b')} + actionColumn={({ id }) => { + return [ + hasPermissions(['billableMetricsUpdate']) + ? { + startIcon: 'pen', + title: translate('text_6256de3bba111e00b3bfa531'), + onAction: () => + navigate( + generatePath(UPDATE_BILLABLE_METRIC_ROUTE, { billableMetricId: id }), + ), + } + : null, + hasPermissions(['billableMetricsDelete']) + ? { + startIcon: 'trash', + title: translate('text_6256de3bba111e00b3bfa533'), + onAction: () => deleteDialogRef.current?.openDialog(id), + } + : null, + ] + }} + placeholder={{ + errorState: !!variables?.searchTerm + ? { + title: translate('text_623b53fea66c76017eaebb6e'), + subtitle: translate('text_63bab307a61c62af497e0599'), + } + : { + title: translate('text_623b53fea66c76017eaebb6e'), + subtitle: translate('text_623b53fea66c76017eaebb76'), + buttonTitle: translate('text_623b53fea66c76017eaebb7a'), + buttonAction: () => location.reload(), + buttonVariant: 'primary', + }, + emptyState: !!variables?.searchTerm + ? { + title: translate('text_63bab307a61c62af497e05a2'), + subtitle: translate('text_63bab307a61c62af497e05a4'), + } + : { + title: translate('text_623b53fea66c76017eaebb70'), + subtitle: translate('text_623b53fea66c76017eaebb78'), + buttonTitle: translate('text_623b53fea66c76017eaebb7c'), + buttonAction: () => navigate(CREATE_BILLABLE_METRIC_ROUTE), + buttonVariant: 'primary', + }, + }} + /> + - + ) } @@ -194,13 +200,4 @@ const HeaderRigthBlock = styled.div` } ` -const ListHead = styled(ListHeader)` - > *:first-child { - flex: 1; - } - > *:not(:last-child) { - margin-right: ${theme.spacing(6)}; - } -` - export default BillableMetricsList