Skip to content

Commit

Permalink
Add drawer accordiong by selected asset type arch
Browse files Browse the repository at this point in the history
  • Loading branch information
kattylucy committed Feb 6, 2025
1 parent d80d564 commit 915c940
Show file tree
Hide file tree
Showing 9 changed files with 165 additions and 92 deletions.
11 changes: 8 additions & 3 deletions centrifuge-app/src/components/Dashboard/assets/AssetsTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,13 @@ type Row = Loan & {
pool: Pool
}

export type Step = 'upload-template' | 'create-asset'

export default function AssetsTable({ loans }: { loans: TransformedLoan[] }) {
const theme = useTheme()
const cent = useCentrifuge()
const [open, setOpen] = useState(false)
const [type, setType] = useState<Step>('create-asset')
const { selectedPools } = useSelectedPools()
const extractedPools = loans.map((loan) => loan.pool)
const poolMetadataMap = usePoolMetadataMap(extractedPools)
Expand Down Expand Up @@ -234,7 +237,7 @@ export default function AssetsTable({ loans }: { loans: TransformedLoan[] }) {
if (isLoading) return <Spinner />

return (
<Box>
<>
<Box display="flex" justifyContent="space-between">
<Box display="flex" alignItems="center">
<Box
Expand All @@ -259,6 +262,7 @@ export default function AssetsTable({ loans }: { loans: TransformedLoan[] }) {
small
onClick={() => {
setOpen(true)
setType('create-asset')
}}
>
Create asset
Expand All @@ -268,6 +272,7 @@ export default function AssetsTable({ loans }: { loans: TransformedLoan[] }) {
small
onClick={() => {
setOpen(true)
setType('upload-template')
}}
>
Manage asset templates
Expand All @@ -290,7 +295,7 @@ export default function AssetsTable({ loans }: { loans: TransformedLoan[] }) {
onRowClicked={(row) => `/pools/${row.poolId}/assets/${row.assetId}`}
/>
</Box>
{open && <CreateAssetsDrawer open={open} onClose={() => setOpen(false)} />}
</Box>
{open && <CreateAssetsDrawer open={open} onClose={() => setOpen(false)} type={type} />}
</>
)
}
90 changes: 90 additions & 0 deletions centrifuge-app/src/components/Dashboard/assets/CreateAssetForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import { Accordion, Box, Divider, IconHelpCircle, Text, TextInput } from '@centrifuge/fabric'
import { Field, FieldProps, useFormikContext } from 'formik'
import { tooltipText } from 'src/components/Tooltips'
import { useTheme } from 'styled-components'
import { useSuitableAccounts } from '../../../../src/utils/usePermissions'
import { CheckboxOption } from '../../../pages/IssuerCreatePool/PoolStructureSection'
import { CreateAssetFormValues } from './CreateAssetsDrawer'
import { CustomAssetForm } from './CustomAssetForm'
import { FundSharesForm } from './FundSharesForm'
import { LiquidAssetsForm } from './LiquidAssetsForm'

const assetTypes = [
{ label: 'Cash', tooltip: 'cashAsset', id: 'cash' },
{ label: 'Liquid assets', tooltip: 'liquidAsset', id: 'liquid' },
{ label: 'Fund shares', tooltip: 'fundShares', id: 'fund' },
{ label: 'Custom assets', tooltip: 'customAsset', id: 'custom' },
]

export function CreateAssetsForm({ hasTemplates }: { hasTemplates: boolean }) {
const form = useFormikContext<CreateAssetFormValues>()
const theme = useTheme()
const canCreateAssets =
useSuitableAccounts({ poolId: form.values.poolId, poolRole: ['Borrower'], proxyType: ['Borrow'] }).length > 0

const renderBody = () => {
switch (form.values.assetType) {
case 'liquid':
return <LiquidAssetsForm />
case 'fund':
return <FundSharesForm />
case 'custom':
return <CustomAssetForm />
}
}

return (
<Box>
<Box
backgroundColor="backgroundSecondary"
borderRadius={8}
p={2}
mt={3}
border={`1px solid ${theme.colors.borderPrimary}`}
>
<Box>
<Text variant="heading4">Select asset type*</Text>
{assetTypes.map((asset) => (
<CheckboxOption
height={40}
name="assetType"
label={asset.label}
icon={<IconHelpCircle size="iconSmall" color={theme.colors.textSecondary} />}
onChange={() => form.setFieldValue('assetType', asset.id)}
isChecked={form.values.assetType === asset.id}
id={asset.tooltip as keyof typeof tooltipText}
/>
))}
</Box>
<Box mt={3}>
<Field name="assetName">
{({ field, form }: FieldProps) => (
<TextInput
name="assetName"
label="Asset name*"
value={field.value}
onChange={(event) => {
form.setFieldValue('assetName', event.target.value)
}}
/>
)}
</Field>
</Box>
</Box>
{hasTemplates && canCreateAssets && form.values.assetType !== 'cash' && (
<Box mt={3}>
<Accordion
items={[
{
title: 'Pricing',
body: renderBody(),
},
]}
hideBorder
/>
<Divider color="backgroundSecondary" />
</Box>
)}
</Box>
)
}
110 changes: 39 additions & 71 deletions centrifuge-app/src/components/Dashboard/assets/CreateAssetsDrawer.tsx
Original file line number Diff line number Diff line change
@@ -1,30 +1,27 @@
import { Pool, PoolMetadata } from '@centrifuge/centrifuge-js'
import { Box, Divider, Drawer, IconHelpCircle, Select, Text, TextInput } from '@centrifuge/fabric'
import { Box, Divider, Drawer, Select } from '@centrifuge/fabric'
import { Field, FieldProps, Form, FormikProvider, useFormik } from 'formik'
import { useMemo, useState } from 'react'
import { useTheme } from 'styled-components'
import { CheckboxOption } from '../../../pages/IssuerCreatePool/PoolStructureSection'
import { usePoolsThatAnyConnectedAddressHasPermissionsFor } from '../../../utils/usePermissions'
import { usePools } from '../../../utils/usePools'
import { LoadBoundary } from '../../../../src/components/LoadBoundary'
import { usePoolAdmin, usePoolsThatAnyConnectedAddressHasPermissionsFor } from '../../../utils/usePermissions'
import { Step } from './AssetsTable'
import { CreateAssetsForm } from './CreateAssetForm'
import { FooterActionButtons } from './FooterActionButtons'
import { UploadAssetForm } from './UploadAssetForm'
import { UploadAssetTemplateForm } from './UploadAssetTemplateForm'
import { usePoolMetadataMap } from './utils'

export type PoolWithMetadata = Pool & { meta: PoolMetadata }

const assetTypes = [
{ label: 'Cash', tooltip: 'cashAsset', id: 'cash' },
{ label: 'Liquid assets', tooltip: 'liquidAsset', id: 'liquid' },
{ label: 'Fund shares', tooltip: 'fundShares', id: 'fund' },
{ label: 'Custom assets', tooltip: 'customAsset', id: 'custom' },
]
export type CreateAssetFormValues = {
poolId: string
assetType: string
assetName: string
}

export function CreateAssetsDrawer({ open, onClose }: { open: boolean; onClose: () => void }) {
const theme = useTheme()
const poolsTest = usePools()
export function CreateAssetsDrawer({ open, onClose, type }: { open: boolean; onClose: () => void; type: Step }) {
const pools = usePoolsThatAnyConnectedAddressHasPermissionsFor() || []

Check warning on line 22 in centrifuge-app/src/components/Dashboard/assets/CreateAssetsDrawer.tsx

View workflow job for this annotation

GitHub Actions / build-app

The 'pools' logical expression could make the dependencies of useMemo Hook (at line 34) change on every render. To fix this, wrap the initialization of 'pools' in its own useMemo() Hook

Check warning on line 22 in centrifuge-app/src/components/Dashboard/assets/CreateAssetsDrawer.tsx

View workflow job for this annotation

GitHub Actions / ff-prod / build-app

The 'pools' logical expression could make the dependencies of useMemo Hook (at line 34) change on every render. To fix this, wrap the initialization of 'pools' in its own useMemo() Hook
const metas = usePoolMetadataMap(pools || [])
const [step, setStep] = useState(0)
const [step, setStep] = useState<Step>(type || 'create-asset')

const poolsMetadata = useMemo(() => {
return pools?.map((pool) => {
Expand All @@ -38,7 +35,7 @@ export function CreateAssetsDrawer({ open, onClose }: { open: boolean; onClose:

const form = useFormik({
initialValues: {
poolId: poolsMetadata[0].id,
poolId: poolsMetadata[0]?.id,
assetType: 'cash',
assetName: '',
},
Expand All @@ -47,22 +44,30 @@ export function CreateAssetsDrawer({ open, onClose }: { open: boolean; onClose:
},
})

const poolAdmin = usePoolAdmin(form.values.poolId)

const selectedPool: PoolWithMetadata | undefined = poolsMetadata.find((pool) => pool.id === form.values.poolId)

const uploadTemplate = () => {
setStep(1)
console.log('uploadTemplate')
const handleButtonClick = () => {
console.log('clicked')
if (type === 'create-asset') {
setStep('upload-template')
}
}

if (!pools?.length || !poolsMetadata?.length) return null
if (!poolsMetadata?.length || !selectedPool) return null

return (
<Drawer isOpen={open} onClose={onClose} title={step === 1 ? 'Upload asset template' : 'Create asset'}>
<Divider color="backgroundSecondary" />
<FormikProvider value={form}>
<Form>
{step === 0 && (
<Box>
<LoadBoundary>
<Drawer
isOpen={open}
onClose={onClose}
title={type === 'upload-template' ? 'Upload asset template' : 'Create asset'}
>
<Divider color="backgroundSecondary" />
<FormikProvider value={form}>
<Form>
<Box mb={2}>
<Field name="poolId">
{({ field, form }: FieldProps) => (
<Select
Expand All @@ -76,50 +81,13 @@ export function CreateAssetsDrawer({ open, onClose }: { open: boolean; onClose:
/>
)}
</Field>
<Box
backgroundColor="backgroundSecondary"
borderRadius={8}
p={2}
mt={3}
border={`1px solid ${theme.colors.borderPrimary}`}
>
<Box>
<Text variant="heading4">Select asset type*</Text>
{assetTypes.map((asset) => (
<CheckboxOption
height={40}
name="assetType"
label={asset.label}
icon={<IconHelpCircle size="iconSmall" color={theme.colors.textSecondary} />}
onChange={() => form.setFieldValue('assetType', asset.id)}
isChecked={form.values.assetType === asset.id}
id={asset.tooltip}
/>
))}
</Box>
<Box mt={3}>
<Field name="assetName">
{({ field, form }: FieldProps) => (
<TextInput
name="assetName"
label="Asset name*"
value={field.value}
onChange={(event) => {
form.setFieldValue('assetName', event.target.value)
}}
/>
)}
</Field>
</Box>
</Box>
</Box>
)}
{step === 1 && <UploadAssetForm pool={selectedPool} />}
{selectedPool && (
<FooterActionButtons onClose={onClose} pool={selectedPool} uploadTemplate={uploadTemplate} step={step} />
)}
</Form>
</FormikProvider>
</Drawer>
{step === 'create-asset' && <CreateAssetsForm hasTemplates={!!selectedPool.meta.loanTemplates?.length} />}
{step === 'upload-template' && !!poolAdmin && <UploadAssetTemplateForm pool={selectedPool} />}
<FooterActionButtons onClose={onClose} pool={selectedPool} type={type} onClick={() => handleButtonClick} />
</Form>
</FormikProvider>
</Drawer>
</LoadBoundary>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export const CustomAssetForm = () => {
return <div>CustomAssetForm</div>
}
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
import { Box, Button, IconWarning, Text } from '@centrifuge/fabric'
import { useFormikContext } from 'formik'
import { usePoolAdmin, useSuitableAccounts } from '../../../../src/utils/usePermissions'
import { PoolWithMetadata } from './CreateAssetsDrawer'
import { CreateAssetFormValues, PoolWithMetadata } from './CreateAssetsDrawer'

export const FooterActionButtons = ({
onClose,
pool,
uploadTemplate,
step,
type,
onClick,
}: {
onClose: () => void
pool: PoolWithMetadata
uploadTemplate: () => void
step: number
type: string
onClick: () => void
}) => {
const form = useFormikContext<{ assetType: string; assetName: string }>()
const form = useFormikContext<CreateAssetFormValues>()
const isCash = form.values.assetType === 'cash'
const poolAdmin = usePoolAdmin(pool.id)
const canCreateAssets =
Expand All @@ -26,20 +26,20 @@ export const FooterActionButtons = ({
const hasLoanTemplates = loanTemplates.length > 0
const isPoolAdmin = !!poolAdmin

if (step === 1) {
if (type === 'upload-template') {
return (
<Box width="100%">
<Button disabled={!form.values.assetName} style={{ width: '100%', marginBottom: 8 }} onClick={uploadTemplate}>
<Button disabled={!form.values.assetName} style={{ width: '100%', marginBottom: 8 }} onClick={onClick}>
Save
</Button>
</Box>
)
}

if (poolAdmin && !isCash) {
if (poolAdmin && !isCash && !hasLoanTemplates) {
return (
<Box width="100%">
<Button disabled={!form.values.assetName} style={{ width: '100%', marginBottom: 8 }} onClick={uploadTemplate}>
<Button disabled={!form.values.assetName} style={{ width: '100%', marginBottom: 8 }} onClick={onClick}>
Upload asset template
</Button>
<Text variant="body3" color="textSecondary">
Expand All @@ -65,7 +65,7 @@ export const FooterActionButtons = ({
)
}

if ((canCreateAssets && hasLoanTemplates && isCash) || (poolAdmin && isCash)) {
if ((canCreateAssets && hasLoanTemplates && !isCash) || (poolAdmin && isCash)) {
return (
<Button style={{ width: '100%' }} disabled={!form.values.assetName}>
Create
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export const FundSharesForm = () => {
return <div>FundSharesForm</div>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export const LiquidAssetsForm = () => {
return <div>LiquidAssetsForm</div>
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { AnchorButton, Box, FileUploadButton, IconDownload, IconFile, IconPlus, Text } from '@centrifuge/fabric'
import { useEffect, useMemo, useState } from 'react'
import { useTheme } from 'styled-components'
import { LoanTemplate } from '../../../../src/types'
import { createDownloadJson } from '../../../../src/utils/createDownloadJson'
import { useMetadataMulti } from '../../../../src/utils/useMetadata'
import { LoanTemplate } from '../../../types'
import { createDownloadJson } from '../../../utils/createDownloadJson'
import { useMetadataMulti } from '../../../utils/useMetadata'
import { PoolWithMetadata } from './CreateAssetsDrawer'

interface UploadedFile {
Expand All @@ -21,7 +21,7 @@ interface DownloadItem {
revoke?: () => void
}

export const UploadAssetForm = ({ pool }: { pool: PoolWithMetadata }) => {
export const UploadAssetTemplateForm = ({ pool }: { pool: PoolWithMetadata }) => {
const theme = useTheme()
const templateIds = pool.meta.loanTemplates?.map((s) => s.id) ?? []
const templateMetadata = useMetadataMulti<LoanTemplate>(templateIds)
Expand Down Expand Up @@ -104,7 +104,7 @@ export const UploadAssetForm = ({ pool }: { pool: PoolWithMetadata }) => {
</Box>
))}

<Box mt={2}>
<Box>
<FileUploadButton
accept=".json"
allowedFileTypes={['application/json']}
Expand Down
Loading

0 comments on commit 915c940

Please sign in to comment.