Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[DEMO] Delete assignment/location #31

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions src/components/admin/AdminCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,14 @@ import {
Input,
EditableInput,
} from '@chakra-ui/react';
import { CheckIcon, CloseIcon, EditIcon } from '@chakra-ui/icons';
import { CheckIcon, CloseIcon, DeleteIcon, EditIcon } from '@chakra-ui/icons';
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The trash icon is imported from Chakra (the component library we use)

import { Assignment, Location } from '@prisma/client';
import { UseTRPCMutationResult } from '@trpc/react/shared';

interface AdminCardProps {
assignmentOrLocation: Assignment | Location;
editMutation: UseTRPCMutationResult<any, any, any, any>;
handleDelete: (id: number) => Promise<void>;
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Every component has props that are typed. Try to avoid using any but sometimes we might need to.

}

const EditableControls = () => {
Expand All @@ -40,7 +41,7 @@ const EditableControls = () => {
* Component which represents a single assignment or location
*/
const AdminCard = (props: AdminCardProps) => {
const { assignmentOrLocation, editMutation } = props;
const { assignmentOrLocation, editMutation, handleDelete } = props;
const boxColor = useColorModeValue('gray.100', 'gray.700');
const [isChecked, setIsChecked] = useState(assignmentOrLocation.active);

Expand Down Expand Up @@ -73,6 +74,7 @@ const AdminCard = (props: AdminCardProps) => {
Active?
</Text>
<Switch onChange={handleActiveChange} mt={2.5} ml={3} isChecked={isChecked} />
<DeleteIcon ml={5} mt={2} onClick={() => handleDelete(assignmentOrLocation.id)} />
Copy link
Collaborator Author

@MeshanKhosla MeshanKhosla Nov 4, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Render out the DeleteIcon and call the handleDelete method (passed in as a prop) with the ID.

</Flex>
</>
);
Expand Down
17 changes: 15 additions & 2 deletions src/components/admin/AdminView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ const AdminView = () => {
const createLocationMutation = trpc.admin.createLocation.useMutation();
const editLocationMutation = trpc.admin.editLocation.useMutation();
const setSiteSettingsMutation = trpc.admin.setSiteSettings.useMutation();
const deleteAssignmentMutation = trpc.admin.deleteAssignment.useMutation();
const deleteLocationMutation = trpc.admin.deleteLocation.useMutation();
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Initialize the mutations that we created in admin.ts


useEffect(() => {
if (siteSettings) {
Expand Down Expand Up @@ -56,6 +58,17 @@ const AdminView = () => {
setLocations(prev => [...prev!, data]);
};

const handleDeleteAssignment = async (id: number) => {
await deleteAssignmentMutation.mutateAsync({ id });
// We can also just invalidate the query, but this makes the UI feel more responsive
setAssignments(prev => prev!.filter(assignment => assignment.id !== id));
}

const handleDeleteLocation = async (id: number) => {
await deleteLocationMutation.mutateAsync({ id });
setLocations(prev => prev!.filter(location => location.id !== id));
}

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These two are essentially identical, but one is for assignments and one is for locations. The key here is that we call the mutateAsync method on the mutation which will actually send the request to our backend.

// Sets the pending stage to enabled or disabled depending on the current state
const handleTogglePendingStageEnabled = async () => {
setIsPendingStageEnabled(prev => !prev);
Expand Down Expand Up @@ -91,7 +104,7 @@ const AdminView = () => {
</Flex>
</Flex>
{assignments.map(assignment => (
<AdminCard key={assignment.id} assignmentOrLocation={assignment} editMutation={editAssignmentMutation} />
<AdminCard key={assignment.id} assignmentOrLocation={assignment} editMutation={editAssignmentMutation} handleDelete={handleDeleteAssignment} />
))}

<Flex direction='column' w='50%' mt={10} mb={3}>
Expand All @@ -106,7 +119,7 @@ const AdminView = () => {
</Flex>
</Flex>
{locations.map(location => (
<AdminCard key={location.id} assignmentOrLocation={location} editMutation={editLocationMutation} />
<AdminCard key={location.id} assignmentOrLocation={location} editMutation={editLocationMutation} handleDelete={handleDeleteLocation} />
))}
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pass in the approprite handler as a prop. The top is for assignments and the bottom is for locations.


<Flex direction='column' mt={10} mb={3}>
Expand Down
28 changes: 28 additions & 0 deletions src/server/trpc/router/admin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,34 @@ export const adminRouter = router({
});
}),

Copy link
Collaborator Author

@MeshanKhosla MeshanKhosla Nov 4, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is our backend. It's split into separate files (routers) such as ticket.ts, admin.ts, user.ts and we can define our "endpoints" here. We can either create a query (GET) or a mutation (POST). Since deleting is modifying our DB, both of our operations will be mutations. The queries are further down in this file. (like getAllAssignments).

Ignore the weird formatting:

deleteLocation: protectedStaffProcedure
Copy link
Collaborator Author

@MeshanKhosla MeshanKhosla Nov 4, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the client we initialized this with "trpc.admin.deleteLocation.useMutation();" For queries we use useQuery. protectedStaffProcedure means that only staff members can invoke this. In other places, we have protectedProcedure meaning only signed in users can invoke it.

.input(
z.object({
id: z.number(),
}),
)
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The input to this mutation is the ID that we want to delete. z (zod) is an input validation library so our server<->client communication is fully typesafe. Meaning it won't let us accidentally pass a string into this method.

.mutation(async ({ input, ctx }) => {
return ctx.prisma.location.delete({
where: {
id: input.id,
},
});
Copy link
Collaborator Author

@MeshanKhosla MeshanKhosla Nov 4, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is equivalent to writing raw SQL to delete from the database but no one wants to do that so we use the prisma ORM which allows us to just write typescript to interact with the database.

}),

deleteAssignment: protectedStaffProcedure
.input(
z.object({
id: z.number(),
}),
)
.mutation(async ({ input, ctx }) => {
return ctx.prisma.assignment.delete({
where: {
id: input.id,
},
});
}),

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same thing for deleteAssignment

setSiteSettings: protectedStaffProcedure
.input(
z.object({
Expand Down