codeforlife 2.6.1
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/.eslintrc.json +47 -0
- package/.github/workflows/contributing.yaml +8 -0
- package/.github/workflows/main.yml +36 -0
- package/.prettierignore +5 -0
- package/.prettierrc.json +4 -0
- package/.vscode/launch.json +22 -0
- package/.vscode/settings.json +30 -0
- package/CHANGELOG.md +1864 -0
- package/CONTRIBUTING.md +3 -0
- package/LICENSE.md +3 -0
- package/README.md +94 -0
- package/codecov.yml +11 -0
- package/package.json +139 -0
- package/src/api/createApi.ts +84 -0
- package/src/api/endpoints/authFactor.ts +31 -0
- package/src/api/endpoints/index.ts +9 -0
- package/src/api/endpoints/klass.ts +87 -0
- package/src/api/endpoints/school.ts +34 -0
- package/src/api/endpoints/session.ts +40 -0
- package/src/api/endpoints/user.ts +70 -0
- package/src/api/index.ts +4 -0
- package/src/api/models.ts +144 -0
- package/src/api/tagTypes.ts +12 -0
- package/src/api/urls.ts +13 -0
- package/src/components/App.css +38 -0
- package/src/components/App.tsx +150 -0
- package/src/components/ClickableTooltip.tsx +43 -0
- package/src/components/CopyIconButton.test.tsx +16 -0
- package/src/components/CopyIconButton.tsx +27 -0
- package/src/components/Countdown.tsx +42 -0
- package/src/components/ElevatedAppBar.tsx +41 -0
- package/src/components/Image.tsx +41 -0
- package/src/components/InputFileButton.tsx +27 -0
- package/src/components/ItemizedList.tsx +61 -0
- package/src/components/OrderedGrid.tsx +92 -0
- package/src/components/ScrollIntoViewLink.tsx +23 -0
- package/src/components/SyncError.tsx +14 -0
- package/src/components/TablePagination.tsx +132 -0
- package/src/components/YouTubeVideo.tsx +26 -0
- package/src/components/form/ApiAutocompleteField.tsx +180 -0
- package/src/components/form/AutocompleteField.tsx +124 -0
- package/src/components/form/CheckboxField.tsx +81 -0
- package/src/components/form/CountryField.tsx +68 -0
- package/src/components/form/DatePickerField.tsx +119 -0
- package/src/components/form/EmailField.tsx +38 -0
- package/src/components/form/FirstNameField.tsx +40 -0
- package/src/components/form/Form.tsx +82 -0
- package/src/components/form/OtpField.tsx +28 -0
- package/src/components/form/PasswordField.tsx +71 -0
- package/src/components/form/RepeatField.tsx +115 -0
- package/src/components/form/SubmitButton.tsx +47 -0
- package/src/components/form/TextField.tsx +103 -0
- package/src/components/form/UkCountyField.tsx +67 -0
- package/src/components/form/index.tsx +28 -0
- package/src/components/index.ts +26 -0
- package/src/components/page/Banner.tsx +84 -0
- package/src/components/page/Notification.tsx +71 -0
- package/src/components/page/Page.tsx +73 -0
- package/src/components/page/Section.tsx +21 -0
- package/src/components/page/TabBar.tsx +131 -0
- package/src/components/page/index.ts +10 -0
- package/src/components/router/Link.tsx +22 -0
- package/src/components/router/LinkButton.tsx +21 -0
- package/src/components/router/LinkIconButton.tsx +21 -0
- package/src/components/router/LinkListItem.tsx +21 -0
- package/src/components/router/LinkTab.tsx +21 -0
- package/src/components/router/Navigate.tsx +33 -0
- package/src/components/router/index.tsx +12 -0
- package/src/components/table/CellStack.tsx +19 -0
- package/src/components/table/Table.tsx +55 -0
- package/src/components/table/index.tsx +10 -0
- package/src/features/InactiveDialog.tsx +40 -0
- package/src/features/ScreenTimeDialog.tsx +33 -0
- package/src/features/index.ts +4 -0
- package/src/fonts/Inter-VariableFont_slnt,wght.ttf +0 -0
- package/src/fonts/SpaceGrotesk-VariableFont_wght.ttf +0 -0
- package/src/hooks/api.tsx +37 -0
- package/src/hooks/auth.tsx +87 -0
- package/src/hooks/general.ts +110 -0
- package/src/hooks/index.ts +4 -0
- package/src/hooks/router.tsx +168 -0
- package/src/index.ts +2 -0
- package/src/middlewares/index.ts +1 -0
- package/src/middlewares/session.ts +16 -0
- package/src/public/images/brain.svg +1 -0
- package/src/schemas/user.ts +4 -0
- package/src/scripts/freshDesk.js +473 -0
- package/src/scripts/index.ts +1 -0
- package/src/server.js +181 -0
- package/src/settings/custom.ts +22 -0
- package/src/settings/index.ts +5 -0
- package/src/settings/vite.ts +26 -0
- package/src/setupTests.ts +1 -0
- package/src/slices/createSlice.ts +8 -0
- package/src/slices/index.ts +2 -0
- package/src/slices/session.ts +32 -0
- package/src/theme/ThemedBox.tsx +265 -0
- package/src/theme/colors.ts +57 -0
- package/src/theme/components/MuiAccordion.tsx +13 -0
- package/src/theme/components/MuiAutocomplete.tsx +11 -0
- package/src/theme/components/MuiButton.ts +70 -0
- package/src/theme/components/MuiCardActions.tsx +12 -0
- package/src/theme/components/MuiCheckbox.ts +12 -0
- package/src/theme/components/MuiContainer.ts +19 -0
- package/src/theme/components/MuiDialog.tsx +16 -0
- package/src/theme/components/MuiFormControlLabel.ts +18 -0
- package/src/theme/components/MuiFormHelperText.ts +12 -0
- package/src/theme/components/MuiGrid2.ts +16 -0
- package/src/theme/components/MuiInputBase.ts +14 -0
- package/src/theme/components/MuiLink.ts +41 -0
- package/src/theme/components/MuiList.ts +12 -0
- package/src/theme/components/MuiListItemText.ts +18 -0
- package/src/theme/components/MuiMenu.ts +14 -0
- package/src/theme/components/MuiMenuItem.ts +15 -0
- package/src/theme/components/MuiSelect.ts +16 -0
- package/src/theme/components/MuiTab.ts +29 -0
- package/src/theme/components/MuiTable.ts +29 -0
- package/src/theme/components/MuiTableBody.ts +15 -0
- package/src/theme/components/MuiTableHead.ts +26 -0
- package/src/theme/components/MuiTabs.ts +26 -0
- package/src/theme/components/MuiTextField.ts +86 -0
- package/src/theme/components/MuiToolbar.ts +11 -0
- package/src/theme/components/MuiTypography.ts +12 -0
- package/src/theme/components/_components.ts +93 -0
- package/src/theme/components/index.ts +57 -0
- package/src/theme/index.ts +25 -0
- package/src/theme/palette.ts +98 -0
- package/src/theme/spacing.ts +8 -0
- package/src/theme/typography.ts +101 -0
- package/src/utils/api.test.ts +19 -0
- package/src/utils/api.tsx +327 -0
- package/src/utils/auth.ts +17 -0
- package/src/utils/form.ts +153 -0
- package/src/utils/general.test.ts +42 -0
- package/src/utils/general.ts +498 -0
- package/src/utils/router.test.ts +156 -0
- package/src/utils/router.ts +67 -0
- package/src/utils/schema.ts +80 -0
- package/src/utils/store.ts +31 -0
- package/src/utils/test.tsx +82 -0
- package/src/utils/theme.tsx +82 -0
- package/src/utils/window.ts +9 -0
- package/src/vite-env.d.ts +1 -0
- package/tsconfig.json +31 -0
- package/tsconfig.node.json +11 -0
- package/types/fixes.d.ts +18 -0
- package/vite.config.ts +21 -0
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
import type { TypedMutationTrigger } from "@reduxjs/toolkit/query/react"
|
|
2
|
+
import type { FieldValidator, FormikHelpers } from "formik"
|
|
3
|
+
import { ValidationError, type Schema, type ValidateOptions } from "yup"
|
|
4
|
+
|
|
5
|
+
import { excludeKeyPaths } from "./general"
|
|
6
|
+
|
|
7
|
+
export type FormValues = Record<string, any>
|
|
8
|
+
|
|
9
|
+
export function isFormError(error: unknown): boolean {
|
|
10
|
+
return (
|
|
11
|
+
typeof error === "object" &&
|
|
12
|
+
error !== null &&
|
|
13
|
+
"status" in error &&
|
|
14
|
+
error.status === 400 &&
|
|
15
|
+
"data" in error &&
|
|
16
|
+
typeof error.data === "object" &&
|
|
17
|
+
error.data !== null
|
|
18
|
+
)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function setFormErrors(
|
|
22
|
+
error: unknown,
|
|
23
|
+
setErrors: (errors: object) => void,
|
|
24
|
+
): void {
|
|
25
|
+
if (!isFormError(error)) throw error
|
|
26
|
+
|
|
27
|
+
const data = Object.fromEntries(
|
|
28
|
+
Object.entries((error as { data: object }).data).map(([field, errors]) => {
|
|
29
|
+
if (Array.isArray(errors)) errors = errors.join(". ")
|
|
30
|
+
return [field, errors]
|
|
31
|
+
}),
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
setErrors(data)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export type SubmitFormOptions<
|
|
38
|
+
Values extends FormValues,
|
|
39
|
+
QueryArg extends FormValues,
|
|
40
|
+
ResultType,
|
|
41
|
+
> = Partial<{
|
|
42
|
+
exclude: string[]
|
|
43
|
+
then: (
|
|
44
|
+
result: ResultType,
|
|
45
|
+
values: Values,
|
|
46
|
+
helpers: FormikHelpers<Values>,
|
|
47
|
+
) => void
|
|
48
|
+
catch: (
|
|
49
|
+
error: unknown,
|
|
50
|
+
values: Values,
|
|
51
|
+
helpers: FormikHelpers<Values>,
|
|
52
|
+
) => void
|
|
53
|
+
finally: (values: Values, helpers: FormikHelpers<Values>) => void
|
|
54
|
+
}> &
|
|
55
|
+
(Values extends QueryArg
|
|
56
|
+
? { clean?: (values: Values) => QueryArg }
|
|
57
|
+
: { clean: (values: Values) => QueryArg })
|
|
58
|
+
|
|
59
|
+
export type SubmitFormHandler<Values extends FormValues> = (
|
|
60
|
+
values: Values,
|
|
61
|
+
helpers: FormikHelpers<Values>,
|
|
62
|
+
) => void | Promise<any>
|
|
63
|
+
|
|
64
|
+
export function submitForm<
|
|
65
|
+
Values extends QueryArg,
|
|
66
|
+
QueryArg extends FormValues,
|
|
67
|
+
ResultType,
|
|
68
|
+
>(
|
|
69
|
+
trigger: TypedMutationTrigger<ResultType, QueryArg, any>,
|
|
70
|
+
options?: SubmitFormOptions<Values, QueryArg, ResultType>,
|
|
71
|
+
): SubmitFormHandler<Values>
|
|
72
|
+
|
|
73
|
+
export function submitForm<
|
|
74
|
+
Values extends FormValues,
|
|
75
|
+
QueryArg extends FormValues,
|
|
76
|
+
ResultType,
|
|
77
|
+
>(
|
|
78
|
+
trigger: TypedMutationTrigger<ResultType, QueryArg, any>,
|
|
79
|
+
options: SubmitFormOptions<Values, QueryArg, ResultType>,
|
|
80
|
+
): SubmitFormHandler<Values>
|
|
81
|
+
|
|
82
|
+
export function submitForm<
|
|
83
|
+
Values extends FormValues,
|
|
84
|
+
QueryArg extends FormValues,
|
|
85
|
+
ResultType,
|
|
86
|
+
>(
|
|
87
|
+
trigger: TypedMutationTrigger<ResultType, QueryArg, any>,
|
|
88
|
+
options?: SubmitFormOptions<Values, QueryArg, ResultType>,
|
|
89
|
+
): SubmitFormHandler<Values> {
|
|
90
|
+
const { exclude, then, catch: _catch, finally: _finally } = options || {}
|
|
91
|
+
|
|
92
|
+
return (values, helpers) => {
|
|
93
|
+
let arg =
|
|
94
|
+
options && options.clean
|
|
95
|
+
? options.clean(values as QueryArg & FormValues)
|
|
96
|
+
: (values as unknown as QueryArg)
|
|
97
|
+
|
|
98
|
+
if (exclude) arg = excludeKeyPaths(arg, exclude)
|
|
99
|
+
|
|
100
|
+
trigger(arg)
|
|
101
|
+
.unwrap()
|
|
102
|
+
.then(result => {
|
|
103
|
+
if (then) then(result, values, helpers)
|
|
104
|
+
})
|
|
105
|
+
.catch(error => {
|
|
106
|
+
if (_catch) _catch(error, values, helpers)
|
|
107
|
+
setFormErrors(error, helpers.setErrors)
|
|
108
|
+
})
|
|
109
|
+
.finally(() => {
|
|
110
|
+
if (_finally) _finally(values, helpers)
|
|
111
|
+
})
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export function schemaToFieldValidator(
|
|
116
|
+
schema: Schema,
|
|
117
|
+
options?: ValidateOptions,
|
|
118
|
+
): FieldValidator {
|
|
119
|
+
return async value => {
|
|
120
|
+
try {
|
|
121
|
+
await schema.validate(value, options)
|
|
122
|
+
} catch (error) {
|
|
123
|
+
if (error instanceof ValidationError) {
|
|
124
|
+
return error.errors.join(". ")
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
throw error
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Checking if individual fields are dirty is not currently supported.
|
|
133
|
+
// https://github.com/jaredpalmer/formik/issues/1421
|
|
134
|
+
export function getDirty<
|
|
135
|
+
Values extends FormValues,
|
|
136
|
+
Names extends Array<keyof Values>,
|
|
137
|
+
>(
|
|
138
|
+
values: Values,
|
|
139
|
+
initialValues: Values,
|
|
140
|
+
names: Names,
|
|
141
|
+
): Record<Names[number], boolean> {
|
|
142
|
+
return Object.fromEntries(
|
|
143
|
+
names.map(name => [name, isDirty(values, initialValues, name)]),
|
|
144
|
+
) as Record<Names[number], boolean>
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export function isDirty<Values extends FormValues, Name extends keyof Values>(
|
|
148
|
+
values: Values,
|
|
149
|
+
initialValues: Values,
|
|
150
|
+
name: Name,
|
|
151
|
+
): boolean {
|
|
152
|
+
return values[name] !== initialValues[name]
|
|
153
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { excludeKeyPaths, getNestedProperty, withKeyPaths } from "./general"
|
|
2
|
+
|
|
3
|
+
// getNestedProperty
|
|
4
|
+
|
|
5
|
+
const PERSON = { father: { father: { name: "John" } } }
|
|
6
|
+
|
|
7
|
+
test("get a nested property with dot notation", () => {
|
|
8
|
+
const name = getNestedProperty(PERSON, "father.father.name")
|
|
9
|
+
|
|
10
|
+
expect(name).equal("John")
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
test("get a nested property with string array", () => {
|
|
14
|
+
const name = getNestedProperty(PERSON, ["father", "father", "name"])
|
|
15
|
+
|
|
16
|
+
expect(name).equal("John")
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
test("get a nested property that doesn't exist", () => {
|
|
20
|
+
const name = getNestedProperty(PERSON, "mother.mother.name")
|
|
21
|
+
|
|
22
|
+
expect(name).toBeUndefined()
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
// withKeyPaths
|
|
26
|
+
|
|
27
|
+
test("get the paths of nested keys", () => {
|
|
28
|
+
const obj = withKeyPaths({ a: 1, b: { c: 2, d: { e: 3 } } })
|
|
29
|
+
|
|
30
|
+
expect(obj).toMatchObject({ a: 1, b: { "b.c": 2, "b.d": { "b.d.e": 3 } } })
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
// excludeKeyPaths
|
|
34
|
+
|
|
35
|
+
test("exclude nested keys by their path", () => {
|
|
36
|
+
const obj = excludeKeyPaths({ a: 1, b: { c: 2, d: { e: 3 } } }, [
|
|
37
|
+
"b.c",
|
|
38
|
+
"b.d.e",
|
|
39
|
+
])
|
|
40
|
+
|
|
41
|
+
expect(obj).toMatchObject({ a: 1, b: { d: {} } })
|
|
42
|
+
})
|
|
@@ -0,0 +1,498 @@
|
|
|
1
|
+
export type Required<T, K extends keyof T> = { [P in K]-?: T[P] }
|
|
2
|
+
export type Optional<T, K extends keyof T> = Partial<Pick<T, K>>
|
|
3
|
+
|
|
4
|
+
export function openInNewTab(url: string, target = "_blank"): void {
|
|
5
|
+
window.open(url, target)
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function wrap(
|
|
9
|
+
newFn: {
|
|
10
|
+
before?: (...args: any[]) => void
|
|
11
|
+
after?: (...args: any[]) => void
|
|
12
|
+
},
|
|
13
|
+
fn?: (...args: any[]) => any,
|
|
14
|
+
): (...args: any[]) => any {
|
|
15
|
+
return (...args) => {
|
|
16
|
+
if (newFn.before !== undefined) {
|
|
17
|
+
newFn.before(...args)
|
|
18
|
+
}
|
|
19
|
+
let value
|
|
20
|
+
if (fn !== undefined) {
|
|
21
|
+
value = fn(...args)
|
|
22
|
+
}
|
|
23
|
+
if (newFn.after !== undefined) {
|
|
24
|
+
newFn.after(...args)
|
|
25
|
+
}
|
|
26
|
+
return value
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function snakeCaseToCamelCase(obj: Record<string, any>): void {
|
|
31
|
+
Object.entries(obj).forEach(([snakeKey, value]) => {
|
|
32
|
+
if (typeof value === "object") snakeCaseToCamelCase(value)
|
|
33
|
+
|
|
34
|
+
const camelKey = snakeKey.replace(/_+[a-z]/g, _char =>
|
|
35
|
+
_char[_char.length - 1].toUpperCase(),
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
|
39
|
+
delete obj[snakeKey]
|
|
40
|
+
obj[camelKey] = value
|
|
41
|
+
})
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function camelCaseToSnakeCase(obj: Record<string, any>): void {
|
|
45
|
+
Object.entries(obj).forEach(([camelKey, value]) => {
|
|
46
|
+
if (typeof value === "object") camelCaseToSnakeCase(value)
|
|
47
|
+
|
|
48
|
+
const snakeKey = camelKey.replace(
|
|
49
|
+
/[A-Z]/g,
|
|
50
|
+
char => `_${char.toLowerCase()}`,
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
|
54
|
+
delete obj[camelKey]
|
|
55
|
+
obj[snakeKey] = value
|
|
56
|
+
})
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export const MIN_DATE = new Date(0, 0, 0)
|
|
60
|
+
|
|
61
|
+
export const COUNTRY_ISO_CODES = [
|
|
62
|
+
"AF",
|
|
63
|
+
"AX",
|
|
64
|
+
"AL",
|
|
65
|
+
"DZ",
|
|
66
|
+
"AS",
|
|
67
|
+
"AD",
|
|
68
|
+
"AO",
|
|
69
|
+
"AI",
|
|
70
|
+
"AQ",
|
|
71
|
+
"AG",
|
|
72
|
+
"AR",
|
|
73
|
+
"AM",
|
|
74
|
+
"AW",
|
|
75
|
+
"AU",
|
|
76
|
+
"AT",
|
|
77
|
+
"AZ",
|
|
78
|
+
"BS",
|
|
79
|
+
"BH",
|
|
80
|
+
"BD",
|
|
81
|
+
"BB",
|
|
82
|
+
"BY",
|
|
83
|
+
"BE",
|
|
84
|
+
"BZ",
|
|
85
|
+
"BJ",
|
|
86
|
+
"BM",
|
|
87
|
+
"BT",
|
|
88
|
+
"BO",
|
|
89
|
+
"BQ",
|
|
90
|
+
"BA",
|
|
91
|
+
"BW",
|
|
92
|
+
"BV",
|
|
93
|
+
"BR",
|
|
94
|
+
"IO",
|
|
95
|
+
"BN",
|
|
96
|
+
"BG",
|
|
97
|
+
"BF",
|
|
98
|
+
"BI",
|
|
99
|
+
"KH",
|
|
100
|
+
"CM",
|
|
101
|
+
"CA",
|
|
102
|
+
"CV",
|
|
103
|
+
"KY",
|
|
104
|
+
"CF",
|
|
105
|
+
"TD",
|
|
106
|
+
"CL",
|
|
107
|
+
"CN",
|
|
108
|
+
"CX",
|
|
109
|
+
"CC",
|
|
110
|
+
"CO",
|
|
111
|
+
"KM",
|
|
112
|
+
"CG",
|
|
113
|
+
"CD",
|
|
114
|
+
"CK",
|
|
115
|
+
"CR",
|
|
116
|
+
"CI",
|
|
117
|
+
"HR",
|
|
118
|
+
"CU",
|
|
119
|
+
"CW",
|
|
120
|
+
"CY",
|
|
121
|
+
"CZ",
|
|
122
|
+
"DK",
|
|
123
|
+
"DJ",
|
|
124
|
+
"DM",
|
|
125
|
+
"DO",
|
|
126
|
+
"EC",
|
|
127
|
+
"EG",
|
|
128
|
+
"SV",
|
|
129
|
+
"GQ",
|
|
130
|
+
"ER",
|
|
131
|
+
"EE",
|
|
132
|
+
"ET",
|
|
133
|
+
"FK",
|
|
134
|
+
"FO",
|
|
135
|
+
"FJ",
|
|
136
|
+
"FI",
|
|
137
|
+
"FR",
|
|
138
|
+
"GF",
|
|
139
|
+
"PF",
|
|
140
|
+
"TF",
|
|
141
|
+
"GA",
|
|
142
|
+
"GM",
|
|
143
|
+
"GE",
|
|
144
|
+
"DE",
|
|
145
|
+
"GH",
|
|
146
|
+
"GI",
|
|
147
|
+
"GR",
|
|
148
|
+
"GL",
|
|
149
|
+
"GD",
|
|
150
|
+
"GP",
|
|
151
|
+
"GU",
|
|
152
|
+
"GT",
|
|
153
|
+
"GG",
|
|
154
|
+
"GN",
|
|
155
|
+
"GW",
|
|
156
|
+
"GY",
|
|
157
|
+
"HT",
|
|
158
|
+
"HM",
|
|
159
|
+
"VA",
|
|
160
|
+
"HN",
|
|
161
|
+
"HK",
|
|
162
|
+
"HU",
|
|
163
|
+
"IS",
|
|
164
|
+
"IN",
|
|
165
|
+
"ID",
|
|
166
|
+
"IR",
|
|
167
|
+
"IQ",
|
|
168
|
+
"IE",
|
|
169
|
+
"IM",
|
|
170
|
+
"IL",
|
|
171
|
+
"IT",
|
|
172
|
+
"JM",
|
|
173
|
+
"JP",
|
|
174
|
+
"JE",
|
|
175
|
+
"JO",
|
|
176
|
+
"KZ",
|
|
177
|
+
"KE",
|
|
178
|
+
"KI",
|
|
179
|
+
"KP",
|
|
180
|
+
"KR",
|
|
181
|
+
"KW",
|
|
182
|
+
"KG",
|
|
183
|
+
"LA",
|
|
184
|
+
"LV",
|
|
185
|
+
"LB",
|
|
186
|
+
"LS",
|
|
187
|
+
"LR",
|
|
188
|
+
"LY",
|
|
189
|
+
"LI",
|
|
190
|
+
"LT",
|
|
191
|
+
"LU",
|
|
192
|
+
"MO",
|
|
193
|
+
"MK",
|
|
194
|
+
"MG",
|
|
195
|
+
"MW",
|
|
196
|
+
"MY",
|
|
197
|
+
"MV",
|
|
198
|
+
"ML",
|
|
199
|
+
"MT",
|
|
200
|
+
"MH",
|
|
201
|
+
"MQ",
|
|
202
|
+
"MR",
|
|
203
|
+
"MU",
|
|
204
|
+
"YT",
|
|
205
|
+
"MX",
|
|
206
|
+
"FM",
|
|
207
|
+
"MD",
|
|
208
|
+
"MC",
|
|
209
|
+
"MN",
|
|
210
|
+
"ME",
|
|
211
|
+
"MS",
|
|
212
|
+
"MA",
|
|
213
|
+
"MZ",
|
|
214
|
+
"MM",
|
|
215
|
+
"NA",
|
|
216
|
+
"NR",
|
|
217
|
+
"NP",
|
|
218
|
+
"NL",
|
|
219
|
+
"NC",
|
|
220
|
+
"NZ",
|
|
221
|
+
"NI",
|
|
222
|
+
"NE",
|
|
223
|
+
"NG",
|
|
224
|
+
"NU",
|
|
225
|
+
"NF",
|
|
226
|
+
"MP",
|
|
227
|
+
"NO",
|
|
228
|
+
"OM",
|
|
229
|
+
"PK",
|
|
230
|
+
"PW",
|
|
231
|
+
"PS",
|
|
232
|
+
"PA",
|
|
233
|
+
"PG",
|
|
234
|
+
"PY",
|
|
235
|
+
"PE",
|
|
236
|
+
"PH",
|
|
237
|
+
"PN",
|
|
238
|
+
"PL",
|
|
239
|
+
"PT",
|
|
240
|
+
"PR",
|
|
241
|
+
"QA",
|
|
242
|
+
"RE",
|
|
243
|
+
"RO",
|
|
244
|
+
"RU",
|
|
245
|
+
"RW",
|
|
246
|
+
"BL",
|
|
247
|
+
"SH",
|
|
248
|
+
"KN",
|
|
249
|
+
"LC",
|
|
250
|
+
"MF",
|
|
251
|
+
"PM",
|
|
252
|
+
"VC",
|
|
253
|
+
"WS",
|
|
254
|
+
"SM",
|
|
255
|
+
"ST",
|
|
256
|
+
"SA",
|
|
257
|
+
"SN",
|
|
258
|
+
"RS",
|
|
259
|
+
"SC",
|
|
260
|
+
"SL",
|
|
261
|
+
"SG",
|
|
262
|
+
"SX",
|
|
263
|
+
"SK",
|
|
264
|
+
"SI",
|
|
265
|
+
"SB",
|
|
266
|
+
"SO",
|
|
267
|
+
"ZA",
|
|
268
|
+
"GS",
|
|
269
|
+
"SS",
|
|
270
|
+
"ES",
|
|
271
|
+
"LK",
|
|
272
|
+
"SD",
|
|
273
|
+
"SR",
|
|
274
|
+
"SJ",
|
|
275
|
+
"SZ",
|
|
276
|
+
"SE",
|
|
277
|
+
"CH",
|
|
278
|
+
"SY",
|
|
279
|
+
"TW",
|
|
280
|
+
"TJ",
|
|
281
|
+
"TZ",
|
|
282
|
+
"TH",
|
|
283
|
+
"TL",
|
|
284
|
+
"TG",
|
|
285
|
+
"TK",
|
|
286
|
+
"TO",
|
|
287
|
+
"TT",
|
|
288
|
+
"TN",
|
|
289
|
+
"TR",
|
|
290
|
+
"TM",
|
|
291
|
+
"TC",
|
|
292
|
+
"TV",
|
|
293
|
+
"UG",
|
|
294
|
+
"UA",
|
|
295
|
+
"AE",
|
|
296
|
+
"GB",
|
|
297
|
+
"US",
|
|
298
|
+
"UM",
|
|
299
|
+
"UY",
|
|
300
|
+
"UZ",
|
|
301
|
+
"VU",
|
|
302
|
+
"VE",
|
|
303
|
+
"VN",
|
|
304
|
+
"VG",
|
|
305
|
+
"VI",
|
|
306
|
+
"WF",
|
|
307
|
+
"EH",
|
|
308
|
+
"YE",
|
|
309
|
+
"ZM",
|
|
310
|
+
"ZW",
|
|
311
|
+
] as const
|
|
312
|
+
|
|
313
|
+
export type CountryIsoCodes = (typeof COUNTRY_ISO_CODES)[number]
|
|
314
|
+
|
|
315
|
+
export const UK_COUNTIES = [
|
|
316
|
+
"Aberdeen City",
|
|
317
|
+
"Aberdeenshire",
|
|
318
|
+
"Angus",
|
|
319
|
+
"Argyll and Bute",
|
|
320
|
+
"Bedfordshire",
|
|
321
|
+
"Belfast",
|
|
322
|
+
"Belfast Greater",
|
|
323
|
+
"Berkshire",
|
|
324
|
+
"Blaenau Gwent",
|
|
325
|
+
"Bridgend",
|
|
326
|
+
"Buckinghamshire",
|
|
327
|
+
"Caerphilly",
|
|
328
|
+
"Cambridgeshire",
|
|
329
|
+
"Cardiff",
|
|
330
|
+
"Carmarthenshire",
|
|
331
|
+
"Ceredigion",
|
|
332
|
+
"Channel Islands",
|
|
333
|
+
"Cheshire",
|
|
334
|
+
"City of Edinburgh",
|
|
335
|
+
"Clackmannanshire",
|
|
336
|
+
"Conwy",
|
|
337
|
+
"Cornwall",
|
|
338
|
+
"County Antrim",
|
|
339
|
+
"County Armagh",
|
|
340
|
+
"County Down",
|
|
341
|
+
"County Fermanagh",
|
|
342
|
+
"County Londonderry",
|
|
343
|
+
"County Tyrone",
|
|
344
|
+
"County of Bristol",
|
|
345
|
+
"Cumbria",
|
|
346
|
+
"Denbighshire",
|
|
347
|
+
"Derbyshire",
|
|
348
|
+
"Devon",
|
|
349
|
+
"Dorset",
|
|
350
|
+
"Dumfries and Galloway",
|
|
351
|
+
"Dunbartonshire",
|
|
352
|
+
"Dundee City",
|
|
353
|
+
"Durham",
|
|
354
|
+
"East Ayrshire",
|
|
355
|
+
"East Dunbartonshire",
|
|
356
|
+
"East Lothian",
|
|
357
|
+
"East Renfrewshire",
|
|
358
|
+
"East Riding of Yorkshire",
|
|
359
|
+
"East Sussex",
|
|
360
|
+
"Essex",
|
|
361
|
+
"Falkirk",
|
|
362
|
+
"Fife",
|
|
363
|
+
"Flintshire",
|
|
364
|
+
"Glasgow City",
|
|
365
|
+
"Gloucestershire",
|
|
366
|
+
"Greater London",
|
|
367
|
+
"Greater Manchester",
|
|
368
|
+
"Guernsey Channel Islands",
|
|
369
|
+
"Gwynedd",
|
|
370
|
+
"Hampshire",
|
|
371
|
+
"Hereford and Worcester",
|
|
372
|
+
"Herefordshire",
|
|
373
|
+
"Hertfordshire",
|
|
374
|
+
"Highland",
|
|
375
|
+
"Inverclyde",
|
|
376
|
+
"Inverness",
|
|
377
|
+
"Isle of Anglesey",
|
|
378
|
+
"Isle of Barra",
|
|
379
|
+
"Isle of Man",
|
|
380
|
+
"Isle of Wight",
|
|
381
|
+
"Jersey Channel Islands",
|
|
382
|
+
"Kent",
|
|
383
|
+
"Lancashire",
|
|
384
|
+
"Leicestershire",
|
|
385
|
+
"Lincolnshire",
|
|
386
|
+
"Merseyside",
|
|
387
|
+
"Merthyr Tydfil",
|
|
388
|
+
"Midlothian",
|
|
389
|
+
"Monmouthshire",
|
|
390
|
+
"Moray",
|
|
391
|
+
"Neath Port Talbot",
|
|
392
|
+
"Newport",
|
|
393
|
+
"Norfolk",
|
|
394
|
+
"North Ayrshire",
|
|
395
|
+
"North Lanarkshire",
|
|
396
|
+
"North Yorkshire",
|
|
397
|
+
"Northamptonshire",
|
|
398
|
+
"Northumberland",
|
|
399
|
+
"Nottinghamshire",
|
|
400
|
+
"Orkney",
|
|
401
|
+
"Orkney Islands",
|
|
402
|
+
"Oxfordshire",
|
|
403
|
+
"Pembrokeshire",
|
|
404
|
+
"Perth and Kinross",
|
|
405
|
+
"Powys",
|
|
406
|
+
"Renfrewshire",
|
|
407
|
+
"Rhondda Cynon Taff",
|
|
408
|
+
"Rutland",
|
|
409
|
+
"Scottish Borders",
|
|
410
|
+
"Shetland Islands",
|
|
411
|
+
"Shropshire",
|
|
412
|
+
"Somerset",
|
|
413
|
+
"South Ayrshire",
|
|
414
|
+
"South Lanarkshire",
|
|
415
|
+
"South Yorkshire",
|
|
416
|
+
"Staffordshire",
|
|
417
|
+
"Stirling",
|
|
418
|
+
"Suffolk",
|
|
419
|
+
"Surrey",
|
|
420
|
+
"Swansea",
|
|
421
|
+
"Torfaen",
|
|
422
|
+
"Tyne and Wear",
|
|
423
|
+
"Vale of Glamorgan",
|
|
424
|
+
"Warwickshire",
|
|
425
|
+
"West Dunbart",
|
|
426
|
+
"West Lothian",
|
|
427
|
+
"West Midlands",
|
|
428
|
+
"West Sussex",
|
|
429
|
+
"West Yorkshire",
|
|
430
|
+
"Western Isles",
|
|
431
|
+
"Wiltshire",
|
|
432
|
+
"Worcestershire",
|
|
433
|
+
"Wrexham",
|
|
434
|
+
] as const
|
|
435
|
+
|
|
436
|
+
export type UkCounties = (typeof UK_COUNTIES)[number]
|
|
437
|
+
|
|
438
|
+
export function getNestedProperty(
|
|
439
|
+
obj: Record<string, any>,
|
|
440
|
+
dotPath: string | string[],
|
|
441
|
+
): any {
|
|
442
|
+
if (typeof dotPath === "string") dotPath = dotPath.split(".")
|
|
443
|
+
|
|
444
|
+
let value: unknown = obj
|
|
445
|
+
for (let i = 0; i < dotPath.length; i++) {
|
|
446
|
+
value = (value as Record<string, any>)[dotPath[i]]
|
|
447
|
+
if (
|
|
448
|
+
i !== dotPath.length - 1 &&
|
|
449
|
+
(typeof value !== "object" || value === null)
|
|
450
|
+
)
|
|
451
|
+
return
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
return value
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
export function withKeyPaths(obj: object, delimiter: string = "."): object {
|
|
458
|
+
function _withKeyPaths(obj: object, path: string[]) {
|
|
459
|
+
return Object.fromEntries(
|
|
460
|
+
Object.entries(obj).map(([key, value]) => {
|
|
461
|
+
const _path = [...path, key]
|
|
462
|
+
|
|
463
|
+
if (typeof value === "object") value = _withKeyPaths(value, _path)
|
|
464
|
+
|
|
465
|
+
return [_path.join(delimiter), value]
|
|
466
|
+
}),
|
|
467
|
+
)
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
return _withKeyPaths(obj, [])
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
export function excludeKeyPaths(
|
|
474
|
+
obj: object,
|
|
475
|
+
exclude: string[],
|
|
476
|
+
delimiter: string = ".",
|
|
477
|
+
): any {
|
|
478
|
+
function _excludeKeyPaths(obj: object, path: string[]) {
|
|
479
|
+
return Object.fromEntries(
|
|
480
|
+
Object.entries(obj)
|
|
481
|
+
.map(([key, value]: [string, unknown]) => {
|
|
482
|
+
const _path = [...path, key]
|
|
483
|
+
|
|
484
|
+
if (
|
|
485
|
+
typeof value === "object" &&
|
|
486
|
+
value !== null &&
|
|
487
|
+
!(value instanceof Date)
|
|
488
|
+
)
|
|
489
|
+
value = _excludeKeyPaths(value, _path)
|
|
490
|
+
|
|
491
|
+
return exclude.includes(_path.join(delimiter)) ? [] : [key, value]
|
|
492
|
+
})
|
|
493
|
+
.filter(entry => entry.length),
|
|
494
|
+
)
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
return exclude.length ? _excludeKeyPaths(obj, []) : obj
|
|
498
|
+
}
|