@swan-io/shared-business 13.1.8 → 13.1.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@swan-io/shared-business",
3
- "version": "13.1.8",
3
+ "version": "13.1.10",
4
4
  "engines": {
5
5
  "node": "^22.12.0"
6
6
  },
@@ -25,7 +25,7 @@
25
25
  ],
26
26
  "license": "MIT",
27
27
  "peerDependencies": {
28
- "@swan-io/lake": "13.1.8"
28
+ "@swan-io/lake": "13.1.10"
29
29
  },
30
30
  "dependencies": {
31
31
  "@formatjs/intl": "^3.1.6",
@@ -50,6 +50,6 @@
50
50
  "@types/react-dom": "^19.1.7",
51
51
  "@types/react-native": "^0.72.8",
52
52
  "type-fest": "^4.41.0",
53
- "@swan-io/lake": "13.1.8"
53
+ "@swan-io/lake": "13.1.10"
54
54
  }
55
55
  }
@@ -39,8 +39,9 @@ type CreditStatementV1Props = {
39
39
  issuerName: string;
40
40
  siret: string;
41
41
  repaymentDate: string;
42
+ closingBalance: Amount;
42
43
  };
43
- export declare const CreditStatementV1: ({ partnerLogoUrl, accountHolderType, accountHolderName, accountHolderAddress, iban, bic, openingDate, closingDate, transactions, issueDate, issuerName, siret, repaymentDate, }: CreditStatementV1Props) => import("react/jsx-runtime").JSX.Element;
44
+ export declare const CreditStatementV1: ({ partnerLogoUrl, accountHolderType, accountHolderName, accountHolderAddress, iban, bic, openingDate, closingDate, transactions, issueDate, issuerName, siret, repaymentDate, closingBalance, }: CreditStatementV1Props) => import("react/jsx-runtime").JSX.Element;
44
45
  export type CreditStatementProps = CreditStatementV1Props;
45
46
  export declare const CreditStatement: (props: CreditStatementProps) => import("react/jsx-runtime").JSX.Element;
46
47
  export {};
@@ -11,7 +11,7 @@ import { printFormat } from "iban";
11
11
  import { StyleSheet } from "react-native";
12
12
  import { match } from "ts-pattern";
13
13
  import { getCountryName } from "../constants/countries";
14
- import { formatNestedMessage, locale, t } from "../utils/i18n";
14
+ import { formatCurrencySymbol, formatNestedMessage, locale, t } from "../utils/i18n";
15
15
  const LOGO_MAX_HEIGHT = 24;
16
16
  const LOGO_MAX_WIDTH = 150;
