|
| 1 | +import { type PropertyForSchema } from '@app-builder/constants/sanction-check-entity'; |
1 | 2 | import { |
2 | | - createPropertyTransformer, |
3 | | - getSanctionEntityProperties, |
4 | | - type SanctionCheckEntityProperty, |
5 | | -} from '@app-builder/constants/sanction-check-entity'; |
6 | | -import { type SanctionCheckMatch } from '@app-builder/models/sanction-check'; |
7 | | -import { useFormatLanguage } from '@app-builder/utils/format'; |
8 | | -import { Fragment, useMemo, useState } from 'react'; |
| 3 | + type SanctionCheckMatch, |
| 4 | + type SanctionCheckSanctionEntity, |
| 5 | +} from '@app-builder/models/sanction-check'; |
| 6 | +import { useState } from 'react'; |
9 | 7 | import { useTranslation } from 'react-i18next'; |
| 8 | +import { ModalV2 } from 'ui-design-system'; |
| 9 | +import { Icon } from 'ui-icons'; |
10 | 10 |
|
| 11 | +import { EntityProperties } from './EntityProperties'; |
11 | 12 | import { sanctionsI18n } from './sanctions-i18n'; |
12 | 13 |
|
13 | 14 | export type MatchDetailsProps = { |
14 | 15 | entity: SanctionCheckMatch['payload']; |
15 | 16 | }; |
16 | 17 |
|
17 | | -export function MatchDetails({ entity }: MatchDetailsProps) { |
18 | | - const { t, i18n } = useTranslation(sanctionsI18n); |
19 | | - const language = useFormatLanguage(); |
20 | | - const [displayAll, setDisplayAll] = useState< |
21 | | - Partial<Record<SanctionCheckEntityProperty, boolean>> |
22 | | - >({}); |
| 18 | +const sanctionProps = [ |
| 19 | + 'country', |
| 20 | + 'authority', |
| 21 | + 'authorityId', |
| 22 | + 'startDate', |
| 23 | + 'endDate', |
| 24 | + 'listingDate', |
| 25 | + 'program', |
| 26 | + 'programId', |
| 27 | + 'programUrl', |
| 28 | + 'summary', |
| 29 | + 'reason', |
| 30 | + 'sourceUrl', |
| 31 | +] satisfies PropertyForSchema<'Sanction'>[]; |
23 | 32 |
|
24 | | - const TransformProperty = useMemo( |
25 | | - () => |
26 | | - createPropertyTransformer({ |
27 | | - language: i18n.language, |
28 | | - formatLanguage: language, |
29 | | - }), |
30 | | - [i18n.language, language], |
| 33 | +export function MatchDetails({ entity }: MatchDetailsProps) { |
| 34 | + const { t } = useTranslation(sanctionsI18n); |
| 35 | + const [selectedSanction, setSelectedSanction] = useState<SanctionCheckSanctionEntity | null>( |
| 36 | + null, |
31 | 37 | ); |
32 | 38 |
|
33 | | - const displayProperties = getSanctionEntityProperties(entity.schema); |
34 | | - const entityPropertyList = displayProperties |
35 | | - .map((property) => { |
36 | | - const items = entity.properties[property] ?? []; |
37 | | - const itemsToDisplay = displayAll[property] ? items : items.slice(0, 5); |
38 | | - return { |
39 | | - property, |
40 | | - values: itemsToDisplay, |
41 | | - restItemsCount: Math.max(0, items.length - itemsToDisplay.length), |
42 | | - }; |
43 | | - }) |
44 | | - .filter((prop) => prop.values.length > 0); |
45 | | - |
46 | | - const handleShowMore = (prop: string) => { |
47 | | - setDisplayAll((prev) => ({ ...prev, [prop]: true })); |
48 | | - }; |
49 | | - |
50 | 39 | return ( |
51 | | - <div className="grid grid-cols-[168px,_1fr] gap-2"> |
52 | | - {entityPropertyList.map(({ property, values, restItemsCount }) => { |
53 | | - return ( |
54 | | - <Fragment key={property}> |
55 | | - <span className="font-bold">{t(`sanctions:entity.property.${property}`)}</span> |
56 | | - <span className="flex flex-wrap gap-1 break-all"> |
57 | | - {values.map((v, i) => ( |
58 | | - <Fragment key={i}> |
59 | | - <TransformProperty property={property} value={v} /> |
60 | | - {i === values.length - 1 ? null : <span>·</span>} |
61 | | - </Fragment> |
62 | | - ))} |
63 | | - {restItemsCount > 0 ? ( |
64 | | - <> |
65 | | - <span>·</span> |
66 | | - <button |
67 | | - onClick={(e) => { |
68 | | - e.preventDefault(); |
69 | | - handleShowMore(property); |
70 | | - }} |
71 | | - className="text-purple-65 font-semibold" |
| 40 | + <div className="flex flex-col gap-4"> |
| 41 | + <EntityProperties |
| 42 | + entity={entity} |
| 43 | + after={ |
| 44 | + entity.properties.sanctions ? ( |
| 45 | + <> |
| 46 | + <span className="font-bold">{t('sanctions:entity.property.sanctions')}</span> |
| 47 | + <div className="flex flex-col gap-2"> |
| 48 | + {entity.properties.sanctions.map((sanction) => ( |
| 49 | + <div |
| 50 | + key={sanction.id} |
| 51 | + className="group/sanction bg-grey-100 grid grid-cols-[1fr_20px] gap-2 rounded p-2" |
72 | 52 | > |
73 | | - + {restItemsCount} more |
74 | | - </button> |
75 | | - </> |
76 | | - ) : null} |
77 | | - </span> |
78 | | - </Fragment> |
79 | | - ); |
80 | | - })} |
| 53 | + <span className="truncate">{sanction.properties['authority']}</span> |
| 54 | + <button type="button" onClick={() => setSelectedSanction(sanction)}> |
| 55 | + <Icon |
| 56 | + icon="visibility-on" |
| 57 | + className="text-grey-90 hover:text-purple-65 size-5 cursor-pointer" |
| 58 | + /> |
| 59 | + </button> |
| 60 | + </div> |
| 61 | + ))} |
| 62 | + </div> |
| 63 | + |
| 64 | + <ModalV2.Content |
| 65 | + open={!!selectedSanction} |
| 66 | + onClose={() => setSelectedSanction(null)} |
| 67 | + size="large" |
| 68 | + className="max-h-[80vh]" |
| 69 | + > |
| 70 | + <ModalV2.Title>{t('sanctions:sanction_detail.title')}</ModalV2.Title> |
| 71 | + <div className="overflow-y-auto p-6"> |
| 72 | + {selectedSanction ? ( |
| 73 | + <EntityProperties entity={selectedSanction} forcedProperties={sanctionProps} /> |
| 74 | + ) : null} |
| 75 | + </div> |
| 76 | + </ModalV2.Content> |
| 77 | + </> |
| 78 | + ) : null |
| 79 | + } |
| 80 | + /> |
81 | 81 | </div> |
82 | 82 | ); |
83 | 83 | } |
0 commit comments