Skip to content

Commit 601faff

Browse files
committed
fix: settings page (addAdmin modal + select all table)
1 parent 5fc174b commit 601faff

File tree

8 files changed

+196
-51
lines changed

8 files changed

+196
-51
lines changed

client/src/components/admin/AddPreview.tsx

Lines changed: 42 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -109,14 +109,24 @@ const AddPreview = ({
109109
}
110110
}
111111

112-
await backend.post(`/caseManagers`, {
112+
const cmResponse = await backend.post(`/caseManagers`, {
113113
role: roleDict[userType],
114114
firstName: newUser.firstName,
115115
lastName: newUser.lastName,
116116
phoneNumber: newUser.phoneNumber,
117117
email: newUser.email,
118118
notes: newUser.notes
119119
});
120+
121+
const cm_id = cmResponse.data[0].id;
122+
123+
await backend.post(`/locations`, {
124+
cmId: cm_id,
125+
name: newUser.location,
126+
date: new Date(),
127+
caloptimaFunded: false,
128+
});
129+
120130

121131
onClose();
122132

@@ -127,6 +137,8 @@ const AddPreview = ({
127137
duration: 9000,
128138
isClosable: true,
129139
});
140+
141+
window.location.reload();
130142
} catch (e) {
131143
console.error(e);
132144

@@ -167,18 +179,22 @@ const AddPreview = ({
167179
) => {
168180
const { name, value } = e.target;
169181

170-
if (name === "name") {
171-
const name = value.split(" ");
182+
if (name === "firstName") {
183+
setNewUser((prev) => ({
184+
...prev,
185+
firstName: value,
186+
}));
172187

173-
if (name.length >= 2) {
174-
setNewUser((prev) => ({
175-
...prev,
176-
firstName: name[0] ?? "",
177-
lastName: name[1] ?? "",
178-
}));
188+
return;
189+
}
179190

180-
return;
181-
}
191+
if (name === "lastName") {
192+
setNewUser((prev) => ({
193+
...prev,
194+
lastName: value,
195+
}));
196+
197+
return;
182198
}
183199

184200
setNewUser((prev) => ({
@@ -224,12 +240,24 @@ const AddPreview = ({
224240
<Table variant="simple">
225241
<Tbody>
226242
<Tr>
227-
<Td fontSize="medium">Name</Td>
243+
<Td fontSize="medium">First Name</Td>
244+
<Td>
245+
<TextInputComponent
246+
name="firstName"
247+
type="text"
248+
value={`${newUser.firstName}`}
249+
onChange={handleChange}
250+
width="100%"
251+
/>
252+
</Td>
253+
</Tr>
254+
<Tr>
255+
<Td fontSize="medium">Last Name</Td>
228256
<Td>
229257
<TextInputComponent
230-
name="name"
258+
name="lastName"
231259
type="text"
232-
value={`${newUser.firstName} ${newUser.lastName}`}
260+
value={`${newUser.lastName}`}
233261
onChange={handleChange}
234262
width="100%"
235263
/>

client/src/components/admin/DeleteUserModal.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ export const DeleteUserModal = ({ isOpen, onClose, title, onSubmit } : ModalProp
6666
Cancel
6767
</Button>
6868
<Button
69-
colorScheme="blue"
69+
colorScheme="red"
7070
onClick={() => {
7171
onSubmit();
7272
onClose();

client/src/components/admin/ManageAccounts.tsx

Lines changed: 77 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import {
1919
Text,
2020
Th,
2121
Thead,
22+
Tooltip,
2223
Tr,
2324
useDisclosure,
2425
useToast,
@@ -87,6 +88,7 @@ export const ManageAccounts = () => {
8788
} = useDisclosure();
8889
const [editDrawerOpened, setEditDrawerOpened] = useState(false);
8990
const [selectedRowIds, setSelectedRowIds] = useState<number[]>([]);
91+
const [checkboxMode, setCheckboxMode] = useState<'hidden' | 'visible-unchecked' | 'visible-checked'>('hidden');
9092
const toast = useToast();
9193

9294
const buttonStyle = {
@@ -112,10 +114,20 @@ export const ManageAccounts = () => {
112114
}
113115
};
114116

117+
115118
const handleSelectAllCheckboxClick = () => {
116-
if (selectedRowIds.length === 0) {
117-
setSelectedRowIds(persons.map((person) => person.id));
119+
if (checkboxMode === 'hidden') {
120+
// State 1 -> State 2: Show checkboxes and select all
121+
setCheckboxMode('visible-checked');
122+
const allIds = persons.map((person) => person.id);
123+
setSelectedRowIds(allIds);
124+
} else if (checkboxMode === 'visible-checked') {
125+
// State 2 -> State 3: Keep checkboxes visible but uncheck all
126+
setCheckboxMode('visible-unchecked');
127+
setSelectedRowIds([]);
118128
} else {
129+
// State 3 -> State 1: Hide checkboxes
130+
setCheckboxMode('hidden');
119131
setSelectedRowIds([]);
120132
}
121133
};
@@ -173,12 +185,12 @@ export const ManageAccounts = () => {
173185
() => [
174186
{
175187
id: "rowNumber",
176-
header: ({ table }) => {
188+
header: () => {
177189
return (
178190
<Box textAlign="center">
179191
<Checkbox
180-
isChecked={selectedRowIds.length > 0}
181-
isIndeterminate={table.getIsSomeRowsSelected()}
192+
isChecked={checkboxMode === 'visible-checked'}
193+
isIndeterminate={checkboxMode === 'visible-unchecked'}
182194
onChange={handleSelectAllCheckboxClick}
183195
/>
184196
</Box>
@@ -208,8 +220,44 @@ export const ManageAccounts = () => {
208220
accessorKey: "location",
209221
header: "Site",
210222
},
223+
{
224+
accessorKey: "notes",
225+
header: "Notes",
226+
cell: ({ getValue }) => {
227+
const notes = getValue() as string;
228+
const maxLength = 20; // Maximum characters to show before truncation
229+
230+
if (!notes || notes.length === 0) {
231+
return <Text color="gray.400" fontStyle="italic">No notes</Text>;
232+
}
233+
234+
const truncatedNotes = notes.length > maxLength
235+
? `${notes.substring(0, maxLength)}...`
236+
: notes;
237+
238+
return (
239+
<Tooltip
240+
label={notes}
241+
placement="top"
242+
hasArrow
243+
maxW="400px"
244+
whiteSpace="pre-wrap"
245+
>
246+
<Text
247+
maxW="200px"
248+
overflow="hidden"
249+
textOverflow="ellipsis"
250+
whiteSpace="nowrap"
251+
_hover={{ color: "blue.500" }}
252+
>
253+
{truncatedNotes}
254+
</Text>
255+
</Tooltip>
256+
);
257+
},
258+
},
211259
],
212-
[]
260+
[selectedRowIds, persons, handleSelectAllCheckboxClick, checkboxMode]
213261
);
214262

215263
const table = useReactTable({
@@ -274,6 +322,8 @@ export const ManageAccounts = () => {
274322
onClick={() => {
275323
setView("admin");
276324
setEditDrawerOpened(false);
325+
setSelectedRowIds([]);
326+
setCheckboxMode('hidden');
277327
}}
278328
>
279329
Admins
@@ -282,6 +332,8 @@ export const ManageAccounts = () => {
282332
onClick={() => {
283333
setView("cms");
284334
setEditDrawerOpened(false);
335+
setSelectedRowIds([]);
336+
setCheckboxMode('hidden');
285337
}}
286338
>
287339
Case Managers
@@ -290,6 +342,8 @@ export const ManageAccounts = () => {
290342
onClick={() => {
291343
setView("clients");
292344
setEditDrawerOpened(false);
345+
setSelectedRowIds([]);
346+
setCheckboxMode('hidden');
293347
}}
294348
>
295349
Clients
@@ -300,7 +354,14 @@ export const ManageAccounts = () => {
300354
<Spacer />
301355
{view !== "clients" && (
302356
<>
303-
<Button onClick={deleteOnOpen}>Delete</Button>
357+
<Button
358+
onClick={deleteOnOpen}
359+
disabled={selectedRowIds.length === 0}
360+
colorScheme={selectedRowIds.length === 0 ? "gray" : "red"}
361+
opacity={selectedRowIds.length === 0 ? 0.5 : 1}
362+
>
363+
Delete ({selectedRowIds.length})
364+
</Button>
304365
<Button
305366
colorScheme="blue"
306367
onClick={onOpen}
@@ -402,14 +463,15 @@ export const ManageAccounts = () => {
402463
}}
403464
>
404465
{cell.column.id === "rowNumber" ? (
405-
<HoverCheckbox
406-
id={row.original.id}
407-
isSelected={selectedRowIds.includes(
408-
row.original.id
409-
)}
410-
onSelectionChange={handleRowSelect}
411-
index={index}
412-
/>
466+
<HoverCheckbox
467+
id={row.original.id}
468+
isSelected={selectedRowIds.includes(
469+
row.original.id
470+
)}
471+
onSelectionChange={handleRowSelect}
472+
index={index}
473+
alwaysVisible={checkboxMode !== 'hidden'}
474+
/>
413475
) : (
414476
flexRender(
415477
cell.column.columnDef.cell,

client/src/components/hoverCheckbox/hoverCheckbox.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,25 +6,25 @@ interface HoverCheckboxProps {
66
index: number;
77
isSelected: boolean;
88
onSelectionChange: (id: number, checked: boolean) => void;
9+
alwaysVisible?: boolean;
910
}
1011

1112
export const HoverCheckbox = ({
1213
id,
1314
index,
1415
isSelected,
1516
onSelectionChange,
17+
alwaysVisible = false,
1618
}: HoverCheckboxProps) => {
17-
const [isHovered, setIsHovered] = useState(false);
19+
const shouldShowCheckbox = isSelected || alwaysVisible;
1820

1921
return (
2022
<Flex
2123
textAlign={"center"}
2224
justifyContent={"center"}
23-
onMouseEnter={() => setIsHovered(true)}
24-
onMouseLeave={() => setIsHovered(false)}
2525
onClick={(e) => e.stopPropagation()}
2626
>
27-
{(isHovered || isSelected) ? (
27+
{shouldShowCheckbox ? (
2828
<Checkbox
2929
isChecked={isSelected}
3030
onChange={(e) => onSelectionChange(id, e.target.checked)}

server/db/schema/random_survey_table.sql

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,5 +19,5 @@ CREATE TABLE random_survey_table (
1919
cm_feedback varchar(2048) NOT NULL,
2020
other_comments varchar(2048),
2121

22-
FOREIGN KEY(cm_id) REFERENCES case_manager(id)
22+
FOREIGN KEY(cm_id) REFERENCES case_managers(id)
2323
);

server/db/schema/screener_comment.sql

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,8 @@ CREATE TABLE screener_comment (
2121
children_in_custody int,
2222
last_city_perm_residence varchar(32),
2323
decision boolean,
24-
additional_comments varchar(1024)
25-
FOREIGN KEY(cm_id) REFERENCES case_manager(id)
24+
additional_comments varchar(1024),
25+
FOREIGN KEY(cm_id) REFERENCES case_managers(id),
2626
FOREIGN KEY(initial_interview_id) REFERENCES initial_interview(id)
2727
);
2828

server/routes/caseManagers.ts

Lines changed: 47 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -58,10 +58,10 @@ caseManagersRouter.get("/id-by-email/:email", async (req, res) => {
5858
// Insert new case manager
5959
caseManagersRouter.post("/", async (req, res) => {
6060
try {
61-
const { role, firstName, lastName, phoneNumber, email } = req.body;
61+
const { role, firstName, lastName, phoneNumber, email, notes } = req.body;
6262
const data = await db.query(
63-
`INSERT INTO case_managers (role, first_name, last_name, phone_number, email) VALUES ($1, $2, $3, $4, $5) RETURNING id;`,
64-
[role, firstName, lastName, phoneNumber, email]
63+
`INSERT INTO case_managers (role, first_name, last_name, phone_number, email, notes) VALUES ($1, $2, $3, $4, $5, $6) RETURNING id;`,
64+
[role, firstName, lastName, phoneNumber, email, notes]
6565
);
6666

6767
res.status(200).json(keysToCamel(data));
@@ -93,15 +93,53 @@ caseManagersRouter.put("/:id", async (req, res) => {
9393
}
9494
});
9595

96-
// Delete case manager by id
96+
// Delete case manager by id with cascade
9797
caseManagersRouter.delete("/:id", async (req, res) => {
9898
try {
9999
const { id } = req.params;
100-
const data = await db.query(`DELETE FROM case_managers WHERE id = $1`, [
101-
id,
102-
]);
103-
104-
res.status(200).json(keysToCamel(data));
100+
101+
// Start a transaction to ensure all deletes succeed or fail together
102+
await db.query('BEGIN');
103+
104+
try {
105+
// Delete related records in order (respecting foreign key constraints)
106+
// Delete screener comments first
107+
await db.query(`DELETE FROM screener_comment WHERE cm_id = $1`, [id]);
108+
109+
// Delete random survey records
110+
await db.query(`DELETE FROM random_survey_table WHERE cm_id = $1`, [id]);
111+
112+
// Delete intake statistics forms
113+
await db.query(`DELETE FROM intake_statistics_form WHERE cm_id = $1`, [id]);
114+
115+
// Delete success stories
116+
await db.query(`DELETE FROM success_story WHERE cm_id = $1`, [id]);
117+
118+
// Delete exit surveys
119+
await db.query(`DELETE FROM exit_survey WHERE cm_id = $1`, [id]);
120+
121+
// Delete case manager monthly stats
122+
await db.query(`DELETE FROM cm_monthly_stats WHERE cm_id = $1`, [id]);
123+
124+
// Delete locations
125+
await db.query(`DELETE FROM locations WHERE cm_id = $1`, [id]);
126+
127+
// Update clients to set created_by to NULL (or handle as needed)
128+
// Note: This might need different handling based on business logic
129+
await db.query(`UPDATE clients SET created_by = NULL WHERE created_by = $1`, [id]);
130+
131+
// Finally delete the case manager
132+
const data = await db.query(`DELETE FROM case_managers WHERE id = $1 RETURNING *`, [id]);
133+
134+
// Commit the transaction
135+
await db.query('COMMIT');
136+
137+
res.status(200).json(keysToCamel(data));
138+
} catch (error) {
139+
// Rollback the transaction if any step fails
140+
await db.query('ROLLBACK');
141+
throw error;
142+
}
105143
} catch (err) {
106144
res.status(500).send(err.message);
107145
}

0 commit comments

Comments
 (0)