Skip to content

Commit d02bfa8

Browse files
adding assignment and location categories (#141)
1 parent d899ea6 commit d02bfa8

File tree

7 files changed

+543
-123
lines changed

7 files changed

+543
-123
lines changed

prisma/schema.prisma

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -130,18 +130,33 @@ model Assignment {
130130
isHidden Boolean @default(false)
131131
isPriority Boolean @default(false)
132132
Ticket Ticket[]
133+
category Category @relation(fields: [categoryId], references: [id])
134+
categoryId Int @default(1)
135+
136+
@@index(id)
137+
@@index(categoryId)
138+
}
139+
140+
model Category {
141+
id Int @id @default(autoincrement())
142+
name String
143+
createdAt DateTime @default(now())
144+
updatedAt DateTime @default(now()) @updatedAt
145+
assignments Assignment[]
146+
locations Location[]
133147
134148
@@index(id)
135149
}
136150

137151
model Location {
138-
id Int @id @default(autoincrement())
139-
name String
140-
createdAt DateTime @default(now())
141-
updatedAt DateTime @default(now()) @updatedAt
142-
isActive Boolean @default(true)
143-
isHidden Boolean @default(false)
144-
Ticket Ticket[]
152+
id Int @id @default(autoincrement())
153+
name String
154+
createdAt DateTime @default(now())
155+
updatedAt DateTime @default(now()) @updatedAt
156+
isActive Boolean @default(true)
157+
isHidden Boolean @default(false)
158+
Ticket Ticket[]
159+
categories Category[]
145160
146161
@@index(id)
147162
}

src/components/admin/AdminCard.tsx

Lines changed: 207 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import {
66
EditableInput,
77
EditablePreview,
88
Flex,
9+
FormControl,
910
IconButton,
1011
Input,
1112
Switch,
@@ -14,8 +15,10 @@ import {
1415
useEditableControls,
1516
useToast,
1617
} from "@chakra-ui/react";
17-
import { Assignment, Location } from "@prisma/client";
18+
19+
import { Assignment, Category, Location } from "@prisma/client";
1820
import { UseTRPCMutationResult } from "@trpc/react/shared";
21+
import { MultiValue, Select, SingleValue } from "chakra-react-select";
1922
import { useState } from "react";
2023
import { FaEye, FaEyeSlash } from "react-icons/fa";
2124
import { DARK_GRAY_COLOR } from "../../utils/constants";
@@ -73,15 +76,47 @@ const AdminCard = (props: AdminCardProps) => {
7376
const [isPriority, setIsPriority] = useState(
7477
isAssignment ? (assignmentOrLocation as Assignment).isPriority : false,
7578
);
79+
const [assignmentCategoryId, setAssignmentCategoryId] = useState(
80+
isAssignment ? (assignmentOrLocation as Assignment).categoryId : undefined,
81+
);
82+
const [locationCategoryIds, setLocationCategoryIds] = useState<number[]>();
83+
const [allCategories, setAllCategories] = useState<Category[]>();
84+
7685
const context = trpc.useContext();
7786
const toast = useToast();
7887

88+
trpc.admin.getCategoriesForLocation.useQuery(
89+
{
90+
locationId: isAssignment
91+
? undefined
92+
: (assignmentOrLocation as Location).id,
93+
},
94+
{
95+
refetchOnWindowFocus: false,
96+
onSuccess: (data) => {
97+
setLocationCategoryIds(data?.categories.map((category) => category.id));
98+
},
99+
enabled: !isAssignment,
100+
},
101+
);
102+
103+
trpc.admin.getAllCategories.useQuery(undefined, {
104+
refetchOnWindowFocus: false,
105+
onSuccess: (data) => {
106+
setAllCategories(data);
107+
},
108+
});
109+
79110
const handleNameChange = async (newName: string) => {
80111
await editMutation.mutateAsync({
81112
id: assignmentOrLocation.id,
82113
name: newName,
83114
isActive: assignmentOrLocation.isActive,
84115
isHidden: assignmentOrLocation.isHidden,
116+
categoryId: isAssignment
117+
? (assignmentOrLocation as Assignment).categoryId
118+
: undefined,
119+
categoryIds: isAssignment ? undefined : locationCategoryIds,
85120
});
86121
};
87122

@@ -93,6 +128,10 @@ const AdminCard = (props: AdminCardProps) => {
93128
isActive: assignmentOrLocation.isActive,
94129
isHidden: assignmentOrLocation.isHidden,
95130
isPriority: newPriority,
131+
categoryId: isAssignment
132+
? (assignmentOrLocation as Assignment).categoryId
133+
: undefined,
134+
categoryIds: isAssignment ? undefined : locationCategoryIds,
96135
});
97136
};
98137

@@ -105,6 +144,10 @@ const AdminCard = (props: AdminCardProps) => {
105144
name: assignmentOrLocation.name,
106145
isActive: newActive,
107146
isHidden: newActive ? false : isHidden,
147+
categoryId: isAssignment
148+
? (assignmentOrLocation as Assignment).categoryId
149+
: undefined,
150+
categoryIds: isAssignment ? undefined : locationCategoryIds,
108151
})
109152
.then(() => {
110153
context.admin.getAllLocations.invalidate();
@@ -130,6 +173,62 @@ const AdminCard = (props: AdminCardProps) => {
130173
name: assignmentOrLocation.name,
131174
isActive: assignmentOrLocation.isActive,
132175
isHidden: !isHidden,
176+
categoryId: isAssignment
177+
? (assignmentOrLocation as Assignment).categoryId
178+
: undefined,
179+
categoryIds: isAssignment ? undefined : locationCategoryIds,
180+
})
181+
.then(() => {
182+
context.admin.getAllLocations.invalidate();
183+
context.admin.getAllAssignments.invalidate();
184+
})
185+
.catch((err) => {
186+
toast({
187+
title: "Error",
188+
description: err.message,
189+
status: "error",
190+
duration: 3000,
191+
isClosable: true,
192+
position: "top-right",
193+
});
194+
});
195+
};
196+
197+
const handleAssignmentCategoryChange = async (categoryId: number) => {
198+
setAssignmentCategoryId(categoryId);
199+
await editMutation
200+
.mutateAsync({
201+
id: assignmentOrLocation.id,
202+
name: assignmentOrLocation.name,
203+
isActive: isActive,
204+
isHidden: isHidden,
205+
categoryId: categoryId,
206+
})
207+
.then(() => {
208+
context.admin.getAllLocations.invalidate();
209+
context.admin.getAllAssignments.invalidate();
210+
})
211+
.catch((err) => {
212+
toast({
213+
title: "Error",
214+
description: err.message,
215+
status: "error",
216+
duration: 3000,
217+
isClosable: true,
218+
position: "top-right",
219+
});
220+
});
221+
};
222+
223+
const handleLocationCategoriesChange = async (categoryIds: number[]) => {
224+
setLocationCategoryIds(categoryIds);
225+
await editMutation
226+
.mutateAsync({
227+
id: assignmentOrLocation.id,
228+
name: assignmentOrLocation.name,
229+
isActive: isActive,
230+
isHidden: isHidden,
231+
categoryIds: categoryIds,
133232
})
134233
.then(() => {
135234
context.admin.getAllLocations.invalidate();
@@ -160,59 +259,114 @@ const AdminCard = (props: AdminCardProps) => {
160259
backgroundColor={boxColor}
161260
justifyContent="space-between"
162261
>
163-
<Flex>
164-
<Editable
165-
onSubmit={handleNameChange}
166-
textAlign="center"
167-
fontWeight="semibold"
168-
display="flex"
169-
defaultValue={assignmentOrLocation.name}
170-
fontSize="xl"
171-
isPreviewFocusable={false}
172-
>
173-
<EditablePreview />
174-
<Input as={EditableInput} />
175-
<EditableControls />
176-
</Editable>
177-
<Text fontSize="large" mt={1.5} ml={5}>
178-
Active?
179-
</Text>
180-
<Switch
181-
onChange={handleActiveChange}
182-
mt={2.5}
183-
ml={3}
184-
isChecked={isActive}
185-
/>
186-
</Flex>
187-
<Checkbox
188-
hidden={!isActive || !isAssignment}
189-
onChange={() => handlePriorityChange(!isPriority)}
190-
colorScheme="telegram"
191-
size="lg"
192-
ml={2}
193-
isChecked={isPriority}
194-
>
195-
Priority
196-
</Checkbox>
197-
{!isActive && (
198-
<Flex>
199-
{isHidden ? (
200-
<FaEyeSlash
201-
size="20px"
202-
className="hover-cursor"
203-
style={{ marginTop: "10px" }}
204-
onClick={handleHidden}
205-
/>
206-
) : (
207-
<FaEye
208-
size="20px"
209-
className="hover-cursor"
210-
style={{ marginTop: "10px" }}
211-
onClick={handleHidden}
212-
/>
213-
)}
262+
<>
263+
<Flex w="50%">
264+
<Editable
265+
onSubmit={handleNameChange}
266+
textAlign="center"
267+
fontWeight="semibold"
268+
display="flex"
269+
defaultValue={assignmentOrLocation.name}
270+
fontSize="xl"
271+
isPreviewFocusable={false}
272+
>
273+
<EditablePreview />
274+
<Input as={EditableInput} />
275+
<EditableControls />
276+
</Editable>
277+
<Text fontSize="large" mt={1.5} ml={5}>
278+
Active?
279+
</Text>
280+
<Switch
281+
onChange={handleActiveChange}
282+
mt={2.5}
283+
ml={3}
284+
isChecked={isActive}
285+
/>
286+
{/*uppercaseFirstLetter(allCategories?.find((category) => category.id === assignmentCategoryId).name)*/}
287+
<FormControl w={isAssignment ? "30%" : "50%"} ml={2}>
288+
{isAssignment ? (
289+
<Select
290+
options={allCategories?.map((category) => ({
291+
label: category.name,
292+
value: category.id,
293+
}))}
294+
value={
295+
assignmentCategoryId !== undefined
296+
? {
297+
label: allCategories?.find(
298+
(category) => category.id === assignmentCategoryId,
299+
)?.name,
300+
value: assignmentCategoryId,
301+
}
302+
: { label: "", value: -1 }
303+
}
304+
onChange={(
305+
newValue: SingleValue<{
306+
label: string | undefined;
307+
value: number;
308+
}>,
309+
) => handleAssignmentCategoryChange(newValue?.value ?? -1)}
310+
/>
311+
) : (
312+
<Select
313+
isMulti
314+
options={allCategories?.map((category) => ({
315+
label: category.name,
316+
value: category.id,
317+
}))}
318+
value={allCategories
319+
?.filter((category) =>
320+
locationCategoryIds?.includes(category.id),
321+
)
322+
?.map((category) => ({
323+
value: category.id,
324+
label: category.name,
325+
}))}
326+
onChange={(
327+
newValue: MultiValue<{
328+
label: string;
329+
value: number;
330+
}>,
331+
) =>
332+
handleLocationCategoriesChange(
333+
newValue.map((item) => item.value),
334+
)
335+
}
336+
/>
337+
)}
338+
</FormControl>
214339
</Flex>
215-
)}
340+
<Checkbox
341+
hidden={!isActive || !isAssignment}
342+
onChange={() => handlePriorityChange(!isPriority)}
343+
colorScheme="telegram"
344+
size="lg"
345+
ml={2}
346+
isChecked={isPriority}
347+
>
348+
Priority
349+
</Checkbox>
350+
{!isActive && (
351+
<Flex>
352+
{isHidden ? (
353+
<FaEyeSlash
354+
size="20px"
355+
className="hover-cursor"
356+
style={{ marginTop: "10px" }}
357+
onClick={handleHidden}
358+
/>
359+
) : (
360+
<FaEye
361+
size="20px"
362+
className="hover-cursor"
363+
style={{ marginTop: "10px" }}
364+
onClick={handleHidden}
365+
/>
366+
)}
367+
</Flex>
368+
)}
369+
</>
216370
</Flex>
217371
);
218372
};

0 commit comments

Comments
 (0)