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

Edit pool fields #2455

Merged
merged 3 commits into from
Sep 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
173 changes: 94 additions & 79 deletions centrifuge-app/src/components/IssuerSection.tsx
Original file line number Diff line number Diff line change
@@ -1,85 +1,61 @@
import { PoolMetadata } from '@centrifuge/centrifuge-js'
import { useCentrifuge } from '@centrifuge/centrifuge-react'
import { Accordion, AnchorButton, Box, IconExternalLink, Shelf, Text } from '@centrifuge/fabric'
import { Accordion, AnchorButton, Box, IconExternalLink, Shelf, Stack, Text } from '@centrifuge/fabric'
import * as React from 'react'
import { useLocation } from 'react-router'
import { ExecutiveSummaryDialog } from './Dialogs/ExecutiveSummaryDialog'
import { LabelValueStack } from './LabelValueStack'
import { PillButton } from './PillButton'
import { AnchorTextLink, RouterTextLink } from './TextLink'
import { AnchorTextLink } from './TextLink'

type IssuerSectionProps = {
metadata: Partial<PoolMetadata> | undefined
}

const reportLinks = [
{ label: 'Balance sheet', href: '/balance-sheet' },
{ label: 'Profit & loss', href: '/profit-and-loss' },
{ label: 'Cashflow statement', href: '/cash-flow-statement' },
{ label: 'View all', href: '/' },
]

export function ReportDetails({ metadata }: IssuerSectionProps) {
const cent = useCentrifuge()
const { pathname } = useLocation()
const report = metadata?.pool?.reports?.[0]

return (
<Shelf flexDirection="column" alignItems="flex-start">
<Shelf marginBottom={30}>
<LabelValueStack
label=""
value={
<Text variant="body1">
<Shelf flexWrap="wrap" gap={2} alignItems="flex-start">
{reportLinks.map((link, i) => (
<RouterTextLink to={`${pathname}/reporting${link.href}`} key={`${link.label}-${i}`}>
{link.label}
</RouterTextLink>
))}
</Shelf>
</Text>
}
/>
<Stack gap={2}>
<Text variant="heading2">Pool analysis</Text>
<Shelf flexDirection="column" alignItems="flex-start">
{report && (
<>
<Shelf gap={1}>
{report.author.avatar?.uri && (
<Box
as="img"
height={40}
borderRadius={30}
src={cent.metadata.parseMetadataUrl(report.author.avatar.uri)}
alt=""
/>
)}
{report.author.name && (
<LabelValueStack label="Reviewer" value={<Text variant="body2">{report.author.name}</Text>} />
)}
{report.author.title && (
<LabelValueStack label="Reviewer title" value={<Text variant="body2">{report.author.title}</Text>} />
)}
</Shelf>
<Shelf marginTop={20}>
<AnchorButton href={report.uri} target="_blank" variant="secondary" icon={IconExternalLink}>
View full analysis
</AnchorButton>
</Shelf>
</>
)}
</Shelf>
{report && (
<>
<Text style={{ marginBottom: 8 }} variant="heading2">
Pool analysis
</Text>
<Shelf gap={1}>
{report.author.avatar?.uri && (
<Box
as="img"
height={40}
borderRadius={30}
src={cent.metadata.parseMetadataUrl(report.author.avatar.uri)}
alt=""
/>
)}
<Text variant="body2">
Reviewer: {report.author.name}
<br />
{report.author.title}
</Text>
</Shelf>
<Shelf marginTop={20}>
<AnchorButton href={report.uri} target="_blank" variant="secondary" icon={IconExternalLink}>
View full report
</AnchorButton>
</Shelf>
</>
)}
</Shelf>
</Stack>
)
}