17
17
  const styles = StyleSheet.create({
@@ -33,6 +33,14 @@ const styles = StyleSheet.create({
33
33
  fontSize: 18,
34
34
  fontWeight: "400",
35
35
  },
36
+ closingBalanceLabel: {
37
+ fontSize: 15,
38
+ fontWeight: "500",
39
+ },
40
+ closingBalanceAmount: {
41
+ fontSize: 15,
42
+ fontWeight: "400",
43
+ },
36
44
  pageTitle: {
37
45
  fontSize: 20,
38
46
  color: colors.swan[500],
@@ -88,7 +96,7 @@ const logoStyle = {
88
96
  objectPosition: "left",
89
97
  };
90
98
  const formatMaskedPan = (value) => `••${value.slice(-4)}`;
91
- export const CreditStatementV1 = ({ partnerLogoUrl, accountHolderType, accountHolderName, accountHolderAddress, iban, bic, openingDate, closingDate, transactions, issueDate, issuerName, siret, repaymentDate, }) => {
99
+ export const CreditStatementV1 = ({ partnerLogoUrl, accountHolderType, accountHolderName, accountHolderAddress, iban, bic, openingDate, closingDate, transactions, issueDate, issuerName, siret, repaymentDate, closingBalance, }) => {
92
100
  return (_jsxs(Box, { style: styles.container, direction: "column", justifyContent: "spaceBetween", children: [_jsxs(Box, { direction: "row", justifyContent: "spaceBetween", alignItems: "start", children: [_jsxs(Box, { direction: "row", alignItems: "center", children: [isNotNullishOrEmpty(partnerLogoUrl) ? (_jsx("img", { src: partnerLogoUrl, style: logoStyle })) : (_jsx(SwanLogo, { style: styles.defaultLogo })), _jsx(Separator, { horizontal: true, space: 8 }), _jsx(LakeText, { style: styles.mediumText, children: t("accountStatement.partnership") }), _jsx(Space, { width: 4 }), _jsx(SwanLogo, { color: colors.gray[900], style: styles.swanLogo })] }), _jsxs(Box, { children: [_jsx(LakeText, { style: styles.pageTitle, children: t("creditStatement.titleDocument") }), _jsx(LakeText, { style: styles.text, color: colors.gray[700], children: accountHolderType === "Company"
93
101
  ? t("accountStatement.titleDocument.companyDescription")
94
102
  : t("accountStatement.titleDocument.individualDescription") }), _jsx(LakeText, { align: "right", style: styles.smallText, children: t("creditStatement.issueDate", {
@@ -101,7 +109,7 @@ export const CreditStatementV1 = ({ partnerLogoUrl, accountHolderType, accountHo
101
109
  }) }), _jsx(Space, { height: 24 }), _jsx(Box, { direction: "row", justifyContent: "spaceBetween", children: _jsx(Box, { direction: "column", justifyContent: "end", children: _jsx(LakeText, { style: styles.subtitle, color: colors.gray[900], children: t("accountStatement.date", {
102
110
  openingDate: dayjs(openingDate).format(locale.dateFormat),
103
111
  closingDate: dayjs(closingDate).format(locale.dateFormat),
104
- }) }) }) }), _jsx(Space, { height: 16 }), _jsxs(_Fragment, { children: [_jsxs(Box, { direction: "row", children: [_jsx(LakeText, { style: [styles.titleColumn, { width: "15%" }], color: colors.gray[700], children: t("accountStatement.column.date") }), _jsx(LakeText, { style: [styles.titleColumn, { width: "55%" }], color: colors.gray[700], children: t("accountStatement.column.description") }), _jsx(LakeText, { style: [styles.titleColumn, { width: "15%", textAlign: "right" }], color: colors.gray[700], children: t("creditStatement.column.debit") }), _jsx(LakeText, { style: [styles.titleColumn, { width: "15%", textAlign: "right" }], color: colors.gray[700], children: t("creditStatement.column.credit") })] }), _jsx(Space, { height: 12 }), _jsx(Box, { direction: "column", children: transactions.map(({ transactionAmount, transactionDate, transactionLabel, transactionSide, transactionType, transactionId, counterparty, maskedPan, }) => (_jsxs(_Fragment, { children: [_jsxs(Box, { direction: "row", children: [_jsx(LakeText, { style: [styles.smallText, { width: "15%" }], color: colors.gray[700], children: dayjs(transactionDate).format(locale.dateFormat) }), _jsxs(Box, { direction: "column", style: { width: "55%" }, children: [_jsx(LakeText, { style: styles.smallText, color: colors.gray[700], children: `${translateTransaction(transactionType)} ${transactionLabel}` }), _jsx(LakeText, { style: styles.smallText, color: colors.gray[500], children: `${counterparty} ${maskedPan ? formatMaskedPan(maskedPan) : ""}` })] }), _jsx(LakeText, { color: colors.gray[700], style: [styles.smallText, styles.rightColumnContent], children: transactionSide === "Debit" ? `- ${transactionAmount.value}` : "" }), _jsx(LakeText, { color: colors.gray[700], style: [styles.smallText, styles.rightColumnContent], children: transactionSide === "Credit" ? transactionAmount.value : "" })] }, transactionId), _jsx(Space, { height: 8 })] }))) })] }), _jsx(Space, { height: 24 })] }));
112
+ }) }) }) }), _jsx(Space, { height: 16 }), _jsxs(_Fragment, { children: [_jsxs(Box, { direction: "row", children: [_jsx(LakeText, { style: [styles.titleColumn, { width: "15%" }], color: colors.gray[700], children: t("accountStatement.column.date") }), _jsx(LakeText, { style: [styles.titleColumn, { width: "55%" }], color: colors.gray[700], children: t("accountStatement.column.description") }), _jsx(LakeText, { style: [styles.titleColumn, { width: "15%", textAlign: "right" }], color: colors.gray[700], children: t("creditStatement.column.debit") }), _jsx(LakeText, { style: [styles.titleColumn, { width: "15%", textAlign: "right" }], color: colors.gray[700], children: t("creditStatement.column.credit") })] }), _jsx(Space, { height: 12 }), _jsxs(Box, { direction: "column", children: [transactions.map(({ transactionAmount, transactionDate, transactionLabel, transactionSide, transactionType, transactionId, counterparty, maskedPan, }) => (_jsxs(_Fragment, { children: [_jsxs(Box, { direction: "row", children: [_jsx(LakeText, { style: [styles.smallText, { width: "15%" }], color: colors.gray[700], children: dayjs(transactionDate).format(locale.dateFormat) }), _jsxs(Box, { direction: "column", style: { width: "55%" }, children: [_jsx(LakeText, { style: styles.smallText, color: colors.gray[700], children: `${translateTransaction(transactionType)} ${transactionLabel}` }), _jsx(LakeText, { style: styles.smallText, color: colors.gray[500], children: `${counterparty} ${maskedPan ? formatMaskedPan(maskedPan) : ""}` })] }), _jsx(LakeText, { color: colors.gray[700], style: [styles.smallText, styles.rightColumnContent], children: transactionSide === "Debit" ? `- ${transactionAmount.value}` : "" }), _jsx(LakeText, { color: colors.gray[700], style: [styles.smallText, styles.rightColumnContent], children: transactionSide === "Credit" ? transactionAmount.value : "" })] }, transactionId), _jsx(Space, { height: 8 })] }))), _jsx(Box, { direction: "row", justifyContent: "end", children: _jsxs(Box, { direction: "row", justifyContent: "spaceBetween", alignItems: "center", style: { width: "50%" }, children: [_jsx(LakeText, { style: styles.closingBalanceLabel, color: colors.gray[900], children: t("accountStatement.closingBalance") }), _jsx(LakeText, { color: colors.gray[900], style: styles.closingBalanceAmount, children: formatCurrencySymbol(Number(closingBalance.value), closingBalance.currency) })] }) })] })] }), _jsx(Space, { height: 24 })] }));
105
113
  };
106
114
  export const CreditStatement = (props) => match(props)
107
115
  .with({ version: "v1" }, props => _jsx(CreditStatementV1, { ...props }))
@@ -1,5 +1,6 @@
1
- import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Array, Option } from "@swan-io/boxed";
3
+ import { Box } from "@swan-io/lake/src/components/Box";
3
4
  import { Form } from "@swan-io/lake/src/components/Form";
