codeforlife 2.7.1 → 2.8.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/.github/workflows/main.yml +0 -3
- package/CHANGELOG.md +14 -0
- package/eslint.config.js +17 -0
- package/package.json +45 -24
- package/src/api/createApi.ts +4 -4
- package/src/api/endpoints/authFactor.ts +2 -2
- package/src/api/endpoints/klass.ts +8 -8
- package/src/api/endpoints/school.ts +2 -2
- package/src/api/endpoints/session.ts +2 -1
- package/src/api/endpoints/user.ts +3 -3
- package/src/api/models.ts +1 -1
- package/src/api/schemas.ts +16 -16
- package/src/components/App.tsx +7 -5
- package/src/components/CopyIconButton.test.tsx +1 -1
- package/src/components/CopyIconButton.tsx +2 -2
- package/src/components/Countdown.tsx +3 -3
- package/src/components/DownloadFileButton.tsx +2 -1
- package/src/components/ElevatedAppBar.tsx +5 -5
- package/src/components/Image.tsx +7 -7
- package/src/components/InputFileButton.tsx +2 -2
- package/src/components/ItemizedList.tsx +4 -4
- package/src/components/OrderedGrid.tsx +3 -3
- package/src/components/ScrollIntoViewLink.tsx +1 -2
- package/src/components/SyncError.tsx +1 -1
- package/src/components/TablePagination.tsx +19 -9
- package/src/components/YouTubeVideo.tsx +2 -2
- package/src/components/form/ApiAutocompleteField.tsx +48 -41
- package/src/components/form/AutocompleteField.tsx +22 -11
- package/src/components/form/CheckboxField.tsx +14 -9
- package/src/components/form/CountryField.tsx +5 -4
- package/src/components/form/DatePickerField.tsx +18 -11
- package/src/components/form/EmailField.tsx +1 -1
- package/src/components/form/FirstNameField.tsx +2 -2
- package/src/components/form/Form.tsx +13 -12
- package/src/components/form/PasswordField.tsx +2 -2
- package/src/components/form/RepeatField.tsx +18 -10
- package/src/components/form/SubmitButton.tsx +14 -10
- package/src/components/form/TextField.tsx +17 -11
- package/src/components/form/UkCountyField.tsx +3 -2
- package/src/components/form/index.tsx +35 -28
- package/src/components/page/Banner.tsx +3 -3
- package/src/components/page/Notification.tsx +3 -3
- package/src/components/page/Page.tsx +5 -5
- package/src/components/page/Section.tsx +1 -1
- package/src/components/page/TabBar.tsx +5 -5
- package/src/components/router/Link.tsx +2 -1
- package/src/components/router/LinkButton.tsx +1 -0
- package/src/components/router/LinkIconButton.tsx +1 -0
- package/src/components/router/LinkListItem.tsx +1 -0
- package/src/components/router/LinkTab.tsx +1 -0
- package/src/components/router/Navigate.tsx +2 -2
- package/src/components/router/index.tsx +9 -12
- package/src/components/table/CellStack.tsx +2 -2
- package/src/components/table/index.tsx +2 -4
- package/src/features/InactiveDialog.tsx +2 -2
- package/src/features/ScreenTimeDialog.tsx +3 -6
- package/src/hooks/api.tsx +4 -2
- package/src/hooks/auth.tsx +29 -16
- package/src/hooks/{general.ts → general.tsx} +3 -3
- package/src/hooks/router.tsx +9 -9
- package/src/middlewares/session.ts +15 -10
- package/src/settings/index.ts +3 -6
- package/src/setupTests.ts +1 -0
- package/src/theme/ThemedBox.tsx +9 -9
- package/src/theme/components/MuiButton.ts +1 -1
- package/src/theme/components/MuiCardActions.tsx +1 -1
- package/src/theme/components/MuiContainer.ts +1 -1
- package/src/theme/components/MuiFormControlLabel.ts +1 -1
- package/src/theme/components/MuiFormHelperText.ts +1 -1
- package/src/theme/components/MuiInputBase.ts +1 -1
- package/src/theme/components/MuiLink.ts +1 -1
- package/src/theme/components/MuiListItemText.ts +1 -1
- package/src/theme/components/MuiMenuItem.ts +1 -1
- package/src/theme/components/MuiSelect.ts +2 -2
- package/src/theme/components/MuiTable.ts +1 -1
- package/src/theme/components/MuiTableBody.ts +1 -1
- package/src/theme/components/MuiTableHead.ts +2 -2
- package/src/theme/components/MuiTextField.ts +3 -3
- package/src/theme/components/_components.ts +4 -2
- package/src/theme/palette.ts +2 -2
- package/src/theme/typography.ts +1 -1
- package/src/utils/api.tsx +6 -4
- package/src/utils/auth.ts +1 -1
- package/src/utils/form.test.ts +1 -1
- package/src/utils/form.ts +14 -9
- package/src/utils/general.test.ts +7 -7
- package/src/utils/general.ts +10 -12
- package/src/utils/router.test.ts +1 -1
- package/src/utils/router.ts +2 -2
- package/src/utils/schema.ts +11 -11
- package/src/utils/test.tsx +2 -2
- package/src/utils/theme.tsx +7 -6
- package/src/utils/window.ts +2 -0
- package/tsconfig.app.json +4 -0
- package/tsconfig.json +4 -28
- package/tsconfig.node.json +3 -10
- package/vite.config.ts +1 -1
- package/.eslintrc.json +0 -47
- package/src/vite.config.ts +0 -49
- /package/src/{public/images → images/svg}/brain.svg +0 -0
|
@@ -1,20 +1,23 @@
|
|
|
1
|
-
import { Button,
|
|
2
|
-
import type { TypedUseLazyQuery } from "@reduxjs/toolkit/query/react"
|
|
1
|
+
import { Button, type ChipTypeMap, CircularProgress } from "@mui/material"
|
|
3
2
|
import {
|
|
4
3
|
Children,
|
|
4
|
+
type ElementType,
|
|
5
|
+
type ForwardRefRenderFunction,
|
|
6
|
+
type HTMLAttributes,
|
|
7
|
+
type JSX,
|
|
5
8
|
forwardRef,
|
|
6
9
|
useEffect,
|
|
7
10
|
useState,
|
|
8
|
-
type ElementType,
|
|
9
11
|
} from "react"
|
|
12
|
+
import type { TypedUseLazyQuery } from "@reduxjs/toolkit/query/react"
|
|
10
13
|
|
|
11
14
|
import {
|
|
12
15
|
AutocompleteField,
|
|
13
16
|
type AutocompleteFieldProps,
|
|
14
17
|
} from "../../components/form"
|
|
15
|
-
import { usePagination } from "../../hooks/api"
|
|
16
18
|
import type { ListArg, ListResult, ModelId } from "../../utils/api"
|
|
17
19
|
import SyncError from "../SyncError"
|
|
20
|
+
import { usePagination } from "../../hooks/api"
|
|
18
21
|
|
|
19
22
|
export interface ApiAutocompleteFieldProps<
|
|
20
23
|
SearchKey extends keyof Omit<QueryArg, "limit" | "offset">,
|
|
@@ -62,7 +65,7 @@ const ApiAutocompleteField = <
|
|
|
62
65
|
useLazyListQuery,
|
|
63
66
|
filterOptions,
|
|
64
67
|
getOptionLabel,
|
|
65
|
-
getOptionKey = result => result.id,
|
|
68
|
+
getOptionKey = result => result.id as ModelId,
|
|
66
69
|
searchKey,
|
|
67
70
|
...otherAutocompleteFieldProps
|
|
68
71
|
}: ApiAutocompleteFieldProps<
|
|
@@ -88,7 +91,7 @@ const ApiAutocompleteField = <
|
|
|
88
91
|
useEffect(
|
|
89
92
|
() => {
|
|
90
93
|
const arg = { limit, offset, ...filterOptions } as QueryArg
|
|
91
|
-
// @ts-expect-error
|
|
94
|
+
// @ts-expect-error search key can index arg
|
|
92
95
|
if (search) arg[searchKey] = search
|
|
93
96
|
|
|
94
97
|
trigger(arg, true)
|
|
@@ -130,6 +133,44 @@ const ApiAutocompleteField = <
|
|
|
130
133
|
setPagination(({ page, limit }) => ({ page: page + 1, limit }))
|
|
131
134
|
}
|
|
132
135
|
|
|
136
|
+
const ListboxComponent: ForwardRefRenderFunction<
|
|
137
|
+
unknown,
|
|
138
|
+
HTMLAttributes<HTMLElement>
|
|
139
|
+
> = ({ children, ...props }, ref) => {
|
|
140
|
+
const listItems = Children.toArray(children)
|
|
141
|
+
if (isLoading) listItems.push(<CircularProgress key="is-loading" />)
|
|
142
|
+
else {
|
|
143
|
+
if (isError) listItems.push(<SyncError key="is-error" />)
|
|
144
|
+
if (hasMore) {
|
|
145
|
+
listItems.push(
|
|
146
|
+
<Button key="load-more" onClick={loadNextPage}>
|
|
147
|
+
Load more
|
|
148
|
+
</Button>,
|
|
149
|
+
)
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
return (
|
|
154
|
+
<ul
|
|
155
|
+
{...props}
|
|
156
|
+
// @ts-expect-error ref is assignable
|
|
157
|
+
ref={ref}
|
|
158
|
+
onScroll={event => {
|
|
159
|
+
// If not already loading and scrolled to bottom
|
|
160
|
+
if (
|
|
161
|
+
!isLoading &&
|
|
162
|
+
event.currentTarget.clientHeight + event.currentTarget.scrollTop >=
|
|
163
|
+
event.currentTarget.scrollHeight
|
|
164
|
+
) {
|
|
165
|
+
loadNextPage()
|
|
166
|
+
}
|
|
167
|
+
}}
|
|
168
|
+
>
|
|
169
|
+
{listItems}
|
|
170
|
+
</ul>
|
|
171
|
+
)
|
|
172
|
+
}
|
|
173
|
+
|
|
133
174
|
return (
|
|
134
175
|
<AutocompleteField
|
|
135
176
|
options={optionKeys}
|
|
@@ -137,41 +178,7 @@ const ApiAutocompleteField = <
|
|
|
137
178
|
onInputChange={(_, value, reason) => {
|
|
138
179
|
setSearch(reason === "input" ? value : "")
|
|
139
180
|
}}
|
|
140
|
-
ListboxComponent={forwardRef(
|
|
141
|
-
const listItems = Children.toArray(children)
|
|
142
|
-
if (isLoading) listItems.push(<CircularProgress key="is-loading" />)
|
|
143
|
-
else {
|
|
144
|
-
if (isError) listItems.push(<SyncError key="is-error" />)
|
|
145
|
-
if (hasMore) {
|
|
146
|
-
listItems.push(
|
|
147
|
-
<Button key="load-more" onClick={loadNextPage}>
|
|
148
|
-
Load more
|
|
149
|
-
</Button>,
|
|
150
|
-
)
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
return (
|
|
155
|
-
<ul
|
|
156
|
-
{...props}
|
|
157
|
-
// @ts-expect-error
|
|
158
|
-
ref={ref}
|
|
159
|
-
onScroll={event => {
|
|
160
|
-
// If not already loading and scrolled to bottom
|
|
161
|
-
if (
|
|
162
|
-
!isLoading &&
|
|
163
|
-
event.currentTarget.clientHeight +
|
|
164
|
-
event.currentTarget.scrollTop >=
|
|
165
|
-
event.currentTarget.scrollHeight
|
|
166
|
-
) {
|
|
167
|
-
loadNextPage()
|
|
168
|
-
}
|
|
169
|
-
}}
|
|
170
|
-
>
|
|
171
|
-
{listItems}
|
|
172
|
-
</ul>
|
|
173
|
-
)
|
|
174
|
-
})}
|
|
181
|
+
ListboxComponent={forwardRef(ListboxComponent)}
|
|
175
182
|
{...otherAutocompleteFieldProps}
|
|
176
183
|
/>
|
|
177
184
|
)
|
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
import {
|
|
2
2
|
Autocomplete,
|
|
3
|
-
TextField,
|
|
4
3
|
type AutocompleteProps,
|
|
5
4
|
type ChipTypeMap,
|
|
5
|
+
TextField,
|
|
6
6
|
type TextFieldProps,
|
|
7
7
|
} from "@mui/material"
|
|
8
|
+
import { type ElementType, type JSX } from "react"
|
|
8
9
|
import { Field, type FieldConfig, type FieldProps } from "formik"
|
|
9
|
-
import { type ElementType } from "react"
|
|
10
10
|
import {
|
|
11
|
+
type ValidateOptions,
|
|
11
12
|
number as YupNumber,
|
|
12
13
|
string as YupString,
|
|
13
|
-
type ValidateOptions,
|
|
14
14
|
} from "yup"
|
|
15
15
|
|
|
16
|
-
import { schemaToFieldValidator } from "../../utils/form"
|
|
16
|
+
import { type FormValues, schemaToFieldValidator } from "../../utils/form"
|
|
17
17
|
import { getNestedProperty } from "../../utils/general"
|
|
18
18
|
|
|
19
19
|
export interface AutocompleteFieldProps<
|
|
@@ -86,17 +86,28 @@ const AutocompleteField = <
|
|
|
86
86
|
return (
|
|
87
87
|
<Field {...fieldConfig}>
|
|
88
88
|
{({ form, meta }: FieldProps) => {
|
|
89
|
-
const value = getNestedProperty(
|
|
90
|
-
|
|
91
|
-
|
|
89
|
+
const value = getNestedProperty(
|
|
90
|
+
form.values as FormValues,
|
|
91
|
+
dotPath,
|
|
92
|
+
) as string
|
|
93
|
+
const touched = getNestedProperty(form.touched, dotPath) as boolean
|
|
94
|
+
const error = getNestedProperty(form.errors, dotPath) as
|
|
95
|
+
| string
|
|
96
|
+
| undefined
|
|
92
97
|
|
|
93
98
|
return (
|
|
94
99
|
<Autocomplete
|
|
95
100
|
options={options}
|
|
101
|
+
// @ts-expect-error value can be assigned
|
|
96
102
|
defaultValue={
|
|
97
|
-
meta.initialValue === ""
|
|
103
|
+
meta.initialValue === ""
|
|
104
|
+
? undefined
|
|
105
|
+
: (meta.initialValue as string)
|
|
98
106
|
}
|
|
99
|
-
renderInput={({
|
|
107
|
+
renderInput={({
|
|
108
|
+
id: _, // eslint-disable-line @typescript-eslint/no-unused-vars
|
|
109
|
+
...otherParams
|
|
110
|
+
}) => (
|
|
100
111
|
<TextField
|
|
101
112
|
id={id ?? name}
|
|
102
113
|
name={name}
|
|
@@ -104,13 +115,13 @@ const AutocompleteField = <
|
|
|
104
115
|
type="text" // Force to be string to avoid number incrementor/decrementor
|
|
105
116
|
value={value}
|
|
106
117
|
error={touched && Boolean(error)}
|
|
107
|
-
helperText={
|
|
118
|
+
helperText={touched && error}
|
|
108
119
|
{...otherTextFieldProps}
|
|
109
120
|
{...otherParams}
|
|
110
121
|
/>
|
|
111
122
|
)}
|
|
112
123
|
onChange={(_, value) => {
|
|
113
|
-
form.setFieldValue(name, value ?? undefined, true)
|
|
124
|
+
void form.setFieldValue(name, value ?? undefined, true)
|
|
114
125
|
}}
|
|
115
126
|
onBlur={form.handleBlur}
|
|
116
127
|
{...otherAutocompleteProps}
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import {
|
|
2
2
|
Checkbox,
|
|
3
|
+
type CheckboxProps,
|
|
3
4
|
FormControl,
|
|
4
5
|
FormControlLabel,
|
|
5
|
-
FormHelperText,
|
|
6
|
-
type CheckboxProps,
|
|
7
6
|
type FormControlLabelProps,
|
|
7
|
+
FormHelperText,
|
|
8
8
|
} from "@mui/material"
|
|
9
9
|
import { Field, type FieldConfig, type FieldProps } from "formik"
|
|
10
|
+
import { type ValidateOptions, bool as YupBool } from "yup"
|
|
10
11
|
import { type FC } from "react"
|
|
11
|
-
import { bool as YupBool, type ValidateOptions } from "yup"
|
|
12
12
|
|
|
13
|
-
import { schemaToFieldValidator } from "../../utils/form"
|
|
13
|
+
import { type FormValues, schemaToFieldValidator } from "../../utils/form"
|
|
14
14
|
import { getNestedProperty } from "../../utils/general"
|
|
15
15
|
|
|
16
16
|
export interface CheckboxFieldProps
|
|
@@ -47,9 +47,14 @@ const CheckboxField: FC<CheckboxFieldProps> = ({
|
|
|
47
47
|
return (
|
|
48
48
|
<Field {...fieldConfig}>
|
|
49
49
|
{({ form, meta }: FieldProps) => {
|
|
50
|
-
const touched = getNestedProperty(form.touched, dotPath)
|
|
51
|
-
const error = getNestedProperty(form.errors, dotPath)
|
|
52
|
-
|
|
50
|
+
const touched = getNestedProperty(form.touched, dotPath) as boolean
|
|
51
|
+
const error = getNestedProperty(form.errors, dotPath) as
|
|
52
|
+
| string
|
|
53
|
+
| undefined
|
|
54
|
+
const value = getNestedProperty(
|
|
55
|
+
form.values as FormValues,
|
|
56
|
+
dotPath,
|
|
57
|
+
) as boolean
|
|
53
58
|
|
|
54
59
|
const hasError = touched && Boolean(error)
|
|
55
60
|
|
|
@@ -59,7 +64,7 @@ const CheckboxField: FC<CheckboxFieldProps> = ({
|
|
|
59
64
|
<FormControlLabel
|
|
60
65
|
control={
|
|
61
66
|
<Checkbox
|
|
62
|
-
defaultChecked={meta.initialValue}
|
|
67
|
+
defaultChecked={meta.initialValue as boolean}
|
|
63
68
|
id={id ?? name}
|
|
64
69
|
name={name}
|
|
65
70
|
value={value}
|
|
@@ -70,7 +75,7 @@ const CheckboxField: FC<CheckboxFieldProps> = ({
|
|
|
70
75
|
}
|
|
71
76
|
{...formControlLabelProps}
|
|
72
77
|
/>
|
|
73
|
-
{hasError && <FormHelperText>{error
|
|
78
|
+
{hasError && <FormHelperText>{error}</FormHelperText>}
|
|
74
79
|
</FormControl>
|
|
75
80
|
)
|
|
76
81
|
}}
|
|
@@ -1,13 +1,14 @@
|
|
|
1
|
+
import { type ElementType, type JSX } from "react"
|
|
1
2
|
import { type ChipTypeMap } from "@mui/material"
|
|
2
|
-
|
|
3
|
+
|
|
4
|
+
import AutocompleteField, {
|
|
5
|
+
type AutocompleteFieldProps,
|
|
6
|
+
} from "./AutocompleteField"
|
|
3
7
|
import {
|
|
4
8
|
COUNTRY_ISO_CODES,
|
|
5
9
|
COUNTRY_ISO_CODE_MAPPING,
|
|
6
10
|
type CountryIsoCodes,
|
|
7
11
|
} from "../../utils/general"
|
|
8
|
-
import AutocompleteField, {
|
|
9
|
-
type AutocompleteFieldProps,
|
|
10
|
-
} from "./AutocompleteField"
|
|
11
12
|
|
|
12
13
|
export interface CountryFieldProps<
|
|
13
14
|
Multiple extends boolean | undefined = false,
|
|
@@ -1,16 +1,17 @@
|
|
|
1
|
+
import "dayjs/locale/en-gb"
|
|
1
2
|
import {
|
|
2
3
|
DatePicker,
|
|
3
|
-
LocalizationProvider,
|
|
4
4
|
type DatePickerProps,
|
|
5
|
+
LocalizationProvider,
|
|
5
6
|
type PickerValidDate,
|
|
6
7
|
} from "@mui/x-date-pickers"
|
|
7
|
-
import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs"
|
|
8
|
-
import dayjs, { type Dayjs } from "dayjs"
|
|
9
|
-
import "dayjs/locale/en-gb"
|
|
10
8
|
import { Field, type FieldConfig, type FieldProps } from "formik"
|
|
11
|
-
import { date as YupDate
|
|
9
|
+
import { type ValidateOptions, date as YupDate } from "yup"
|
|
10
|
+
import dayjs, { type Dayjs } from "dayjs"
|
|
11
|
+
import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs"
|
|
12
|
+
import { type JSX } from "react"
|
|
12
13
|
|
|
13
|
-
import { schemaToFieldValidator } from "../../utils/form"
|
|
14
|
+
import { type FormValues, schemaToFieldValidator } from "../../utils/form"
|
|
14
15
|
import { getNestedProperty } from "../../utils/general"
|
|
15
16
|
|
|
16
17
|
export interface DatePickerFieldProps<
|
|
@@ -69,14 +70,19 @@ const DatePickerField = <
|
|
|
69
70
|
return (
|
|
70
71
|
<Field {...fieldConfig}>
|
|
71
72
|
{({ form }: FieldProps) => {
|
|
72
|
-
const error = getNestedProperty(form.errors, dotPath)
|
|
73
|
-
|
|
74
|
-
|
|
73
|
+
const error = getNestedProperty(form.errors, dotPath) as
|
|
74
|
+
| string
|
|
75
|
+
| undefined
|
|
76
|
+
const touched = getNestedProperty(form.touched, dotPath) as boolean
|
|
77
|
+
let value: Dayjs | null | string = getNestedProperty(
|
|
78
|
+
form.values as FormValues,
|
|
79
|
+
dotPath,
|
|
80
|
+
) as string
|
|
75
81
|
|
|
76
82
|
value = value ? dayjs(value) : null
|
|
77
83
|
|
|
78
84
|
function handleChange(value: Dayjs | null) {
|
|
79
|
-
form.setFieldValue(
|
|
85
|
+
void form.setFieldValue(
|
|
80
86
|
name,
|
|
81
87
|
value && value.isValid() ? value.format("YYYY-MM-DD") : null,
|
|
82
88
|
true,
|
|
@@ -88,6 +94,7 @@ const DatePickerField = <
|
|
|
88
94
|
dateAdapter={AdapterDayjs}
|
|
89
95
|
adapterLocale="en-gb"
|
|
90
96
|
>
|
|
97
|
+
{/* @ts-expect-error value is compatible */}
|
|
91
98
|
<DatePicker
|
|
92
99
|
name={name}
|
|
93
100
|
value={value}
|
|
@@ -98,7 +105,7 @@ const DatePickerField = <
|
|
|
98
105
|
textField: {
|
|
99
106
|
id: name,
|
|
100
107
|
onChange: value => {
|
|
101
|
-
// @ts-expect-error
|
|
108
|
+
// @ts-expect-error value is compatible
|
|
102
109
|
handleChange(value as Dayjs | null)
|
|
103
110
|
},
|
|
104
111
|
onBlur: form.handleBlur,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { EmailOutlined as EmailOutlinedIcon } from "@mui/icons-material"
|
|
2
|
-
import { InputAdornment } from "@mui/material"
|
|
3
2
|
import type { FC } from "react"
|
|
3
|
+
import { InputAdornment } from "@mui/material"
|
|
4
4
|
import { string as YupString } from "yup"
|
|
5
5
|
|
|
6
6
|
import TextField, { type TextFieldProps } from "./TextField"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { PersonOutlined as PersonOutlinedIcon } from "@mui/icons-material"
|
|
2
|
-
import { InputAdornment } from "@mui/material"
|
|
3
1
|
import type { FC } from "react"
|
|
2
|
+
import { InputAdornment } from "@mui/material"
|
|
3
|
+
import { PersonOutlined as PersonOutlinedIcon } from "@mui/icons-material"
|
|
4
4
|
|
|
5
5
|
import TextField, { type TextFieldProps } from "./TextField"
|
|
6
6
|
import { schemas } from "../../api"
|
|
@@ -1,26 +1,27 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type FC,
|
|
3
|
+
type JSX,
|
|
4
|
+
type ReactNode,
|
|
5
|
+
type RefObject,
|
|
6
|
+
useEffect,
|
|
7
|
+
useRef,
|
|
8
|
+
} from "react"
|
|
1
9
|
import { FormHelperText, type FormHelperTextProps } from "@mui/material"
|
|
2
10
|
import {
|
|
3
11
|
Formik,
|
|
4
|
-
Form as FormikForm,
|
|
5
12
|
type FormikConfig,
|
|
6
13
|
type FormikErrors,
|
|
14
|
+
Form as FormikForm,
|
|
7
15
|
type FormikProps,
|
|
8
16
|
} from "formik"
|
|
9
|
-
import {
|
|
10
|
-
type ReactNode,
|
|
11
|
-
type FC,
|
|
12
|
-
useRef,
|
|
13
|
-
useEffect,
|
|
14
|
-
type RefObject,
|
|
15
|
-
} from "react"
|
|
16
17
|
import type { TypedUseMutation } from "@reduxjs/toolkit/query/react"
|
|
17
18
|
|
|
18
|
-
import { getKeyPaths } from "../../utils/general"
|
|
19
19
|
import {
|
|
20
|
-
submitForm,
|
|
21
|
-
type SubmitFormOptions,
|
|
22
20
|
type FormValues,
|
|
21
|
+
type SubmitFormOptions,
|
|
22
|
+
submitForm,
|
|
23
23
|
} from "../../utils/form"
|
|
24
|
+
import { getKeyPaths } from "../../utils/general"
|
|
24
25
|
|
|
25
26
|
const SCROLL_INTO_VIEW_OPTIONS: ScrollIntoViewOptions = {
|
|
26
27
|
behavior: "smooth",
|
|
@@ -67,7 +68,7 @@ const BaseForm = <Values extends FormValues>({
|
|
|
67
68
|
...otherFormikProps
|
|
68
69
|
}: BaseFormProps<Values>) => (
|
|
69
70
|
<Formik {...otherFormikProps}>
|
|
70
|
-
{/* @ts-expect-error */}
|
|
71
|
+
{/* @ts-expect-error value is assignable */}
|
|
71
72
|
{(formik: _FormikProps<Values>) => {
|
|
72
73
|
const hasErrors = Boolean(Object.keys(formik.errors).length)
|
|
73
74
|
const hasNonFieldErrors =
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
+
import { type FC, useState } from "react"
|
|
2
|
+
import { IconButton, InputAdornment } from "@mui/material"
|
|
1
3
|
import {
|
|
2
4
|
Visibility as VisibilityIcon,
|
|
3
5
|
VisibilityOff as VisibilityOffIcon,
|
|
4
6
|
} from "@mui/icons-material"
|
|
5
|
-
import { IconButton, InputAdornment } from "@mui/material"
|
|
6
|
-
import { useState, type FC } from "react"
|
|
7
7
|
import { string as YupString } from "yup"
|
|
8
8
|
|
|
9
9
|
import RepeatField, { type RepeatFieldProps } from "./RepeatField"
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
import { TextField as MuiTextField, type TextFieldProps } from "@mui/material"
|
|
2
|
-
import { Field, type FieldConfig, type FieldProps } from "formik"
|
|
3
1
|
import {
|
|
4
|
-
useEffect,
|
|
5
|
-
useState,
|
|
6
2
|
type Dispatch,
|
|
7
3
|
type FC,
|
|
8
4
|
type SetStateAction,
|
|
5
|
+
useEffect,
|
|
6
|
+
useState,
|
|
9
7
|
} from "react"
|
|
10
|
-
import {
|
|
8
|
+
import { Field, type FieldConfig, type FieldProps } from "formik"
|
|
9
|
+
import { TextField as MuiTextField, type TextFieldProps } from "@mui/material"
|
|
10
|
+
import { type ValidateOptions, string as YupString } from "yup"
|
|
11
11
|
|
|
12
|
-
import { schemaToFieldValidator } from "../../utils/form"
|
|
12
|
+
import { type FormValues, schemaToFieldValidator } from "../../utils/form"
|
|
13
13
|
import { getNestedProperty } from "../../utils/general"
|
|
14
14
|
|
|
15
15
|
export type RepeatFieldProps = Omit<
|
|
@@ -47,12 +47,20 @@ const TextField: FC<
|
|
|
47
47
|
const { form } = fieldProps
|
|
48
48
|
|
|
49
49
|
const dotPath = name.split(".")
|
|
50
|
-
const value = getNestedProperty(form.values, dotPath)
|
|
50
|
+
const value = getNestedProperty(form.values as FormValues, dotPath) as string
|
|
51
51
|
|
|
52
52
|
const repeatDotPath = repeatName.split(".")
|
|
53
|
-
const repeatValue = getNestedProperty(
|
|
54
|
-
|
|
55
|
-
|
|
53
|
+
const repeatValue = getNestedProperty(
|
|
54
|
+
form.values as FormValues,
|
|
55
|
+
repeatDotPath,
|
|
56
|
+
) as string
|
|
57
|
+
const repeatTouched = getNestedProperty(
|
|
58
|
+
form.touched,
|
|
59
|
+
repeatDotPath,
|
|
60
|
+
) as boolean
|
|
61
|
+
const repeatError = getNestedProperty(form.errors, repeatDotPath) as
|
|
62
|
+
| string
|
|
63
|
+
| undefined
|
|
56
64
|
|
|
57
65
|
useEffect(() => {
|
|
58
66
|
setValue(value)
|
|
@@ -2,6 +2,8 @@ import { Button, type ButtonProps } from "@mui/material"
|
|
|
2
2
|
import { Field, type FieldProps } from "formik"
|
|
3
3
|
import type { FC } from "react"
|
|
4
4
|
|
|
5
|
+
import { type FormValues } from "../../utils/form"
|
|
6
|
+
|
|
5
7
|
export interface SubmitButtonProps
|
|
6
8
|
extends Omit<ButtonProps, "type" | "onClick"> {}
|
|
7
9
|
|
|
@@ -15,7 +17,7 @@ const SubmitButton: FC<SubmitButtonProps> = ({
|
|
|
15
17
|
) {
|
|
16
18
|
touched = touched || {}
|
|
17
19
|
for (const key in values) {
|
|
18
|
-
const value = values[key]
|
|
20
|
+
const value: unknown = values[key]
|
|
19
21
|
touched[key] =
|
|
20
22
|
value instanceof Object && value.constructor === Object
|
|
21
23
|
? getTouched(value, touched)
|
|
@@ -31,15 +33,17 @@ const SubmitButton: FC<SubmitButtonProps> = ({
|
|
|
31
33
|
<Button
|
|
32
34
|
type="button"
|
|
33
35
|
onClick={() => {
|
|
34
|
-
form
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
36
|
+
void form
|
|
37
|
+
.setTouched(getTouched(form.values as FormValues), true)
|
|
38
|
+
.then(errors => {
|
|
39
|
+
const hasErrors = Boolean(errors && Object.keys(errors).length)
|
|
40
|
+
// If has errors, set isSubmitting=true so fields in the form are
|
|
41
|
+
// aware that a submission was attempted. Else, set
|
|
42
|
+
// isSubmitting=false as it will be set to true when calling
|
|
43
|
+
// submitForm().
|
|
44
|
+
form.setSubmitting(hasErrors)
|
|
45
|
+
if (!hasErrors) void form.submitForm()
|
|
46
|
+
})
|
|
43
47
|
}}
|
|
44
48
|
{...otherButtonProps}
|
|
45
49
|
>
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
+
import { type FC, useEffect, useState } from "react"
|
|
2
|
+
import { Field, type FieldConfig, type FieldProps } from "formik"
|
|
1
3
|
import {
|
|
2
4
|
TextField as MuiTextField,
|
|
3
5
|
type TextFieldProps as MuiTextFieldProps,
|
|
4
6
|
} from "@mui/material"
|
|
5
|
-
import { Field, type FieldConfig, type FieldProps } from "formik"
|
|
6
|
-
import { type FC, useState, useEffect } from "react"
|
|
7
7
|
import { type StringSchema, type ValidateOptions, array as YupArray } from "yup"
|
|
8
8
|
|
|
9
|
-
import { schemaToFieldValidator } from "../../utils/form"
|
|
9
|
+
import { type FormValues, schemaToFieldValidator } from "../../utils/form"
|
|
10
10
|
import { getNestedProperty } from "../../utils/general"
|
|
11
11
|
|
|
12
12
|
export type TextFieldProps = Omit<
|
|
@@ -79,7 +79,7 @@ const TextField: FC<TextFieldProps> = ({
|
|
|
79
79
|
return (
|
|
80
80
|
new Set(
|
|
81
81
|
uniqueCaseInsensitive
|
|
82
|
-
? values.map(value =>
|
|
82
|
+
? values.map(value => value.toLowerCase())
|
|
83
83
|
: values,
|
|
84
84
|
).size === values.length
|
|
85
85
|
)
|
|
@@ -104,18 +104,24 @@ const TextField: FC<TextFieldProps> = ({
|
|
|
104
104
|
validate: schemaToFieldValidator(buildSchema(), validateOptions),
|
|
105
105
|
}
|
|
106
106
|
|
|
107
|
-
const
|
|
108
|
-
const initialValue = getNestedProperty(
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
107
|
+
const FieldInternal: FC<FieldProps> = ({ form }) => {
|
|
108
|
+
const initialValue = getNestedProperty(
|
|
109
|
+
form.initialValues as FormValues,
|
|
110
|
+
dotPath,
|
|
111
|
+
) as string
|
|
112
|
+
const value = getNestedProperty(
|
|
113
|
+
form.values as FormValues,
|
|
114
|
+
dotPath,
|
|
115
|
+
) as string
|
|
116
|
+
const error = getNestedProperty(form.errors, dotPath) as string | undefined
|
|
117
|
+
const touched = getNestedProperty(form.touched, dotPath) as boolean
|
|
112
118
|
|
|
113
119
|
useEffect(() => {
|
|
114
120
|
setInitialValue(initialValue)
|
|
115
121
|
}, [initialValue])
|
|
116
122
|
|
|
117
123
|
useEffect(() => {
|
|
118
|
-
form.setFieldValue(
|
|
124
|
+
void form.setFieldValue(
|
|
119
125
|
name,
|
|
120
126
|
split && typeof value === "string" ? value.split(split) : value,
|
|
121
127
|
true,
|
|
@@ -138,7 +144,7 @@ const TextField: FC<TextFieldProps> = ({
|
|
|
138
144
|
)
|
|
139
145
|
}
|
|
140
146
|
|
|
141
|
-
return <Field {...fieldConfig}>{
|
|
147
|
+
return <Field {...fieldConfig}>{FieldInternal}</Field>
|
|
142
148
|
}
|
|
143
149
|
|
|
144
150
|
export default TextField
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
+
import { type ElementType, type JSX } from "react"
|
|
1
2
|
import { type ChipTypeMap } from "@mui/material"
|
|
2
|
-
|
|
3
|
-
import { UK_COUNTIES } from "../../utils/general"
|
|
3
|
+
|
|
4
4
|
import AutocompleteField, {
|
|
5
5
|
type AutocompleteFieldProps,
|
|
6
6
|
} from "./AutocompleteField"
|
|
7
|
+
import { UK_COUNTIES } from "../../utils/general"
|
|
7
8
|
|
|
8
9
|
export interface UkCountyFieldProps<
|
|
9
10
|
Multiple extends boolean | undefined = false,
|
|
@@ -1,28 +1,35 @@
|
|
|
1
|
-
export
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
export
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
export
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
export
|
|
14
|
-
export {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
export { default as
|
|
19
|
-
export
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
export
|
|
24
|
-
export { default as
|
|
25
|
-
export
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
1
|
+
export {
|
|
2
|
+
default as ApiAutocompleteField,
|
|
3
|
+
type ApiAutocompleteFieldProps,
|
|
4
|
+
} from "./ApiAutocompleteField"
|
|
5
|
+
export {
|
|
6
|
+
default as AutocompleteField,
|
|
7
|
+
type AutocompleteFieldProps,
|
|
8
|
+
} from "./AutocompleteField"
|
|
9
|
+
export {
|
|
10
|
+
default as CheckboxField,
|
|
11
|
+
type CheckboxFieldProps,
|
|
12
|
+
} from "./CheckboxField"
|
|
13
|
+
export { default as CountryField, type CountryFieldProps } from "./CountryField"
|
|
14
|
+
export {
|
|
15
|
+
default as DatePickerField,
|
|
16
|
+
type DatePickerFieldProps,
|
|
17
|
+
} from "./DatePickerField"
|
|
18
|
+
export { default as EmailField, type EmailFieldProps } from "./EmailField"
|
|
19
|
+
export {
|
|
20
|
+
default as FirstNameField,
|
|
21
|
+
type FirstNameFieldProps,
|
|
22
|
+
} from "./FirstNameField"
|
|
23
|
+
export { default as Form, type FormProps, type FormErrors } from "./Form"
|
|
24
|
+
export { default as OtpField, type OtpFieldProps } from "./OtpField"
|
|
25
|
+
export {
|
|
26
|
+
default as PasswordField,
|
|
27
|
+
type PasswordFieldProps,
|
|
28
|
+
} from "./PasswordField"
|
|
29
|
+
export { default as RepeatField, type RepeatFieldProps } from "./RepeatField"
|
|
30
|
+
export { default as SubmitButton, type SubmitButtonProps } from "./SubmitButton"
|
|
31
|
+
export { default as TextField, type TextFieldProps } from "./TextField"
|
|
32
|
+
export {
|
|
33
|
+
default as UkCountyField,
|
|
34
|
+
type UkCountyFieldProps,
|
|
35
|
+
} from "./UkCountyField"
|