export function IssuerDetails({ metadata }: IssuerSectionProps) {
const cent = useCentrifuge()
const [isDialogOpen, setIsDialogOpen] = React.useState(false)
return (
<>
<Shelf gap={1}>
<Stack gap={2}>
<Stack gap={1}>
{metadata?.pool?.issuer.logo && (
<Box
as="img"
Expand All @@ -89,28 +65,38 @@ export function IssuerDetails({ metadata }: IssuerSectionProps) {
src={cent.metadata.parseMetadataUrl(metadata?.pool?.issuer.logo?.uri)}
/>
)}
<Text variant="body2">{metadata?.pool?.issuer.name}</Text>
</Shelf>
<Text variant="body2">{metadata?.pool?.issuer.description}</Text>

{metadata?.pool?.links.executiveSummary && (
<LabelValueStack label="Issuer" value={<Text variant="body2">{metadata?.pool?.issuer.name}</Text>} />
<LabelValueStack
label="Download"
value={
<>
<PillButton variant="small" onClick={() => setIsDialogOpen(true)}>
Executive summary
</PillButton>
<ExecutiveSummaryDialog
issuerName={metadata?.pool?.issuer.name}
href={cent.metadata.parseMetadataUrl(metadata?.pool?.links.executiveSummary?.uri)}
open={isDialogOpen}
onClose={() => setIsDialogOpen(false)}
/>
</>
}
label="Legal representative"
value={<Text variant="body2">{metadata?.pool?.issuer.repName}</Text>}
/>
)}
<LabelValueStack
label="Short description"
value={<Text variant="body2">{metadata?.pool?.issuer.shortDescription}</Text>}
/>
<LabelValueStack
label="Description"
value={<Text variant="body2">{metadata?.pool?.issuer.description}</Text>}
/>
{metadata?.pool?.links.executiveSummary && (
<LabelValueStack
label="Download"
value={
<>
<PillButton variant="small" onClick={() => setIsDialogOpen(true)}>
Executive summary
</PillButton>
<ExecutiveSummaryDialog
issuerName={metadata?.pool?.issuer.name}
href={cent.metadata.parseMetadataUrl(metadata?.pool?.links.executiveSummary?.uri)}
open={isDialogOpen}
onClose={() => setIsDialogOpen(false)}
/>
</>
}
/>
)}
</Stack>

{(metadata?.pool?.links.website || metadata?.pool?.links.forum || metadata?.pool?.issuer.email) && (
<LabelValueStack
Expand All @@ -135,6 +121,35 @@ export function IssuerDetails({ metadata }: IssuerSectionProps) {
{!!metadata?.pool?.details?.length && (
<LabelValueStack label="Details" value={<Accordion items={metadata?.pool?.details} />} />
)}
</>
</Stack>
)
}

export function RatingDetails({ metadata }: IssuerSectionProps) {
const rating = metadata?.pool?.rating

return (
<Stack gap={1}>
<Text variant="heading2">Pool rating</Text>
<Shelf flexDirection="column" alignItems="flex-start">
{rating && (
<Shelf gap={1}>
{rating.ratingAgency && (
<LabelValueStack label="Rating agency" value={<Text variant="body2">{rating.ratingAgency}</Text>} />
)}
{rating.ratingValue && (
<LabelValueStack label="Rating" value={<Text variant="body2">{rating.ratingValue}</Text>} />
)}
</Shelf>
)}
</Shelf>
<Shelf>
{rating?.ratingReportUrl && (
<AnchorButton href={rating.ratingReportUrl} target="_blank" variant="secondary" icon={IconExternalLink}>
View full report
</AnchorButton>
)}
</Shelf>
</Stack>
)
}
20 changes: 20 additions & 0 deletions centrifuge-app/src/pages/IssuerCreatePool/PoolRatingInput.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { Grid, Stack, Text, TextInput } from '@centrifuge/fabric'
import { FieldWithErrorMessage } from '../../components/FieldWithErrorMessage'

export function PoolRatingInput() {
return (
<Stack gap={2}>
<Text variant="heading2">Pool rating</Text>
<Grid columns={[1, 2]} equalColumns gap={2} rowGap={3}>
<FieldWithErrorMessage name="ratingAgency" as={TextInput} label="Rating agency" placeholder="Agency Name..." />
<FieldWithErrorMessage name="ratingValue" as={TextInput} label="Rating" placeholder="Rating value..." />
<FieldWithErrorMessage
name="ratingReportUrl"
as={TextInput}
label="Rating report URL"
placeholder="https://..."
/>
</Grid>
</Stack>
)
}
88 changes: 44 additions & 44 deletions centrifuge-app/src/pages/IssuerCreatePool/PoolReportsInput.tsx
Original file line number Diff line number Diff line change
@@ -1,53 +1,53 @@
import { FileUpload, Grid, TextInput } from '@centrifuge/fabric'
import { FileUpload, Grid, Stack, Text, TextInput } from '@centrifuge/fabric'
import { Field, FieldProps } from 'formik'
import { FieldWithErrorMessage } from '../../components/FieldWithErrorMessage'
import { combineAsync, imageFile, maxFileSize, maxImageSize } from '../../utils/validation'
import { validate } from './validate'

export function PoolReportsInput() {
return (
<Grid columns={[1, 2]} equalColumns gap={2} rowGap={3}>
<FieldWithErrorMessage
name="reportUrl"
as={TextInput}
label="Report URL"
placeholder="https://..."
validate={validate.websiteNotRequired}
/>
<FieldWithErrorMessage
name="reportAuthorName"
as={TextInput}
label="Reviewer name"
placeholder="Name..."
maxLength={100}
/>
<FieldWithErrorMessage
name="reportAuthorTitle"
as={TextInput}
label="Reviewer job title"
placeholder="Title..."
maxLength={100}
/>
<Field
name="reportAuthorAvatar"
validate={combineAsync(imageFile(), maxFileSize(1024 ** 2), maxImageSize(200, 200))}
>
{({ field, meta, form }: FieldProps) => (
<FileUpload
file={field.value}
onFileChange={(file) => {
form.setFieldTouched('reportAuthorAvatar', true, false)
form.setFieldValue('reportAuthorAvatar', file)
}}
label="Reviewer avatar (JPG/PNG/SVG, max 40x40px)"
errorMessage={meta.touched && meta.error ? meta.error : undefined}
accept="image/*"
/>
)}
</Field>
<FieldWithErrorMessage name="ratingAgency" as={TextInput} label="Rating agency" placeholder="Agency Name..." />
<FieldWithErrorMessage name="ratingValue" as={TextInput} label="Rating" placeholder="Rating value..." />
<FieldWithErrorMessage name="ratingReport" as={TextInput} label="Rating report URL" placeholder="https://..." />
</Grid>
<Stack gap={2}>
<Text variant="heading2">Pool analysis</Text>
<Grid columns={[1, 2]} equalColumns gap={2} rowGap={3}>
<FieldWithErrorMessage
name="reportUrl"
as={TextInput}
label="Report URL"
placeholder="https://..."
validate={validate.websiteNotRequired}
/>
<FieldWithErrorMessage
name="reportAuthorName"
as={TextInput}
label="Reviewer name"
placeholder="Name..."
maxLength={100}
/>
<FieldWithErrorMessage
name="reportAuthorTitle"
as={TextInput}
label="Reviewer job title"
placeholder="Title..."
maxLength={100}
/>
<Field
name="reportAuthorAvatar"
validate={combineAsync(imageFile(), maxFileSize(1024 ** 2), maxImageSize(200, 200))}
>
{({ field, meta, form }: FieldProps) => (
<FileUpload
file={field.value}
onFileChange={(file) => {
form.setFieldTouched('reportAuthorAvatar', true, false)
form.setFieldValue('reportAuthorAvatar', file)
}}
label="Reviewer avatar (JPG/PNG/SVG, max 40x40px)"
errorMessage={meta.touched && meta.error ? meta.error : undefined}
accept="image/*"
/>
)}
</Field>
</Grid>
</Stack>
)
}
20 changes: 14 additions & 6 deletions centrifuge-app/src/pages/IssuerCreatePool/TrancheInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -67,28 +67,36 @@ export function TrancheSection() {
</Button>
}
>
<TrancheInput canRemove />
<TrancheInput canRemove poolName={values.poolName} />
</PageSection>
)}
</FieldArray>
)
}

export function TrancheInput({ canRemove, isUpdating }: { canRemove?: boolean; isUpdating?: boolean }) {
export function TrancheInput({
canRemove,
isUpdating,
poolName,
}: {
canRemove?: boolean
isUpdating?: boolean
poolName?: string
}) {
const fmk = useFormikContext<PoolMetadataInput>()
const { values } = fmk

const getTrancheName = (index: number) => {
if (values.tranches.length === 1) {
return values.poolName
return poolName
}
switch (index) {
case 0:
return `${values.poolName} Junior`
return `${poolName} Junior`
case 1:
return values.tranches.length === 2 ? `${values.poolName} Senior` : `${values.poolName} Mezzanine`
return values.tranches.length === 2 ? `${poolName} Senior` : `${poolName} Mezzanine`
case 2:
return `${values.poolName} Senior`
return `${poolName} Senior`
default:
return ''
}
Expand Down
Loading
Loading