Skip to content

Commit faf03fa

Browse files
Merge pull request #758 from bcgov/SRS/569
feat: SRS: 569 CATS Manage People – Edit Person
2 parents 0f8e557 + f479b15 commit faf03fa

File tree

21 files changed

+6077
-3180
lines changed

21 files changed

+6077
-3180
lines changed

backend/users/package-lock.json

Lines changed: 5118 additions & 2879 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

cats-frontend/src/App.css

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@
6464
/* Sidebar styles */
6565
.sidebar-container {
6666
width: 128px; /* Initial width */
67-
transition: width 0.3s ease; /* Smooth transition for width */
67+
transition: width 0.9s ease; /* Smooth transition for width */
6868
height: 100%; /* Ensure full height of the sidebar */
6969
cursor: pointer;
7070
background: var(--side-bar-background-color);

cats-frontend/src/app/components/form/Form.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,12 +73,14 @@ const Form: React.FC<IFormRendererProps> = ({
7373
customEditLabelCss={field.customEditLabelCss}
7474
customEditInputTextCss={field.customEditInputTextCss}
7575
customPlaceholderCss={field.customPlaceholderCss}
76+
searchCustomInputMenuCss={field.searchCustomInputMenuCss}
77+
searchCustomInputContainerCss={field.searchCustomInputContainerCss}
7678
placeholder={field.placeholder}
7779
value={formData[field.graphQLPropertyName ?? ''] || ''}
7880
onChange={(value) =>
7981
handleInputChange(field.graphQLPropertyName, value)
8082
}
81-
options={field.options || []}
83+
options={field.options || null}
8284
type={FormFieldType.Text}
8385
validation={field.validation}
8486
allowNumbersOnly={field.allowNumbersOnly}
@@ -88,6 +90,7 @@ const Form: React.FC<IFormRendererProps> = ({
8890
customInfoMessage={field.customInfoMessage}
8991
customMenuMessage={field.customMenuMessage}
9092
isDisabled={field.isDisabled}
93+
isSearchCustomInputIcon={field.isSearchCustomInputIcon}
9194
/>
9295
)}
9396
{field.type === FormFieldType.TextArea && (

cats-frontend/src/app/components/input-controls/IFormField.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,11 @@ export enum FormFieldType {
1717
IconButton = "iconbutton",
1818
Icon = "icon",
1919
Switch = 'switch',
20-
Email = 'email',
2120
}
2221

2322
export interface IFormField {
2423
type:
2524
| FormFieldType.Text
26-
| FormFieldType.Email
2725
| FormFieldType.DropDown
2826
| FormFieldType.Date
2927
| FormFieldType.Group
@@ -50,9 +48,11 @@ export interface IFormField {
5048
customLeftIconCss?: string;
5149
customRightIconCss?: string;
5250
customErrorCss?: string;
51+
searchCustomInputContainerCss?: string;
52+
searchCustomInputMenuCss?: string;
5353
graphQLPropertyName?: string;
5454
allowNumbersOnly?: boolean;
55-
options?: { key: string; value: string; imageUrl?: any }[];
55+
options?: { key: string; value: string; imageUrl?: any }[] | null;
5656
filteredOptions?: { key: string; value: string }[];
5757
value?: any;
5858
customLinkValue?: any;
@@ -83,4 +83,5 @@ export interface IFormField {
8383
handleSearch?: (event: any) => void;
8484
componentName?: string;
8585
labelPosition?: "left" | "right";
86+
isSearchCustomInputIcon?: boolean;
8687
}

cats-frontend/src/app/components/input-controls/InputControls.tsx

Lines changed: 32 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ export const Link: React.FC<InputProps> = ({
6969
className={`d-flex pt-1 ${customInputTextCss ?? ''}`}
7070
aria-label={`${label + ' ' + value}`}
7171
state={{ from: componentName ?? '' }}
72+
onClick={onChange}
7273
>
7374
{customIcon && customIcon}{" "}
7475
<span className="ps-1">{customLinkValue ?? value}</span>
@@ -209,7 +210,7 @@ export const TextInput: React.FC<InputProps> = ({
209210
const [error, setError] = useState<string | null>(null);
210211

211212
useEffect(() => {
212-
if (validation?.required) {
213+
if (validation?.required || validation?.pattern) {
213214
setError(null);
214215
validateInput(value);
215216
}
@@ -233,7 +234,7 @@ export const TextInput: React.FC<InputProps> = ({
233234

234235
const handleTextInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
235236
const inputValue = e.target.value;
236-
if (validation?.required) {
237+
if (validation?.required || validation?.pattern) {
237238
validateInput(inputValue);
238239
}
239240

@@ -246,10 +247,6 @@ export const TextInput: React.FC<InputProps> = ({
246247
}
247248
};
248249

249-
const handleCheckBoxChange = (isChecked: boolean) => {
250-
onChange(isChecked);
251-
};
252-
253250
// Replace any spaces in the label with underscores to create a valid id
254251
const inputTxtId = label.replace(/\s+/g, "_") + "_" + v4();
255252
return (
@@ -1226,11 +1223,14 @@ export const SearchCustomInput: React.FC<InputProps> = ({
12261223
customErrorCss,
12271224
customInfoMessage,
12281225
customMenuMessage,
1226+
searchCustomInputContainerCss,
1227+
searchCustomInputMenuCss,
12291228
stickyCol,
12301229
isLoading,
12311230
onChange,
12321231
tableMode,
12331232
isDisabled,
1233+
isSearchCustomInputIcon = true,
12341234
}) => {
12351235
const ContainerElement = tableMode ? "td" : "div";
12361236
const [error, setError] = useState<string | null>(null);
@@ -1395,7 +1395,7 @@ export const SearchCustomInput: React.FC<InputProps> = ({
13951395
if (isOpen) {
13961396
adjustMenuPosition();
13971397
}
1398-
}, [options]);
1398+
}, [options]);
13991399

14001400
const inputTxtId = label.replace(/\s+/g, "_") + "_" + v4();
14011401
return (
@@ -1415,7 +1415,7 @@ export const SearchCustomInput: React.FC<InputProps> = ({
14151415
</label>
14161416
)}
14171417
{isEditing ? (
1418-
<div className="d-flex align-items-center justify-content-center w-100 ">
1418+
<div className={`d-flex align-items-center justify-content-center w-100 ${searchCustomInputContainerCss ?? ""}`}>
14191419
<input
14201420
ref={inputRef}
14211421
type={type}
@@ -1432,26 +1432,28 @@ export const SearchCustomInput: React.FC<InputProps> = ({
14321432
required={error ? true : false}
14331433
disabled={isDisabled}
14341434
/>
1435-
<div className="d-flex align-items-center justify-content-center position-relative custom-search-box-container ">
1436-
{value.length <= 0 ? (
1437-
<span
1438-
id="right-icon"
1439-
data-testid="right-icon"
1440-
className={`${customRightIconCss ?? 'custom-search-icon-position custom-search-icon position-absolute px-2'}`}
1441-
>
1442-
<MagnifyingGlassIcon />
1443-
</span>
1444-
) : (
1445-
<span
1446-
data-testid="left-icon"
1447-
id="left-icon"
1448-
className={`${customLeftIconCss ?? 'custom-clear-icon-position custom-search-icon position-absolute px-2'}`}
1449-
onClick={closeSearch}
1450-
>
1451-
<CircleXMarkIcon />
1452-
</span>
1453-
)}
1454-
</div>
1435+
{ isSearchCustomInputIcon &&
1436+
<div className={"d-flex align-items-center justify-content-center position-relative custom-search-box-container "}>
1437+
{value.length <= 0 ? (
1438+
<span
1439+
id="right-icon"
1440+
data-testid="right-icon"
1441+
className={`${customRightIconCss ?? 'custom-search-icon-position custom-search-icon position-absolute px-2'}`}
1442+
>
1443+
<MagnifyingGlassIcon />
1444+
</span>
1445+
) : (
1446+
<span
1447+
data-testid="left-icon"
1448+
id="left-icon"
1449+
className={`${customLeftIconCss ?? 'custom-clear-icon-position custom-search-icon position-absolute px-2'}`}
1450+
onClick={closeSearch}
1451+
>
1452+
<CircleXMarkIcon />
1453+
</span>
1454+
)}
1455+
</div>
1456+
}
14551457

14561458
{/* Dropdown menu */}
14571459
{options && options?.length >= 0 && isOpen && (
@@ -1461,7 +1463,7 @@ export const SearchCustomInput: React.FC<InputProps> = ({
14611463
menuPosition === 'bottom'
14621464
? 'custom-search-input-menu-bottom'
14631465
: 'custom-search-input-menu-top'
1464-
}`}
1466+
} ${searchCustomInputMenuCss ?? ''}`}
14651467
style={menuPositionStyle}
14661468
role="menu"
14671469
aria-labelledby="search-input-dropdown"
@@ -1514,7 +1516,7 @@ export const SearchCustomInput: React.FC<InputProps> = ({
15141516
</div>
15151517
))
15161518
) : (
1517-
<div className="p-2">{customInfoMessage}</div>
1519+
customInfoMessage && <div className="p-2">{customInfoMessage}</div>
15181520
)}
15191521
</div>
15201522
)}

cats-frontend/src/app/components/modaldialog/ModalDialog.css

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,8 @@
1414
}
1515

1616
.custom-modal-content {
17-
/* padding: 20px; */
18-
/* border: 1px solid #888; */
1917
width: 50%;
2018
max-width: 50%;
21-
/* margin: auto; */
2219
position: relative;
2320
border-radius: var(--layout-border-width-medium);
2421
background: var(--surface-background-white);
@@ -42,8 +39,8 @@
4239
gap: var(--layout-margin-medium);
4340
display: flex;
4441
flex-direction: column;
45-
border-bottom: 1px solid grey;
46-
border-top: 1px solid grey;
42+
border-top: 1px solid var(--surface-border-light, #d8d8d8);
43+
border-bottom: 1px solid var(--surface-border-light, #d8d8d8);
4744
}
4845

4946
.custom-modal-header-text {

cats-frontend/src/app/components/modaldialog/ModalDialog.tsx

Lines changed: 88 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,12 @@ interface ModalDialogCloseHandlerProps {
1414
saveBtnLabel?: string;
1515
cancelBtnLabel?: string;
1616
dicardBtnLabel?: string;
17+
headerLabel?: string;
18+
customHeaderCss?: string;
19+
customHeaderTextCss?: string;
20+
customFooterCss?: string;
1721
discardOption?: boolean;
22+
errorOption?: boolean;
1823
}
1924

2025
const ModalDialog: React.FC<ModalDialogCloseHandlerProps> = ({
@@ -25,14 +30,18 @@ const ModalDialog: React.FC<ModalDialogCloseHandlerProps> = ({
2530
cancelBtnLabel,
2631
dicardBtnLabel,
2732
discardOption,
33+
errorOption,
34+
headerLabel,
35+
customHeaderCss,
36+
customHeaderTextCss,
37+
customFooterCss,
2838
}) => {
2939
saveBtnLabel = saveBtnLabel ?? '';
3040
cancelBtnLabel = cancelBtnLabel ?? '';
3141
dicardBtnLabel = dicardBtnLabel ?? '';
3242

3343
const [open, setOpen] = useState<boolean>(true);
34-
const displayLabel =
35-
label ?? 'Are you sure you want to commit changes to this site?';
44+
const displayLabel = label ?? '';
3645

3746
const handleClose = () => {
3847
setOpen(false);
@@ -53,44 +62,87 @@ const ModalDialog: React.FC<ModalDialogCloseHandlerProps> = ({
5362
return (
5463
<div>
5564
{open && (
56-
<div className="custom-modal">
57-
<div className="custom-modal-content">
58-
<div className="custom-modal-header">
59-
<span className="custom-modal-header-text">{displayLabel}</span>
60-
<XmarkIcon
61-
className="custom-modal-header-close"
62-
onClick={handleClose}
63-
></XmarkIcon>
64-
</div>
65-
{children && <div className="custom-modal-data">{children}</div>}
66-
{!discardOption && (
67-
<div className="custom-modal-actions-footer">
68-
<CancelButton
69-
clickHandler={handleClose}
70-
label={cancelBtnLabel}
71-
/>
72-
<SaveButton clickHandler={handleSave} label={saveBtnLabel} />
73-
</div>
74-
)}
75-
{discardOption && (
76-
<div className="custom-modal-actions-footer">
77-
<CancelButton
78-
clickHandler={handleClose}
79-
label={cancelBtnLabel}
80-
/>
81-
<DiscardButton
82-
clickHandler={handleDiscard}
83-
label={dicardBtnLabel}
84-
showIcon={false}
85-
/>
86-
<SaveButton clickHandler={handleSave} label={saveBtnLabel} />
87-
</div>
88-
)}
65+
<ModalDialogWrapper closeHandler={handleClose}>
66+
<div className={`${customHeaderCss || 'custom-modal-header'}`}>
67+
<span
68+
className={`${customHeaderTextCss || 'custom-modal-header-text'}`}
69+
>
70+
{headerLabel || displayLabel}
71+
</span>
72+
<XmarkIcon
73+
className="custom-modal-header-close"
74+
onClick={handleClose}
75+
></XmarkIcon>
8976
</div>
90-
</div>
77+
{children && <div className="custom-modal-data">{children}</div>}
78+
{!discardOption && !errorOption && (
79+
<div className={`${customFooterCss || 'custom-modal-actions-footer'}`}>
80+
<CancelButton
81+
variant="tertiary"
82+
clickHandler={handleClose}
83+
label={cancelBtnLabel}
84+
/>
85+
<SaveButton variant='primary' clickHandler={handleSave} label={saveBtnLabel} />
86+
</div>
87+
)}
88+
{discardOption && (
89+
<div className={`${customFooterCss || 'custom-modal-actions-footer'}`}>
90+
<CancelButton
91+
variant="tertiary"
92+
clickHandler={handleClose}
93+
label={cancelBtnLabel}
94+
/>
95+
<DiscardButton
96+
clickHandler={handleDiscard}
97+
label={dicardBtnLabel}
98+
showIcon={false}
99+
/>
100+
<SaveButton clickHandler={handleSave} label={saveBtnLabel} />
101+
</div>
102+
)}
103+
{errorOption && (
104+
<div className={`${customFooterCss || 'custom-modal-actions-footer'}`}>
105+
<CancelButton clickHandler={handleClose} label={cancelBtnLabel} />
106+
</div>
107+
)}
108+
</ModalDialogWrapper>
91109
)}
92110
</div>
93111
);
94112
};
95113

96114
export default ModalDialog;
115+
116+
export const ModalDialogWrapper: React.FC<ModalDialogCloseHandlerProps> = ({
117+
closeHandler,
118+
children,
119+
}) => {
120+
return (
121+
<div>
122+
<div className="custom-modal">
123+
<div className="custom-modal-content">{children}</div>
124+
</div>
125+
</div>
126+
);
127+
};
128+
129+
export const ModalDialogHeaderOnly: React.FC<ModalDialogCloseHandlerProps> = ({
130+
closeHandler,
131+
children,
132+
}) => {
133+
return <div className="custom-modal-header">{children}</div>;
134+
};
135+
136+
export const ModalDialogWrapperWithHeader: React.FC<
137+
ModalDialogCloseHandlerProps
138+
> = ({ closeHandler, children }) => {
139+
return (
140+
<div>
141+
<ModalDialogWrapper closeHandler={ModalDialogHeaderOnly}>
142+
<ModalDialogHeaderOnly closeHandler={ModalDialogHeaderOnly}>
143+
{children}
144+
</ModalDialogHeaderOnly>
145+
</ModalDialogWrapper>
146+
</div>
147+
);
148+
};

0 commit comments

Comments
 (0)