4
5
  import { LakeButton, LakeButtonGroup } from "@swan-io/lake/src/components/LakeButton";
5
6
  import { LakeCopyButton } from "@swan-io/lake/src/components/LakeCopyButton";
@@ -10,7 +11,7 @@ import { ReadOnlyFieldList } from "@swan-io/lake/src/components/ReadOnlyFieldLis
10
11
  import { Space } from "@swan-io/lake/src/components/Space";
11
12
  import { colors } from "@swan-io/lake/src/constants/design";
12
13
  import { isNotNullish } from "@swan-io/lake/src/utils/nullish";
13
- import { Request, badStatusToError } from "@swan-io/request";
14
+ import { badStatusToError, Request } from "@swan-io/request";
14
15
  import { Fragment, useEffect, useImperativeHandle, useMemo, useRef, useState } from "react";
15
16
  import { StyleSheet } from "react-native";
16
17
  import { match } from "ts-pattern";
@@ -30,10 +31,17 @@ const Help = (props) => {
30
31
  })
31
32
  .with({ type: "button" }, ({ label, onPress }) => {
32
33
  var _a;
33
- return (_jsx(LakeButton, { mode: "tertiary", size: "small", color: "gray", icon: (_a = props.icon) !== null && _a !== void 0 ? _a : "question-circle-regular", onPress: onPress, style: styles.button, ariaLabel: t("supportingDocuments.help.whatIsThis"), children: label }));
34
+ return (_jsx(LakeButton, { mode: "secondary", size: "small", color: "gray", icon: (_a = props.icon) !== null && _a !== void 0 ? _a : "question-circle-regular", onPress: onPress, style: styles.button, ariaLabel: t("supportingDocuments.help.whatIsThis"), children: label }));
34
35
  })
35
36
  .exhaustive();
36
37
  };
