@swan-io/shared-business 1.0.0
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/HISTORY.md +3 -0
- package/LICENSE +21 -0
- package/README.md +1 -0
- package/package.json +46 -0
- package/src/components/CountryPicker.d.ts +20 -0
- package/src/components/CountryPicker.js +26 -0
- package/src/components/SkipToContent.d.ts +3 -0
- package/src/components/SkipToContent.js +35 -0
- package/src/components/SupportChat.d.ts +26 -0
- package/src/components/SupportChat.js +87 -0
- package/src/components/SupportingDocument.d.ts +33 -0
- package/src/components/SupportingDocument.js +166 -0
- package/src/components/UploadArea.d.ts +26 -0
- package/src/components/UploadArea.js +161 -0
- package/src/constants/business.d.ts +12 -0
- package/src/constants/business.js +36 -0
- package/src/constants/countries.d.ts +2002 -0
- package/src/constants/countries.js +2363 -0
- package/src/constants/legal.d.ts +3 -0
- package/src/constants/legal.js +9 -0
- package/src/constants/registrationNumbers.d.ts +14 -0
- package/src/constants/registrationNumbers.js +117 -0
- package/src/constants/termsAndConditions.d.ts +7 -0
- package/src/constants/termsAndConditions.js +28 -0
- package/src/constants/ubos.d.ts +6 -0
- package/src/constants/ubos.js +47 -0
- package/src/constants/uploads.d.ts +1 -0
- package/src/constants/uploads.js +2 -0
- package/src/third-party/Pappers.d.ts +8 -0
- package/src/third-party/Pappers.js +36 -0
- package/src/utils/date.d.ts +3 -0
- package/src/utils/date.js +13 -0
- package/src/utils/i18n.d.ts +21 -0
- package/src/utils/i18n.js +115 -0
- package/src/utils/languages.d.ts +6 -0
- package/src/utils/languages.js +44 -0
- package/src/utils/validation.d.ts +7 -0
- package/src/utils/validation.js +65 -0
package/HISTORY.md
ADDED
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2022 Swan
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# @swan-io/shared-business
|
package/package.json
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@swan-io/shared-business",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"engines": {
|
|
5
|
+
"node": ">=14.0.0",
|
|
6
|
+
"yarn": "^1.20.0"
|
|
7
|
+
},
|
|
8
|
+
"files": [
|
|
9
|
+
"LICENSE",
|
|
10
|
+
"src/**/*.js",
|
|
11
|
+
"src/**/*.d.ts",
|
|
12
|
+
"README.md",
|
|
13
|
+
"HISTORY.md"
|
|
14
|
+
],
|
|
15
|
+
"publishConfig": {
|
|
16
|
+
"access": "public",
|
|
17
|
+
"registry": "https://registry.npmjs.org"
|
|
18
|
+
},
|
|
19
|
+
"browserslist": [
|
|
20
|
+
">0.2%",
|
|
21
|
+
"not op_mini all",
|
|
22
|
+
"not dead",
|
|
23
|
+
"not ie <= 11",
|
|
24
|
+
"safari >= 12"
|
|
25
|
+
],
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"@formatjs/intl": "2.6.7",
|
|
28
|
+
"@swan-io/boxed": "0.12.1",
|
|
29
|
+
"@swan-io/lake": "1.0.0",
|
|
30
|
+
"react": "18.2.0",
|
|
31
|
+
"react-dom": "18.2.0",
|
|
32
|
+
"react-native-web": "0.18.12",
|
|
33
|
+
"react-ux-form": "1.3.0",
|
|
34
|
+
"ts-pattern": "4.2.1"
|
|
35
|
+
},
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"@testing-library/react": "13.4.0",
|
|
38
|
+
"@testing-library/user-event": "14.4.3",
|
|
39
|
+
"@types/react": "18.0.28",
|
|
40
|
+
"@types/react-dom": "18.0.11",
|
|
41
|
+
"@types/react-native": "0.70.11",
|
|
42
|
+
"@types/uuid": "9.0.1",
|
|
43
|
+
"jsdom": "21.1.0",
|
|
44
|
+
"vitest": "0.29.2"
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/// <reference types="react" />
|
|
2
|
+
import { CountryCCA2, CountryCCA3 } from "../constants/countries";
|
|
3
|
+
export type CountryItem<T extends CountryCCA3> = {
|
|
4
|
+
cca3: T;
|
|
5
|
+
cca2?: CountryCCA2;
|
|
6
|
+
name: string;
|
|
7
|
+
};
|
|
8
|
+
type Props<T extends CountryCCA3> = {
|
|
9
|
+
onValueChange: (country: T) => void;
|
|
10
|
+
value: T | undefined;
|
|
11
|
+
items: CountryItem<T>[];
|
|
12
|
+
error?: string;
|
|
13
|
+
placeholder?: string;
|
|
14
|
+
readOnly?: boolean;
|
|
15
|
+
nativeID?: string;
|
|
16
|
+
disabled?: boolean;
|
|
17
|
+
hideErrors?: boolean;
|
|
18
|
+
};
|
|
19
|
+
export declare function CountryPicker<T extends CountryCCA3>({ onValueChange, value, items, readOnly, nativeID, error, placeholder, disabled, hideErrors, }: Props<T>): JSX.Element;
|
|
20
|
+
export {};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { Flag } from "@swan-io/lake/src/components/Flag";
|
|
3
|
+
import { LakeSelect } from "@swan-io/lake/src/components/LakeSelect";
|
|
4
|
+
import { useMemo } from "react";
|
|
5
|
+
export function CountryPicker({ onValueChange, value, items, readOnly, nativeID, error, placeholder, disabled, hideErrors, }) {
|
|
6
|
+
const countries = useMemo(() => {
|
|
7
|
+
const hasIntl = "Intl" in window && "DisplayNames" in window.Intl;
|
|
8
|
+
const countryResolver = hasIntl && Intl.DisplayNames.supportedLocalesOf(["en"]).length
|
|
9
|
+
? new Intl.DisplayNames(["en"], { type: "region" })
|
|
10
|
+
: undefined;
|
|
11
|
+
const seen = new Set();
|
|
12
|
+
return items
|
|
13
|
+
.filter(item => {
|
|
14
|
+
const hasBeenSeen = seen.has(item.cca3);
|
|
15
|
+
seen.add(item.cca3);
|
|
16
|
+
return !hasBeenSeen;
|
|
17
|
+
})
|
|
18
|
+
.map(country => ({
|
|
19
|
+
name: countryResolver?.of(country.cca2 ?? "") ?? country.name,
|
|
20
|
+
icon: _jsx(Flag, { width: 14, icon: country.cca3 }),
|
|
21
|
+
value: country.cca3,
|
|
22
|
+
}))
|
|
23
|
+
.sort((a, b) => a.name.localeCompare(b.name));
|
|
24
|
+
}, [items]);
|
|
25
|
+
return (_jsx(LakeSelect, { readOnly: readOnly, nativeID: nativeID, error: error, items: countries, placeholder: placeholder, value: value, onValueChange: onValueChange, disabled: disabled, hideErrors: hideErrors }));
|
|
26
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { LakeButton } from "@swan-io/lake/src/components/LakeButton";
|
|
3
|
+
import { animations } from "@swan-io/lake/src/constants/design";
|
|
4
|
+
import { useCallback, useEffect, useState } from "react";
|
|
5
|
+
import { StyleSheet } from "react-native";
|
|
6
|
+
import { t } from "../utils/i18n";
|
|
7
|
+
const styles = StyleSheet.create({
|
|
8
|
+
button: {
|
|
9
|
+
position: "absolute",
|
|
10
|
+
top: -100,
|
|
11
|
+
left: 10,
|
|
12
|
+
zIndex: 1000,
|
|
13
|
+
},
|
|
14
|
+
focused: {
|
|
15
|
+
top: 10,
|
|
16
|
+
...animations.fadeAndSlideInFromTop.enter,
|
|
17
|
+
},
|
|
18
|
+
});
|
|
19
|
+
export const CONTENT_ID = "content";
|
|
20
|
+
export const SkipToContent = () => {
|
|
21
|
+
const [contentElement, setContentElement] = useState(null);
|
|
22
|
+
useEffect(() => {
|
|
23
|
+
const element = document.querySelector(`#${CONTENT_ID}`);
|
|
24
|
+
setContentElement(element);
|
|
25
|
+
}, []);
|
|
26
|
+
const onPress = useCallback(() => {
|
|
27
|
+
if (contentElement != null) {
|
|
28
|
+
contentElement.focus();
|
|
29
|
+
}
|
|
30
|
+
}, [contentElement]);
|
|
31
|
+
if (contentElement == null) {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
return (_jsx(LakeButton, { style: ({ focused }) => [styles.button, focused && styles.focused], onPress: onPress, color: "current", children: t("common.skipToContent") }));
|
|
35
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { ReactElement } from "react";
|
|
2
|
+
declare const keys: {
|
|
3
|
+
"end-user": string;
|
|
4
|
+
partner: string;
|
|
5
|
+
};
|
|
6
|
+
export type AdditionalInfo = {
|
|
7
|
+
firstName?: string;
|
|
8
|
+
lastName?: string;
|
|
9
|
+
email?: string;
|
|
10
|
+
phoneNumber?: string;
|
|
11
|
+
userId?: string;
|
|
12
|
+
onboardingId?: string;
|
|
13
|
+
membershipId?: string;
|
|
14
|
+
environment?: string;
|
|
15
|
+
projectName?: string;
|
|
16
|
+
};
|
|
17
|
+
type Props = {
|
|
18
|
+
accentColor?: string;
|
|
19
|
+
children: (props: {
|
|
20
|
+
onPressShow: () => void;
|
|
21
|
+
}) => ReactElement;
|
|
22
|
+
type: keyof typeof keys;
|
|
23
|
+
additionalInfo: AdditionalInfo;
|
|
24
|
+
};
|
|
25
|
+
export declare const SupportChat: ({ children, accentColor, type, additionalInfo }: Props) => ReactElement<any, string | import("react").JSXElementConstructor<any>>;
|
|
26
|
+
export {};
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { Array, Deferred, Dict, Option } from "@swan-io/boxed";
|
|
2
|
+
import { useCallback, useEffect } from "react";
|
|
3
|
+
const keys = {
|
|
4
|
+
"end-user": "275d1c72-3760-4303-a7f0-806f5266a251",
|
|
5
|
+
partner: "7a1d4c9f-9d4f-493d-8458-1e34f4a6b5ef",
|
|
6
|
+
};
|
|
7
|
+
const zendeskFieldMapping = {
|
|
8
|
+
phoneNumber: 5607378510109,
|
|
9
|
+
userId: 5140675351581,
|
|
10
|
+
email: "email",
|
|
11
|
+
onboardingId: 5138993599517,
|
|
12
|
+
membershipId: 5140686247581,
|
|
13
|
+
environment: 5139163414173,
|
|
14
|
+
};
|
|
15
|
+
const [zendeskApi, setZendeskApi] = Deferred.make();
|
|
16
|
+
export const SupportChat = ({ children, accentColor, type, additionalInfo }) => {
|
|
17
|
+
useEffect(() => {
|
|
18
|
+
const script = document.createElement("script");
|
|
19
|
+
script.id = "ze-snippet";
|
|
20
|
+
script.src = `https://static.zdassets.com/ekr/snippet.js?key=${keys[type]}`;
|
|
21
|
+
document.body.append(script);
|
|
22
|
+
script.addEventListener("load", () => {
|
|
23
|
+
const intervalId = setInterval(() => {
|
|
24
|
+
if (typeof zE != "undefined") {
|
|
25
|
+
setZendeskApi(zE);
|
|
26
|
+
clearInterval(intervalId);
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
return () => {
|
|
31
|
+
script.remove();
|
|
32
|
+
};
|
|
33
|
+
}, [type]);
|
|
34
|
+
useEffect(() => {
|
|
35
|
+
// @ts-expect-error
|
|
36
|
+
window.zESettings = {
|
|
37
|
+
color: { theme: accentColor ?? "#26232f" },
|
|
38
|
+
};
|
|
39
|
+
}, [accentColor]);
|
|
40
|
+
useEffect(() => {
|
|
41
|
+
const { firstName, lastName, email, projectName, ...fieldInfos } = additionalInfo;
|
|
42
|
+
const values = Dict.entries({ ...fieldInfos, email });
|
|
43
|
+
zendeskApi.onResolve(() => {
|
|
44
|
+
try {
|
|
45
|
+
zE("webWidget", "updateSettings", {
|
|
46
|
+
color: { theme: accentColor ?? "#26232f" },
|
|
47
|
+
webWidget: {
|
|
48
|
+
contactForm: {
|
|
49
|
+
fields: Array.keepMap(values, ([key, value]) => value != null
|
|
50
|
+
? Option.Some({
|
|
51
|
+
id: zendeskFieldMapping[key],
|
|
52
|
+
prefill: {
|
|
53
|
+
"*": key === "environment" ? (value === "Live" ? "live" : "sandbox") : value,
|
|
54
|
+
},
|
|
55
|
+
})
|
|
56
|
+
: Option.None()),
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
});
|
|
60
|
+
zE("webWidget", "hide");
|
|
61
|
+
zE("webWidget:on", "close", () => {
|
|
62
|
+
zE("webWidget", "hide");
|
|
63
|
+
});
|
|
64
|
+
if (firstName != null && lastName != null && email != null) {
|
|
65
|
+
zE("webWidget", "identify", {
|
|
66
|
+
name: `${firstName} ${lastName}`,
|
|
67
|
+
email,
|
|
68
|
+
...(projectName != null ? { organization: projectName } : null),
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
catch (err) {
|
|
73
|
+
// nothing
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
}, [accentColor, additionalInfo]);
|
|
77
|
+
const onPressShow = useCallback(() => {
|
|
78
|
+
try {
|
|
79
|
+
zE("webWidget", "show");
|
|
80
|
+
zE("webWidget", "open");
|
|
81
|
+
}
|
|
82
|
+
catch (err) {
|
|
83
|
+
// nothing
|
|
84
|
+
}
|
|
85
|
+
}, []);
|
|
86
|
+
return children({ onPressShow });
|
|
87
|
+
};
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/// <reference types="react" />
|
|
2
|
+
import { CountryCCA3 } from "../constants/countries";
|
|
3
|
+
export type Document = {
|
|
4
|
+
id: string;
|
|
5
|
+
name?: string;
|
|
6
|
+
downloadUrl?: string;
|
|
7
|
+
purpose: SupportingDocumentPurpose;
|
|
8
|
+
};
|
|
9
|
+
export type TypeOfRepresentation = "LegalRepresentative" | "PowerOfAttorney";
|
|
10
|
+
export type SupportingDocumentPurpose = "AssociationRegistration" | "CompanyRegistration" | "ProofOfIdentity" | "Other" | "SignedStatus";
|
|
11
|
+
type SupportingDocumentPurposeEnum = "AssociationRegistration" | "Banking" | "CompanyRegistration" | "Other" | "PowerOfAttorney" | "ProofOfCompanyAddress" | "ProofOfCompanyIncome" | "ProofOfIdentity" | "ProofOfIndividualAddress" | "ProofOfIndividualIncome" | "ProofOfOriginOfFunds" | "SignedStatus" | "UBODeclaration";
|
|
12
|
+
type Props = {
|
|
13
|
+
getAwsUrl: (file: File, purpose: SupportingDocumentPurpose) => Promise<{
|
|
14
|
+
upload: {
|
|
15
|
+
url: string;
|
|
16
|
+
fields: {
|
|
17
|
+
key: string;
|
|
18
|
+
value: string;
|
|
19
|
+
}[];
|
|
20
|
+
};
|
|
21
|
+
id: string;
|
|
22
|
+
}>;
|
|
23
|
+
documents: Document[];
|
|
24
|
+
requiredDocumentTypes: SupportingDocumentPurposeEnum[];
|
|
25
|
+
onChange?: (documents: Document[]) => void;
|
|
26
|
+
country?: CountryCCA3;
|
|
27
|
+
typeOfRepresentation?: TypeOfRepresentation;
|
|
28
|
+
};
|
|
29
|
+
export type SupportingDocumentRef = {
|
|
30
|
+
submit: (callback: (value: boolean) => void) => void;
|
|
31
|
+
};
|
|
32
|
+
export declare const SupportingDocument: import("react").ForwardRefExoticComponent<Props & import("react").RefAttributes<SupportingDocumentRef>>;
|
|
33
|
+
export {};
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { Box } from "@swan-io/lake/src/components/Box";
|
|
3
|
+
import { Form } from "@swan-io/lake/src/components/Form";
|
|
4
|
+
import { LakeButton, LakeButtonGroup } from "@swan-io/lake/src/components/LakeButton";
|
|
5
|
+
import { LakeLabel } from "@swan-io/lake/src/components/LakeLabel";
|
|
6
|
+
import { LakeModal } from "@swan-io/lake/src/components/LakeModal";
|
|
7
|
+
import { LakeRadio } from "@swan-io/lake/src/components/LakeRadio";
|
|
8
|
+
import { LakeText } from "@swan-io/lake/src/components/LakeText";
|
|
9
|
+
import { LakeTooltip } from "@swan-io/lake/src/components/LakeTooltip";
|
|
10
|
+
import { Pressable } from "@swan-io/lake/src/components/Pressable";
|
|
11
|
+
import { Space } from "@swan-io/lake/src/components/Space";
|
|
12
|
+
import { isNotNullish, isNullish } from "@swan-io/lake/src/utils/nullish";
|
|
13
|
+
import { UploadArea } from "@swan-io/shared-business/src/components/UploadArea";
|
|
14
|
+
import { forwardRef, useEffect, useImperativeHandle, useMemo, useState } from "react";
|
|
15
|
+
import { StyleSheet } from "react-native";
|
|
16
|
+
import { useForm } from "react-ux-form";
|
|
17
|
+
import { MAX_SUPPORTING_DOCUMENT_UPLOAD_SIZE } from "../constants/uploads";
|
|
18
|
+
import { t } from "../utils/i18n";
|
|
19
|
+
const ACCEPTED_FORMATS = ["application/pdf", "image/png", "image/jpeg"];
|
|
20
|
+
const NO_ID_YET = "NO_ID_YET";
|
|
21
|
+
const validateNotEmpty = value => {
|
|
22
|
+
if (value.length === 0) {
|
|
23
|
+
return t("error.requiredField");
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
const styles = StyleSheet.create({
|
|
27
|
+
button: {
|
|
28
|
+
opacity: 1,
|
|
29
|
+
},
|
|
30
|
+
buttonWithDefaultCursor: {
|
|
31
|
+
opacity: 1,
|
|
32
|
+
cursor: "default",
|
|
33
|
+
},
|
|
34
|
+
});
|
|
35
|
+
const Help = ({ text, width, onPress, }) => {
|
|
36
|
+
return (_jsx(LakeTooltip, { content: t(text), width: width, togglableOnFocus: true, placement: "top", children: _jsx(LakeButton, { mode: "tertiary", color: "gray", icon: "question-circle-regular", onPress: onPress, disabled: onPress == null, style: [styles.button, onPress == null && styles.buttonWithDefaultCursor], accessibilityLabel: t("supportingDoc.whatIsThis") }) }));
|
|
37
|
+
};
|
|
38
|
+
export const SupportingDocument = forwardRef(({ documents, getAwsUrl, onChange, requiredDocumentTypes, country, typeOfRepresentation }, externalRef) => {
|
|
39
|
+
const initialValues = useMemo(() => documents.reduce((acc, doc) => {
|
|
40
|
+
return {
|
|
41
|
+
...acc,
|
|
42
|
+
[doc.purpose]: [
|
|
43
|
+
...(acc[doc.purpose] ?? []),
|
|
44
|
+
{
|
|
45
|
+
id: doc.id,
|
|
46
|
+
name: doc.name,
|
|
47
|
+
fileUrl: doc.downloadUrl,
|
|
48
|
+
finished: true,
|
|
49
|
+
},
|
|
50
|
+
],
|
|
51
|
+
};
|
|
52
|
+
}, {}), [documents]);
|
|
53
|
+
const [showPowerOfAttorneyModal, setShowPowerOfAttorneyModal] = useState(false);
|
|
54
|
+
const [isOther, setIsOther] = useState(isNotNullish(initialValues["Other"]) || isNotNullish(initialValues["ProofOfIdentity"]));
|
|
55
|
+
const { Field, setFieldValue, getFieldState, listenFields, submitForm } = useForm({
|
|
56
|
+
CompanyRegistration: {
|
|
57
|
+
initialValue: initialValues["CompanyRegistration"] ?? [],
|
|
58
|
+
validate: validateNotEmpty,
|
|
59
|
+
},
|
|
60
|
+
AssociationRegistration: {
|
|
61
|
+
initialValue: initialValues["AssociationRegistration"] ?? [],
|
|
62
|
+
validate: validateNotEmpty,
|
|
63
|
+
},
|
|
64
|
+
SignedStatus: {
|
|
65
|
+
initialValue: initialValues["SignedStatus"] ?? [],
|
|
66
|
+
validate: validateNotEmpty,
|
|
67
|
+
},
|
|
68
|
+
ProofOfIdentity: {
|
|
69
|
+
initialValue: initialValues["ProofOfIdentity"] ?? [],
|
|
70
|
+
validate: validateNotEmpty,
|
|
71
|
+
},
|
|
72
|
+
Other: {
|
|
73
|
+
initialValue: initialValues["Other"] ?? [],
|
|
74
|
+
validate: validateNotEmpty,
|
|
75
|
+
},
|
|
76
|
+
});
|
|
77
|
+
useImperativeHandle(externalRef, () => {
|
|
78
|
+
return {
|
|
79
|
+
submit: (callback) => {
|
|
80
|
+
submitForm(() => callback(true), () => callback(false));
|
|
81
|
+
},
|
|
82
|
+
};
|
|
83
|
+
});
|
|
84
|
+
useEffect(() => {
|
|
85
|
+
const removeListener = listenFields([
|
|
86
|
+
"CompanyRegistration",
|
|
87
|
+
"AssociationRegistration",
|
|
88
|
+
"SignedStatus",
|
|
89
|
+
"Other",
|
|
90
|
+
"ProofOfIdentity",
|
|
91
|
+
], state => {
|
|
92
|
+
let documents = [];
|
|
93
|
+
Object.entries(state).forEach(([key, { value: values }]) => {
|
|
94
|
+
documents = [
|
|
95
|
+
...documents,
|
|
96
|
+
...(values ?? []).map(v => ({
|
|
97
|
+
id: v.id,
|
|
98
|
+
name: v.name,
|
|
99
|
+
downloadUrl: v.fileUrl,
|
|
100
|
+
purpose: key,
|
|
101
|
+
})),
|
|
102
|
+
];
|
|
103
|
+
});
|
|
104
|
+
onChange?.(documents);
|
|
105
|
+
});
|
|
106
|
+
return () => removeListener();
|
|
107
|
+
}, [listenFields, onChange]);
|
|
108
|
+
const handleUpload = (files, fieldName) => {
|
|
109
|
+
const file = files[0];
|
|
110
|
+
if (isNullish(file)) {
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
getAwsUrl(file, fieldName)
|
|
114
|
+
.then(({ upload: { url, fields }, id }) => {
|
|
115
|
+
const xhr = new XMLHttpRequest();
|
|
116
|
+
xhr.open("POST", url, true);
|
|
117
|
+
const state = getFieldState(fieldName).value;
|
|
118
|
+
setFieldValue(fieldName, state.map(doc => doc.id === NO_ID_YET
|
|
119
|
+
? {
|
|
120
|
+
progress: 0,
|
|
121
|
+
name: file.name,
|
|
122
|
+
finished: false,
|
|
123
|
+
id,
|
|
124
|
+
}
|
|
125
|
+
: doc));
|
|
126
|
+
xhr.upload.onprogress = event => {
|
|
127
|
+
const progress = (event.loaded / event.total) * 100;
|
|
128
|
+
const state = getFieldState(fieldName).value;
|
|
129
|
+
setFieldValue(fieldName, state.map(uploadState => uploadState.id === id ? { ...uploadState, progress } : uploadState));
|
|
130
|
+
};
|
|
131
|
+
xhr.onload = () => {
|
|
132
|
+
const state = getFieldState(fieldName).value;
|
|
133
|
+
if (xhr.status !== 200 && xhr.status !== 204) {
|
|
134
|
+
setFieldValue(fieldName, state.filter(uploadState => (uploadState.id === id ? false : true)));
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
setFieldValue(fieldName, state.map(uploadState => uploadState.id === id
|
|
138
|
+
? { ...uploadState, progress: undefined, finished: true }
|
|
139
|
+
: uploadState));
|
|
140
|
+
};
|
|
141
|
+
const formData = new FormData();
|
|
142
|
+
fields.forEach(({ key, value }) => {
|
|
143
|
+
formData.append(key, value);
|
|
144
|
+
});
|
|
145
|
+
formData.append("file", file);
|
|
146
|
+
xhr.send(formData);
|
|
147
|
+
})
|
|
148
|
+
.catch(console.error);
|
|
149
|
+
};
|
|
150
|
+
return (_jsxs(Form, { children: [typeOfRepresentation == null && (_jsx(LakeLabel, { label: t("supportingDoc.whoAreYou"), render: () => (_jsxs(LakeButtonGroup, { children: [_jsx(Pressable, { onPress: () => setIsOther(false), children: _jsxs(Box, { direction: "row", alignItems: "center", children: [_jsx(LakeRadio, { value: !isOther, color: "current" }), _jsx(Space, { width: 12 }), _jsx(LakeText, { children: t("supportingDoc.legalRepresentative") })] }) }), _jsx(Space, { width: 24 }), _jsx(Pressable, { onPress: () => setIsOther(true), children: _jsxs(Box, { direction: "row", alignItems: "center", children: [_jsx(LakeRadio, { value: isOther, color: "current" }), _jsx(Space, { width: 12 }), _jsx(LakeText, { children: t("supportingDoc.other") })] }) })] })) })), requiredDocumentTypes.some(t => t === "CompanyRegistration") && (_jsxs(_Fragment, { children: [_jsx(LakeLabel, { label: t("supportingDoc.companyRegistration"), help: _jsx(Help, { text: "supportingDoc.companyRegistration.description" }), render: () => (_jsx(Field, { name: "CompanyRegistration", children: ({ value, onChange, error }) => (_jsx(UploadArea, { layout: "horizontal", error: error, onDropAccepted: files => {
|
|
151
|
+
onChange([...value, { id: NO_ID_YET }]);
|
|
152
|
+
handleUpload(files, "CompanyRegistration");
|
|
153
|
+
}, documents: value, accept: ACCEPTED_FORMATS, icon: "document-regular", description: t("supportingDoc.documentTypes"), maxSize: MAX_SUPPORTING_DOCUMENT_UPLOAD_SIZE })) })) }), _jsx(Space, { height: 24 })] })), requiredDocumentTypes.some(t => t === "AssociationRegistration") && (_jsxs(_Fragment, { children: [_jsx(LakeLabel, { label: t("supportingDoc.associationRegistration"), help: _jsx(Help, { text: "supportingDoc.associationRegistration.description" }), render: () => (_jsx(Field, { name: "AssociationRegistration", children: ({ value, onChange, error }) => (_jsx(UploadArea, { layout: "horizontal", onDropAccepted: files => {
|
|
154
|
+
onChange([...value, { id: NO_ID_YET }]);
|
|
155
|
+
handleUpload(files, "AssociationRegistration");
|
|
156
|
+
}, error: error, documents: value, accept: ACCEPTED_FORMATS, icon: "document-regular", description: t("supportingDoc.documentTypes"), maxSize: MAX_SUPPORTING_DOCUMENT_UPLOAD_SIZE })) })) }), _jsx(Space, { height: 24 })] })), requiredDocumentTypes.some(t => t === "SignedStatus") && (_jsxs(_Fragment, { children: [_jsx(LakeLabel, { label: t("supportingDoc.signedStatus"), help: _jsx(Help, { text: "supportingDoc.signedStatus.description" }), render: () => (_jsx(Field, { name: "SignedStatus", children: ({ value, onChange, error }) => (_jsx(UploadArea, { layout: "horizontal", onDropAccepted: files => {
|
|
157
|
+
onChange([...value, { id: NO_ID_YET }]);
|
|
158
|
+
handleUpload(files, "SignedStatus");
|
|
159
|
+
}, error: error, documents: value, accept: ACCEPTED_FORMATS, icon: "document-regular", description: t("supportingDoc.documentTypes"), maxSize: MAX_SUPPORTING_DOCUMENT_UPLOAD_SIZE })) })) }), _jsx(Space, { height: 24 })] })), requiredDocumentTypes.length === 0 && !isOther ? (_jsxs(_Fragment, { children: [_jsx(Space, { height: 24 }), _jsx(LakeText, { children: t("supportingDoc.noRequiredDocuments") }), _jsx(Space, { height: 24 })] })) : null, typeOfRepresentation === "LegalRepresentative" || isOther ? (_jsxs(_Fragment, { children: [_jsx(LakeLabel, { label: t("supportingDoc.proofOfIdentity"), help: _jsx(Help, { width: 600, text: "supportingDoc.proofOfIdentity.description" }), render: () => (_jsx(Field, { name: "ProofOfIdentity", children: ({ value, onChange, error }) => (_jsx(UploadArea, { layout: "horizontal", onDropAccepted: files => {
|
|
160
|
+
onChange([...value, { id: NO_ID_YET }]);
|
|
161
|
+
handleUpload(files, "ProofOfIdentity");
|
|
162
|
+
}, error: error, documents: value, accept: ACCEPTED_FORMATS, icon: "document-regular", description: t("supportingDoc.documentTypes"), maxSize: MAX_SUPPORTING_DOCUMENT_UPLOAD_SIZE })) })) }), _jsx(Space, { height: 24 }), _jsx(LakeLabel, { label: t("supportingDoc.powerAttornySigned"), help: _jsx(Help, { text: "supportingDoc.powerAttornySigned.description", onPress: () => setShowPowerOfAttorneyModal(true) }), render: () => (_jsx(Field, { name: "Other", children: ({ value, onChange, error }) => (_jsx(UploadArea, { layout: "horizontal", onDropAccepted: files => {
|
|
163
|
+
onChange([...value, { id: NO_ID_YET }]);
|
|
164
|
+
handleUpload(files, "Other");
|
|
165
|
+
}, error: error, documents: value, accept: ACCEPTED_FORMATS, icon: "document-regular", description: t("supportingDoc.documentTypes"), maxSize: MAX_SUPPORTING_DOCUMENT_UPLOAD_SIZE })) })) }), _jsxs(LakeModal, { visible: showPowerOfAttorneyModal, title: t("supportingDoc.powerAttorney"), icon: "document-regular", onPressClose: () => setShowPowerOfAttorneyModal(false), children: [_jsx(LakeText, { children: t("supportingDoc.powerAttornySigned.description") }), _jsx(Space, { height: 16 }), _jsx(LakeButtonGroup, { paddingBottom: 0, children: _jsx(LakeButton, { grow: true, color: "current", onPress: () => window.open(`/power-of-attorney-template/${country === "FRA" ? "fr" : "en"}.pdf`), children: t("supportingDoc.downloadTemplate") }) })] })] })) : null] }));
|
|
166
|
+
});
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/// <reference types="react" />
|
|
2
|
+
import { IconName } from "@swan-io/lake/src/components/Icon";
|
|
3
|
+
import { DropzoneOptions } from "react-dropzone";
|
|
4
|
+
export type UploadFileStatus = {
|
|
5
|
+
id: string;
|
|
6
|
+
name?: string;
|
|
7
|
+
fileUrl?: string;
|
|
8
|
+
progress?: number;
|
|
9
|
+
finished?: boolean;
|
|
10
|
+
};
|
|
11
|
+
type Props = {
|
|
12
|
+
icon: IconName;
|
|
13
|
+
documents?: UploadFileStatus[];
|
|
14
|
+
onRemoveFile?: (fileId: string) => void;
|
|
15
|
+
accept: string[];
|
|
16
|
+
value?: File | Element;
|
|
17
|
+
disabled?: boolean;
|
|
18
|
+
onDropAccepted?: DropzoneOptions["onDropAccepted"];
|
|
19
|
+
onDropRejected?: DropzoneOptions["onDropRejected"];
|
|
20
|
+
layout?: "vertical" | "horizontal";
|
|
21
|
+
description?: string;
|
|
22
|
+
error?: string;
|
|
23
|
+
maxSize?: number;
|
|
24
|
+
};
|
|
25
|
+
export declare const UploadArea: ({ icon, accept, value, documents, disabled, onRemoveFile, onDropAccepted, onDropRejected, layout, description, error, maxSize, }: Props) => JSX.Element;
|
|
26
|
+
export {};
|