Skip to content

Commit

Permalink
Centrifuge App: Pending write-off and epoch changes (#1628)
Browse files Browse the repository at this point in the history
  • Loading branch information
onnovisser authored Oct 19, 2023
1 parent e2e3525 commit 772aca2
Show file tree
Hide file tree
Showing 9 changed files with 205 additions and 124 deletions.
41 changes: 41 additions & 0 deletions centrifuge-app/src/components/PoolChangesBanner.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { Banner, Text } from '@centrifuge/fabric'
import * as React from 'react'
import { useLoanChanges, usePoolChanges } from '../utils/usePools'
import { RouterTextLink } from './TextLink'

export type PoolChangesBannerProps = {
poolId: string
}
const STORAGE_KEY = 'poolChangesBannerDismissed'

export function PoolChangesBanner({ poolId }: PoolChangesBannerProps) {
const poolChanges = usePoolChanges(poolId)
const { policyChanges } = useLoanChanges(poolId)
const [isOpen, setIsOpen] = React.useState(false)

React.useEffect(() => {
const dismissed = !!sessionStorage.getItem(STORAGE_KEY)
const hasReady = policyChanges?.some((change) => change.status === 'ready') || poolChanges?.status === 'ready'
if (!dismissed && hasReady) {
setIsOpen(true)
}
}, [poolChanges, policyChanges])

function onClose() {
sessionStorage.setItem(STORAGE_KEY, '1')
setIsOpen(false)
}

return (
<Banner
isOpen={isOpen}
onClose={onClose}
title={
<Text as="h3" color="textInverted" variant="heading5">
There are pending pool changes that can now be enabled{' '}
<RouterTextLink to={`/issuer/${poolId}/configuration`}>here</RouterTextLink>
</Text>
}
/>
)
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { CurrencyBalance, Perquintill, PoolMetadata, PoolMetadataInput, Rate } from '@centrifuge/centrifuge-js'
import { useCentrifugeTransaction } from '@centrifuge/centrifuge-react'
import { Button, Grid, NumberInput, Shelf, Stack, Text, Thumbnail } from '@centrifuge/fabric'
import { useCentrifugeConsts, useCentrifugeTransaction } from '@centrifuge/centrifuge-react'
import { Button, Grid, NumberInput, Shelf, Stack, StatusChip, Text, Thumbnail } from '@centrifuge/fabric'
import { Form, FormikProvider, useFormik } from 'formik'
import * as React from 'react'
import { useParams } from 'react-router'
Expand All @@ -12,7 +12,7 @@ import { LabelValueStack } from '../../../components/LabelValueStack'
import { PageSection } from '../../../components/PageSection'
import { formatBalance, formatPercentage } from '../../../utils/formatting'
import { useSuitableAccounts } from '../../../utils/usePermissions'
import { useConstants, usePool, usePoolMetadata } from '../../../utils/usePools'
import { usePool, usePoolChanges, usePoolMetadata } from '../../../utils/usePools'
import { TrancheInput } from '../../IssuerCreatePool/TrancheInput'
import { validate } from '../../IssuerCreatePool/validate'

Expand All @@ -25,7 +25,13 @@ export function EpochAndTranches() {
const [isEditing, setIsEditing] = React.useState(false)
const pool = usePool(poolId)
const { data: metadata } = usePoolMetadata(pool)
const [account] = useSuitableAccounts({ poolId, poolRole: ['PoolAdmin'] })
const [account] = useSuitableAccounts({ poolId, poolRole: ['PoolAdmin'], proxyType: ['Borrow'] })
const changes = usePoolChanges(poolId)

const { execute: executeApply, isLoading: isApplyLoading } = useCentrifugeTransaction(
'Apply pool update',
(cent) => cent.pools.applyPoolUpdate
)

const columns: Column[] = [
{
Expand Down Expand Up @@ -85,7 +91,7 @@ export function EpochAndTranches() {
[pool, metadata]
)

const consts = useConstants()
const consts = useCentrifugeConsts()

const epochHours = Math.floor((pool?.parameters.minEpochTime ?? 0) / 3600)
const epochMinutes = Math.floor(((pool?.parameters.minEpochTime ?? 0) / 60) % 60)
Expand Down Expand Up @@ -177,7 +183,7 @@ export function EpochAndTranches() {
]
execute(
[poolId, newPoolMetadata, { minEpochTime: epochSeconds, tranches: hasTrancheChanges ? tranches : undefined }],
{ account }
{ account, forceProxyType: 'Borrow' }
)
actions.setSubmitting(false)
},
Expand All @@ -198,22 +204,23 @@ export function EpochAndTranches() {

const hasChanges = Object.entries(form.values).some(([k, v]) => (initialValues as any)[k] !== v)

const delay = consts?.minUpdateDelay ? consts.minUpdateDelay / (60 * 60 * 24) : null
const delay = consts.poolSystem.minUpdateDelay / (60 * 60 * 24)

const trancheData = [...tranches].reverse()

return (
<FormikProvider value={form}>
<Form>
<PageSection
title="Epoch and tranches"
subtitle={
delay
? `Changes take ${
delay < 0.5 ? `${Math.ceil(delay / 24)} hours` : `${Math.round(delay)} days`
} to take effect`
: undefined
title={
<Shelf gap={1}>
Epoch and tranches{' '}
{changes && changes.status !== 'ready' && <StatusChip status="info">Pending changes</StatusChip>}
</Shelf>
}
subtitle={`Changes require ${
delay < 0.5 ? `${Math.ceil(delay / 24)} hour(s)` : `${Math.round(delay)} day(s)`
} and no oustanding redeem orders before they can be enabled`}
headerRight={
isEditing ? (
<ButtonGroup variant="small">
Expand All @@ -232,9 +239,22 @@ export function EpochAndTranches() {
</Button>
</ButtonGroup>
) : (
<Button variant="secondary" onClick={() => setIsEditing(true)} small key="edit">
Edit
</Button>
<ButtonGroup>
{changes?.status === 'ready' && (
<Button
small
loading={isApplyLoading}
disabled={!account}
onClick={() => executeApply([poolId], { account })}
key="apply"
>
Apply changes
</Button>
)}
<Button variant="secondary" onClick={() => setIsEditing(true)} small key="edit">
Edit
</Button>
</ButtonGroup>
)
}
>
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Rate, WriteOffGroup } from '@centrifuge/centrifuge-js'
import { useCentrifugeTransaction } from '@centrifuge/centrifuge-react'
import { Box, Button, Stack } from '@centrifuge/fabric'
import { useCentrifugeConsts, useCentrifugeTransaction } from '@centrifuge/centrifuge-react'
import { Box, Button, Shelf, Stack, StatusChip } from '@centrifuge/fabric'
import { FieldArray, Form, FormikErrors, FormikProvider, setIn, useFormik } from 'formik'
import * as React from 'react'
import { useParams } from 'react-router'
Expand All @@ -9,8 +9,7 @@ import { Column, DataTable } from '../../../components/DataTable'
import { PageSection } from '../../../components/PageSection'
import { formatPercentage } from '../../../utils/formatting'
import { useSuitableAccounts } from '../../../utils/usePermissions'
import { useConstants, useWriteOffGroups } from '../../../utils/usePools'
import { PendingLoanChanges } from './PendingLoanChanges'
import { useLoanChanges, useWriteOffGroups } from '../../../utils/usePools'
import { WriteOffInput } from './WriteOffInput'

export type Row = WriteOffGroup
Expand Down Expand Up @@ -53,8 +52,15 @@ export type WriteOffGroupValues = { writeOffGroups: WriteOffGroupInput[] }
export function WriteOffGroups() {
const { pid: poolId } = useParams<{ pid: string }>()
const [isEditing, setIsEditing] = React.useState(false)
const consts = useConstants()
const [account] = useSuitableAccounts({ poolId, poolRole: ['LoanAdmin'] })
const consts = useCentrifugeConsts()
const [account] = useSuitableAccounts({ poolId, poolRole: ['PoolAdmin'] })
const { policyChanges } = useLoanChanges(poolId)
const latestPolicyChange = policyChanges?.at(-1)

const { execute: executeApply, isLoading: isApplyLoading } = useCentrifugeTransaction(
'Apply write-off policy',
(cent) => cent.pools.applyWriteOffPolicyUpdate
)

const savedGroups = useWriteOffGroups(poolId)
const sortedSavedGroups = [...(savedGroups ?? [])].sort((a, b) => a.overdueDays - b.overdueDays)
Expand Down Expand Up @@ -160,7 +166,7 @@ export function WriteOffGroups() {
}}
small
key="edit"
disabled={form.values.writeOffGroups.length >= (consts?.maxWriteOffPolicySize ?? 5) || !account}
disabled={form.values.writeOffGroups.length >= consts.loans.maxWriteOffPolicySize || !account}
>
Add another
</Button>
Expand All @@ -172,7 +178,14 @@ export function WriteOffGroups() {
<FormikProvider value={form}>
<Form>
<PageSection
title="Write-off policy"
title={
<Shelf gap={1}>
Write-off policy{' '}
{latestPolicyChange && latestPolicyChange.status !== 'ready' && (
<StatusChip status="info">Pending changes</StatusChip>
)}
</Shelf>
}
subtitle="At least one write-off activity is required"
headerRight={
<>
Expand All @@ -186,15 +199,29 @@ export function WriteOffGroups() {
small
loading={isLoading || form.isSubmitting}
loadingMessage={isLoading || form.isSubmitting ? 'Pending...' : undefined}
disabled={!account}
key="done"
>
Done
</Button>
</ButtonGroup>
) : (
<Button variant="secondary" onClick={() => setIsEditing(true)} small>
Edit
</Button>
<ButtonGroup>
{latestPolicyChange?.status === 'ready' && (
<Button
small
loading={isApplyLoading}
disabled={!account}
onClick={() => executeApply([poolId, latestPolicyChange.hash], { account })}
key="apply"
>
Apply changes
</Button>
)}
<Button variant="secondary" onClick={() => setIsEditing(true)} small>
Edit
</Button>
</ButtonGroup>
)}
</>
}
Expand All @@ -208,7 +235,6 @@ export function WriteOffGroups() {
) : (
<DataTable data={sortedSavedGroups} columns={columns} />
)}
<PendingLoanChanges poolId={poolId} />
</Stack>
</PageSection>
</Form>
Expand Down
6 changes: 4 additions & 2 deletions centrifuge-app/src/pages/IssuerPool/Configuration/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { useDebugFlags } from '../../../components/DebugFlags'
import { LoadBoundary } from '../../../components/LoadBoundary'
import { PageWithSideBar } from '../../../components/PageWithSideBar'
import { PendingMultisigs } from '../../../components/PendingMultisigs'
import { usePoolAdmin } from '../../../utils/usePermissions'
import { useCanBorrow, usePoolAdmin } from '../../../utils/usePermissions'
import { IssuerPoolHeader } from '../Header'
import { Details } from './Details'
import { EpochAndTranches } from './EpochAndTranches'
Expand All @@ -28,10 +28,12 @@ export function IssuerPoolConfigurationPage() {
function IssuerPoolConfiguration() {
const { pid: poolId } = useParams<{ pid: string }>()
const { editPoolConfig } = useDebugFlags()
const isPoolAdmin = !!usePoolAdmin(poolId)
const isBorrower = useCanBorrow(poolId)

return (
<Stack>
{!!usePoolAdmin(poolId) && (
{(isPoolAdmin || isBorrower) && (
<>
<Details />
<Issuer />
Expand Down
31 changes: 18 additions & 13 deletions centrifuge-app/src/pages/IssuerPool/index.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import * as React from 'react'
import { Route, Switch, useRouteMatch } from 'react-router'
import { Route, Switch, useParams, useRouteMatch } from 'react-router'
import { PoolChangesBanner } from '../../components/PoolChangesBanner'
import { IssuerPoolAccessPage } from './Access'
import { IssuerPoolAssetPage } from './Assets'
import { IssuerPoolConfigurationPage } from './Configuration'
Expand All @@ -10,20 +11,24 @@ import { IssuerPoolLiquidityPage } from './Liquidity'
import { IssuerPoolOverviewPage } from './Overview'
import { IssuerPoolReportingPage } from './Reporting'

export const IssuerPoolPage: React.FC = () => {
export function IssuerPoolPage() {
const { path } = useRouteMatch()
const { pid: poolId } = useParams<{ pid: string }>()

return (
<Switch>
<Route path={`${path}/configuration/view-asset-template/:sid`} component={IssuerPoolViewLoanTemplatePage} />
<Route path={`${path}/configuration/create-asset-template`} component={IssuerPoolCreateLoanTemplatePage} />
<Route path={`${path}/configuration`} component={IssuerPoolConfigurationPage} />
<Route path={`${path}/investors`} component={IssuerPoolInvestorsPage} />
<Route path={`${path}/access`} component={IssuerPoolAccessPage} />
<Route path={`${path}/assets`} component={IssuerPoolAssetPage} />
<Route path={`${path}/liquidity`} component={IssuerPoolLiquidityPage} />
<Route path={`${path}/reporting`} component={IssuerPoolReportingPage} />
<Route path={path} component={IssuerPoolOverviewPage} />
</Switch>
<>
<Switch>
<Route path={`${path}/configuration/view-asset-template/:sid`} component={IssuerPoolViewLoanTemplatePage} />
<Route path={`${path}/configuration/create-asset-template`} component={IssuerPoolCreateLoanTemplatePage} />
<Route path={`${path}/configuration`} component={IssuerPoolConfigurationPage} />
<Route path={`${path}/investors`} component={IssuerPoolInvestorsPage} />
<Route path={`${path}/access`} component={IssuerPoolAccessPage} />
<Route path={`${path}/assets`} component={IssuerPoolAssetPage} />
<Route path={`${path}/liquidity`} component={IssuerPoolLiquidityPage} />
<Route path={`${path}/reporting`} component={IssuerPoolReportingPage} />
<Route path={path} component={IssuerPoolOverviewPage} />
</Switch>
<PoolChangesBanner poolId={poolId} />
</>
)
}
Loading

0 comments on commit 772aca2

Please sign in to comment.