38
+ const getSupportLink = (language) => match(language)
39
+ .with("fr", () => "https://support.swan.io/hc/en-150/articles/22502977563933--Proof-of-registration-for-French-companies")
40
+ .with("it", () => "https://support.swan.io/hc/en-150/articles/22537604831005--Proof-of-registration-for-Italian-companies")
41
+ .with("de", () => "https://support.swan.io/hc/en-150/articles/22535023588509--Proof-of-registration-for-German-companies")
42
+ .with("es", () => "https://support.swan.io/hc/en-150/articles/22544703221021--Proof-of-registration-for-Spanish-companies")
43
+ .with("nl", () => "https://support.swan.io/hc/en-150/articles/22543228421277--Proof-of-registration-for-Dutch-companies")
44
+ .otherwise(() => "https://support.swan.io/hc/en-150/articles/22620756787869-Proof-of-company-registration");
37
45
  export const getSupportingDocumentPurposeLabel = (purpose) => {
38
46
  const key = `supportingDocuments.purpose.${purpose}`;
39
47
  return isTranslationKey(key) ? t(key) : purpose;
@@ -124,37 +132,41 @@ export const SupportingDocumentCollection = ({ ref, documents, generateUpload, u
124
132
  var _a, _b;
125
133
  const metadata = getPurposeMetadata === null || getPurposeMetadata === void 0 ? void 0 : getPurposeMetadata(purpose);
126
134
  const label = (_a = purposeDescriptionLabelOverrides === null || purposeDescriptionLabelOverrides === void 0 ? void 0 : purposeDescriptionLabelOverrides[purpose]) !== null && _a !== void 0 ? _a : getSupportingDocumentPurposeDescriptionLabel(purpose);
127
- return (_jsxs(Fragment, { children: [_jsx(LakeLabel, { label: (_b = purposeLabelOverrides === null || purposeLabelOverrides === void 0 ? void 0 : purposeLabelOverrides[purpose]) !== null && _b !== void 0 ? _b : getSupportingDocumentPurposeLabel(purpose), description: label, help: isNotNullish(metadata) ? (_jsx(Help, { type: "button", label: metadata.title, onPress: () => setCurrentMetadata(metadata) })) : (match(purpose)
135
+ return (_jsxs(Fragment, { children: [_jsx(LakeLabel, { label: (_b = purposeLabelOverrides === null || purposeLabelOverrides === void 0 ? void 0 : purposeLabelOverrides[purpose]) !== null && _b !== void 0 ? _b : getSupportingDocumentPurposeLabel(purpose), description: label, help: isNotNullish(metadata) ? (purpose === "CompanyRegistration" ? (_jsx(LakeButton, { size: "small", mode: "tertiary", icon: "question-circle-regular", onPress: () => window.open(getSupportLink(locale.language)), ariaLabel: t("supportingDocuments.help.howToSendAGoodDocument"), children: t("supportingDocuments.help.howToSendAGoodDocument") })) : (_jsx(Help, { type: "button", label: metadata.title, onPress: () => setCurrentMetadata(metadata) }))) : (match(purpose)
128
136
  .with("PowerOfAttorney", () => (_jsx(Help, { type: "button", icon: "arrow-down-filled", label: t("supportingDocuments.help.downloadTemplate"), onPress: () => setShowPowerOfAttorneyModal(true) })))
129
137
  .with("SwornStatement", () => (_jsx(Help, { type: "button", icon: "arrow-down-filled", label: t("supportingDocuments.help.downloadTemplate"), onPress: () => setShowSwornStatementModal(true) })))
130
- .otherwise(() => null)), render: () => (_jsx(FilesUploader, { ref: ref => {
131
- filesUploaderRefByPurpose.current[purpose] = ref;
132
- },
133
- // Only allow uploading is the Supporting Document Collection awaits for docs
134
- // and that the specific purpose isn't already fully validated
135
- canUpload: !readonlyDocumentPurposes.includes(purpose) &&
136
- !readOnly &&
137
- status === "WaitingForDocument" &&
138
- !areAllDocumentsValidated, accept: ACCEPTED_FORMATS, maxSize: 20000000, icon: "document-regular", initialFiles: files, generateUpload: generateUpload, getUploadConfig: file => ({ fileName: file.name, purpose }), uploadFile: isNotNullish(uploadFile)
139
- ? uploadFile
140
- : ({ upload, file, onProgress }) => {
141
- const body = new FormData();
142
- upload.fields.forEach(({ key, value }) => body.append(key, value));
143
- body.append("file", file);
144
- setTimeout(() => onProgress(0.8), 100);
145
- return Request.make({
146
- url: upload.url,
147
- method: "POST",
148
- body,
149
- type: "text",
150
- }).mapOkToResult(badStatusToError);
151
- }, formatAndSizeDescription: t("supportingDocuments.documentTypes", {
152
- maxSizeMB: 20000000 / 1000000,
153
- }), onRemoveFile: readOnly ? undefined : onRemoveFile, onChange: files => {
154
- if (isRequired) {
155
- filesByRequiredPurpose.current.set(purpose, files);
156
- }
157
- }, showIds: showIds })) }), _jsx(Space, { height: 24 })] }, purpose));
138
+ .with("CompanyRegistration", () => (_jsx(LakeButton, { size: "small", mode: "tertiary", icon: "question-circle-regular", onPress: () => window.open(getSupportLink(locale.language)), ariaLabel: t("supportingDocuments.help.howToSendAGoodDocument"), children: t("supportingDocuments.help.howToSendAGoodDocument") })))
139
+ .otherwise(() => null)), render: () => (_jsxs(_Fragment, { children: [_jsx(Box, { direction: "row", children: match(purpose)
140
+ .with("PowerOfAttorney", () => (_jsx(Help, { type: "button", icon: "arrow-down-filled", label: t("supportingDocuments.help.downloadTemplate"), onPress: () => setShowPowerOfAttorneyModal(true) })))
141
+ .with("SwornStatement", () => (_jsx(Help, { type: "button", icon: "arrow-down-filled", label: t("supportingDocuments.help.downloadTemplate"), onPress: () => setShowSwornStatementModal(true) })))
142
+ .otherwise(() => null) }), _jsx(Space, { height: 16 }), _jsx(FilesUploader, { ref: ref => {
143
+ filesUploaderRefByPurpose.current[purpose] = ref;
144
+ },
145
+ // Only allow uploading is the Supporting Document Collection awaits for docs
146
+ // and that the specific purpose isn't already fully validated
147
+ canUpload: !readonlyDocumentPurposes.includes(purpose) &&
148
+ !readOnly &&
149
+ status === "WaitingForDocument" &&
150
+ !areAllDocumentsValidated, accept: ACCEPTED_FORMATS, maxSize: 20000000, icon: "document-regular", initialFiles: files, generateUpload: generateUpload, getUploadConfig: file => ({ fileName: file.name, purpose }), uploadFile: isNotNullish(uploadFile)
151
+ ? uploadFile
152
+ : ({ upload, file, onProgress }) => {
153
+ const body = new FormData();
154
+ upload.fields.forEach(({ key, value }) => body.append(key, value));
155
+ body.append("file", file);
156
+ setTimeout(() => onProgress(0.8), 100);
157
+ return Request.make({
158
+ url: upload.url,
159
+ method: "POST",
160
+ body,
161
+ type: "text",
162
+ }).mapOkToResult(badStatusToError);
163
+ }, formatAndSizeDescription: t("supportingDocuments.documentTypes", {
164
+ maxSizeMB: 20000000 / 1000000,
165
+ }), onRemoveFile: readOnly ? undefined : onRemoveFile, onChange: files => {
166
+ if (isRequired) {
167
+ filesByRequiredPurpose.current.set(purpose, files);
168
+ }
169
+ }, showIds: showIds })] })) }), _jsx(Space, { height: 24 })] }, purpose));
158
170
  }), showableDocumentPurposes.length === 0 ? (_jsxs(_Fragment, { children: [_jsx(Space, { height: 24 }), _jsx(LakeText, { align: "center", children: t("supportingDocuments.noDocuments") }), _jsx(Space, { height: 24 })] })) : null, _jsxs(LakeModal, { visible: showPowerOfAttorneyModal, title: t("supportingDocuments.powerOfAttorneyModal.title"), icon: "document-regular", onPressClose: () => setShowPowerOfAttorneyModal(false), children: [_jsx(LakeText, { children: t("supportingDocuments.powerOfAttorneyModal.description") }), _jsx(Space, { height: 16 }), _jsx(LakeButtonGroup, { paddingBottom: 0, children: _jsx(LakeButton, { grow: true, color: "current", onPress: () => window.open(`/power-of-attorney-template/${match(templateLanguage)
159
171
  .with("fr", () => "fr")
160
172
  .with("de", () => "de")
@@ -298,6 +298,7 @@
298
298
  "supportingDocuments.help.whatIsThis": "What is this?",
299
299
  "supportingDocuments.informations": "Document details",
300
300
  "supportingDocuments.noDocuments": "No documents.",
301
+ "supportingDocuments.help.howToSendAGoodDocument": "How to send a good document?",
301
302
  "supportingDocuments.powerOfAttorneyModal.description": "A signed and dated document authorizing an individual to represent or act on behalf of a company's legal representative.",
302
303
  "supportingDocuments.powerOfAttorneyModal.title": "Power of attorney signed by the legal representative",
303
304
  "supportingDocuments.purpose.AdministratorDecisionOfAppointment": "Administrator appointment letter",