Skip to content

Commit

Permalink
Create pool - functionality (#2545)
Browse files Browse the repository at this point in the history
* Fix ts error and change logic for onboarding values

* Add create pool existing functionality

* Cleanup types

* cleanup

* Add deposit banner

* Fix linter errors

* Add metadata values

* Cleanup types

* Add onboarding functionality and UI fixes

* Add proxies functionality

* Fix ts errors

* Add create pool dialog

* Add dialogs

* Add review feedback

* wip

* Add waiting before redirecting to avoid error

* Remove default empty pool fee
  • Loading branch information
kattylucy authored Dec 4, 2024
1 parent 439ae65 commit d912f29
Show file tree
Hide file tree
Showing 16 changed files with 772 additions and 189 deletions.
4 changes: 2 additions & 2 deletions centrifuge-app/src/components/Menu/IssuerMenu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -62,9 +62,9 @@ export function IssuerMenu({ defaultOpen = false, children }: IssuerMenuProps) {
Issuer
{isLarge &&
(open ? (
<IconChevronDown size={['iconMedium', 'iconMedium', 'iconSmall']} />
<IconChevronDown size={['iconMedium', 'iconMedium', 'iconSmall']} color="white" />
) : (
<IconChevronRight size={['iconMedium', 'iconMedium', 'iconSmall']} />
<IconChevronRight size={['iconMedium', 'iconMedium', 'iconSmall']} color="white" />
))}
</Toggle>

Expand Down
2 changes: 1 addition & 1 deletion centrifuge-app/src/components/Menu/Toggle.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export const Toggle = styled(Text)<{ isActive?: boolean; stacked?: boolean }>`
width: 100%;
grid-template-columns: ${({ stacked, theme }) =>
stacked ? '1fr' : `${theme.sizes.iconSmall}px 1fr ${theme.sizes.iconSmall}px`};
color: ${({ isActive, theme }) => (isActive ? theme.colors.textGold : theme.colors.textInverted)};
color: ${({ theme }) => theme.colors.textInverted};
border-radius: 4px;
background-color: ${({ isActive }) => (isActive ? LIGHT_BACKGROUND : 'transparent')};
Expand Down
4 changes: 4 additions & 0 deletions centrifuge-app/src/components/Menu/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ const StyledRouterLinkButton = styled(RouterLinkButton)`
background-color: ${COLOR};
color: white;
}
&:active {
border-color: transparent;
}
}
`

Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { useWallet } from '@centrifuge/centrifuge-react'
import { Button } from '@centrifuge/fabric'
import { useFormikContext } from 'formik'
import { CreatePoolValues } from '.'
import { PageSection } from '../../components/PageSection'
import { MultisigForm } from '../IssuerPool/Access/MultisigForm'
import { CreatePoolValues } from './types'

export function AdminMultisigSection() {
const form = useFormikContext<CreatePoolValues>()
Expand Down
40 changes: 24 additions & 16 deletions centrifuge-app/src/pages/IssuerCreatePool/IssuerCategories.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { PoolMetadataInput } from '@centrifuge/centrifuge-js'
import { Box, IconButton, IconTrash, Select, Text, TextInput } from '@centrifuge/fabric'
import { Box, Grid, IconButton, IconTrash, Select, Text, TextInput } from '@centrifuge/fabric'
import { Field, FieldArray, FieldProps, useFormikContext } from 'formik'
import { AddButton } from './PoolDetailsSection'
import { StyledGrid } from './PoolStructureSection'
Expand Down Expand Up @@ -38,24 +38,32 @@ export const IssuerCategoriesSection = () => {
<FieldArray name="issuerCategories">
{({ push, remove }) => (
<>
{form.values.issuerCategories.map((_, index) => (
{form.values.issuerCategories.map((category, index) => (
<>
<Field name={`issuerCategories.${index}.type`}>
{({ field, meta }: FieldProps) => (
<Select
name={field.name}
label="Type"
onChange={(event) => form.setFieldValue(field.name, event.target.value)}
onBlur={field.onBlur}
value={field.value}
options={PROVIDERS}
placeholder="Please select..."
/>
<Grid gridTemplateColumns={['1fr', category.type === 'other' ? '1fr 1fr' : '1fr']} gap={2}>
<Field name={`issuerCategories.${index}.type`}>
{({ field, meta }: FieldProps) => (
<Select
name={field.name}
label="Type"
onChange={(event) => form.setFieldValue(field.name, event.target.value)}
onBlur={field.onBlur}
value={field.value}
options={PROVIDERS}
placeholder="Please select..."
/>
)}
</Field>
{category.type === 'other' && (
<Field name={`issuerCategories.${index}.description`}>
{({ field, meta }: FieldProps) => (
<TextInput {...field} label="Description" placeholder="Type here..." maxLength={100} />
)}
</Field>
)}
</Field>

</Grid>
<Field name={`issuerCategories.${index}.value`}>
{({ field, meta }: FieldProps) => (
{({ field }: FieldProps) => (
<TextInput
{...field}
label={
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,6 @@ export const PoolDetailsSection = () => {
const form = useFormikContext<PoolMetadataInput>()
const createLabel = (label: string) => `${label}${isTestEnv ? '' : '*'}`

console.log(form.values)

return (
<Box>
<Text variant="heading2" fontWeight={700}>
Expand Down Expand Up @@ -193,7 +191,6 @@ export const PoolDetailsSection = () => {
placeholder="Type here..."
maxLength={1000}
errorMessage={meta.touched && meta.error ? meta.error : undefined}
// disabled={waitingForStoredIssuer}
/>
)}
</Field>
Expand Down Expand Up @@ -299,9 +296,9 @@ export const PoolDetailsSection = () => {
onFileChange={(file) => {
form.setFieldValue('reportAuthorAvatar', file)
}}
accept="application/pdf"
label="Reviewer avatar"
placeholder="Choose file"
accept="image/*"
small
/>
)}
Expand Down
165 changes: 123 additions & 42 deletions centrifuge-app/src/pages/IssuerCreatePool/PoolSetupSection.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { PoolMetadataInput } from '@centrifuge/centrifuge-js'
import { addressToHex, evmToSubstrateAddress, PoolMetadataInput } from '@centrifuge/centrifuge-js'
import { useCentEvmChainId } from '@centrifuge/centrifuge-react'
import {
Box,
Checkbox,
Expand All @@ -18,6 +19,7 @@ import { useTheme } from 'styled-components'
import { FieldWithErrorMessage } from '../../../src/components/FieldWithErrorMessage'
import { Tooltips } from '../../../src/components/Tooltips'
import { feeCategories } from '../../../src/config'
import { isEvmAddress } from '../../../src/utils/address'
import { AddButton } from './PoolDetailsSection'
import { CheckboxOption, Line, StyledGrid } from './PoolStructureSection'

Expand All @@ -28,8 +30,30 @@ const FEE_TYPES = [

const FEE_POSISTIONS = [{ label: 'Top of waterfall', value: 'Top of waterfall' }]

const TaxDocument = () => {
const form = useFormikContext<PoolMetadataInput>()

return (
<Box mt={2}>
<Text variant="heading4">Tax document requirement</Text>

<Field name="onboarding">
{({ field }: FieldProps) => (
<Checkbox
{...field}
label="Require investors to upload tax documents before signing the subscription agreement."
variant="square"
onChange={(val) => form.setFieldValue('onboarding.taxInfoRequired', val.target.checked ? true : false)}
/>
)}
</Field>
</Box>
)
}

export const PoolSetupSection = () => {
const theme = useTheme()
const chainId = useCentEvmChainId()
const form = useFormikContext<PoolMetadataInput>()
const { values } = form

Expand Down Expand Up @@ -73,7 +97,24 @@ export const PoolSetupSection = () => {
values.adminMultisig?.signers?.map((_, index) => (
<Box key={index} mt={2}>
<Field name={`adminMultisig.signers.${index}`}>
{({ field }: FieldProps) => <TextInput placeholder="Type here..." {...field} />}
{({ field, form }: FieldProps) => (
<TextInput
placeholder="Type address..."
{...field}
onChange={(val) => {
form.setFieldValue(`adminMultisig.signers.${index}`, val.target.value)
}}
onBlur={() => {
const value = form.values.adminMultisig.signers[index]
if (value) {
const transformedValue = isEvmAddress(value)
? evmToSubstrateAddress(value, chainId ?? 0)
: value
form.setFieldValue(`adminMultisig.signers.${index}`, transformedValue)
}
}}
/>
)}
</Field>
</Box>
))
Expand All @@ -88,7 +129,7 @@ export const PoolSetupSection = () => {
<Box display="flex" justifyContent="flex-end" mt={2}>
<AddButton
onClick={() => {
if (form.values.adminMultisig && form.values.adminMultisig.signers?.length <= 10) {
if (values.adminMultisig && values.adminMultisig.signers?.length <= 10) {
push('')
}
}}
Expand All @@ -103,19 +144,19 @@ export const PoolSetupSection = () => {
</Box>
<Box mt={2} mb={2}>
<StyledGrid gridTemplateColumns={['1fr', '1fr 1fr']} gap={3} mt={3}>
<Field name="subAssetClass">
<Field name="adminMultisig.threshold">
{({ field, meta, form }: FieldProps) => (
<Select
name="subAssetClass"
name="adminMultisig.threshold"
label={`Configuration change threshold (1 out of ${Math.max(
values?.adminMultisig?.signers?.length ?? 0,
1
)} managers)`}
onChange={(event) => form.setFieldValue('subAssetClass', event.target.value)}
onChange={(event) => form.setFieldValue('adminMultisig.threshold', event.target.value)}
onBlur={field.onBlur}
errorMessage={meta.touched && meta.error ? meta.error : undefined}
value={field.value}
options={form.values.adminMultisig.signers.map((_: string, i: number) => ({
options={values.adminMultisig?.signers.map((_: string, i: number) => ({
label: i + 1,
value: i + 1,
}))}
Expand All @@ -142,10 +183,27 @@ export const PoolSetupSection = () => {
Add or remove addresses that can:
</Text>
<Text variant="heading2">Originate assets and invest in the pool*</Text>
{form.values.assetOriginators?.map((_: string, index: number) => (
{values.assetOriginators?.map((_: string, index: number) => (
<Box key={index} mt={2}>
<Field name={`assetOriginators.${index}`}>
{({ field }: FieldProps) => <TextInput placeholder="Type address..." {...field} />}
{({ field, form }: FieldProps) => (
<TextInput
placeholder="Type address..."
{...field}
onChange={(val) => {
form.setFieldValue(`assetOriginators.${index}`, val.target.value)
}}
onBlur={() => {
const value = form.values.assetOriginators[index]
if (value) {
const transformedValue = isEvmAddress(value)
? evmToSubstrateAddress(value, chainId ?? 0)
: addressToHex(value)
form.setFieldValue(`assetOriginators.${index}`, transformedValue)
}
}}
/>
)}
</Field>
</Box>
))}
Expand All @@ -154,7 +212,7 @@ export const PoolSetupSection = () => {
<Box gridColumn="2 / span 1" alignSelf="end">
<AddButton
onClick={() => {
if (form.values.adminMultisig && form.values.adminMultisig.signers?.length <= 10) {
if (values.adminMultisig && values.adminMultisig.signers?.length <= 10) {
push('')
}
}}
Expand Down Expand Up @@ -207,16 +265,14 @@ export const PoolSetupSection = () => {
<FieldArray name="poolFees">
{({ push, remove }) => (
<>
{form.values.poolFees.map((_, index) => (
{values.poolFees.map((_, index) => (
<Box mt={4} mb={3} key={index}>
<StyledGrid mt={3} gap={1}>
<Box display="flex" justifyContent="space-between" alignItems="center">
<Text variant="heading3">Pool fees {index + 1}</Text>
{form.values.poolFees.length > 1 && (
<IconButton onClick={() => remove(index)}>
<IconTrash color="textSecondary" />
</IconButton>
)}
<IconButton onClick={() => remove(index)}>
<IconTrash color="textSecondary" />
</IconButton>
</Box>
<Line />
<Grid gridTemplateColumns={['1fr', '1fr 1fr']} gap={3}>
Expand Down Expand Up @@ -325,33 +381,58 @@ export const PoolSetupSection = () => {
icon={<IconHelpCircle size="iconSmall" color={theme.colors.textSecondary} />}
/>
</Box>
<Box>
<Field name="subscriptionDocuments">
{({ field, meta, form }: FieldProps) => (
<Box>
<FileUpload
name="subscriptionDocuments"
file={field.value}
onFileChange={async (file) => {
form.setFieldTouched('poolIcon', true, false)
form.setFieldValue('poolIcon', file)
}}
label="Click to upload"
errorMessage={meta.touched && meta.error ? meta.error : undefined}
accept="application/pdf"
small
/>
</Box>
)}
</Field>
<Box mt={8}>
<Text variant="heading4">Tax document requirement</Text>
<Checkbox
label="Require investors to upload tax documents before signing the subscription agreement."
variant="square"
/>
{values.onboardingExperience === 'centrifuge' && (
<Box>
<Box>
{values.tranches.map((tranche, index) => (
<Field key={index} name={`onboarding.tranches.${tranche.tokenName}`}>
{({ field, meta }: FieldProps) => (
<Box mb={4}>
<FileUpload
name={`onboarding.${tranche.tokenName}`}
file={field.value}
onFileChange={async (file) => {
form.setFieldTouched(`onboarding.tranches.${tranche.tokenName}`, true, false)
form.setFieldValue(`onboarding.tranches.${tranche.tokenName}`, file)
}}
label={`Subscription document for ${tranche.tokenName}`}
errorMessage={meta.touched && meta.error ? meta.error : undefined}
accept="application/pdf"
small
/>
</Box>
)}
</Field>
))}
</Box>
<TaxDocument />
</Box>
</Box>
)}
{values.onboardingExperience === 'external' && (
<Box>
{values.tranches.map((tranche, index) => (
<Field key={index} name={`onboarding.tranches.${tranche.tokenName}`}>
{({ field, meta }: FieldProps) => (
<Box mb={4}>
<FieldWithErrorMessage
{...field}
as={TextInput}
name={`onboarding.tranches.${tranche.tokenName}`}
value={field.value}
label={<Text variant="heading4">Onboarding URL {tranche.tokenName}</Text>}
isUrl
placeholder="www.example.com"
onChange={(e: React.ChangeEvent<HTMLInputElement>) =>
form.setFieldValue(`onboarding.tranches.${tranche.tokenName}`, e.target.value)
}
/>
</Box>
)}
</Field>
))}
<TaxDocument />
</Box>
)}
</StyledGrid>
</Box>
</Box>
Expand Down
Loading

0 comments on commit d912f29

Please sign in to comment.