can edit and delete events

This commit is contained in:
Kalle (Sendou)
2021-04-02 15:25:40 +03:00
parent 714f970569
commit 190c5a19fb
7 changed files with 166 additions and 37 deletions

View File

@@ -1,6 +1,7 @@
import { createRouter } from "pages/api/trpc/[trpc]";
import { throwIfNotLoggedIn } from "utils/api";
import { eventSchema } from "utils/validators/event";
import * as z from "zod";
import service from "./service";
const plusApi = createRouter()
@@ -15,5 +16,26 @@ const plusApi = createRouter()
const user = throwIfNotLoggedIn(ctx.user);
return service.addEvent({ input, userId: user.id });
},
})
.mutation("editEvent", {
input: z.object({ event: eventSchema, eventId: z.number() }),
resolve({ ctx, input }) {
const user = throwIfNotLoggedIn(ctx.user);
console.log("input", input);
return service.editEvent({
...input,
userId: user.id,
});
},
})
.mutation("deleteEvent", {
input: z.object({ eventId: z.number() }),
resolve({ ctx, input }) {
const user = throwIfNotLoggedIn(ctx.user);
return service.deleteEvent({
eventId: input.eventId,
userId: user.id,
});
},
});
export default plusApi;

View File

@@ -7,12 +7,14 @@ import { useMyTheme } from "hooks/common";
import { Fragment, useState } from "react";
import { trpc } from "utils/trpc";
import EventInfo from "./EventInfo";
import { EventModal } from "./EventModal";
import { EventModal, FormData } from "./EventModal";
export default function CalendarPage() {
const { gray } = useMyTheme();
const events = trpc.useQuery(["calendar.events"], { enabled: false });
const [modalIsOpen, setModalIsOpen] = useState(false);
const [eventToEdit, setEventToEdit] = useState<
boolean | (FormData & { id: number })
>(false);
const [filter, setFilter] = useState("");
let lastPrintedDate: [number, number, Date] | null = null;
@@ -22,13 +24,13 @@ export default function CalendarPage() {
return (
<>
<div>
<Button size="sm" onClick={() => setModalIsOpen(true)}>
<Button size="sm" onClick={() => setEventToEdit(true)}>
<Trans>Add event</Trans>
</Button>
{modalIsOpen && (
{eventToEdit && (
<EventModal
onClose={() => setModalIsOpen(false)}
event={false}
onClose={() => setEventToEdit(false)}
event={typeof eventToEdit === "boolean" ? undefined : eventToEdit}
refetchQuery={events.refetch}
/>
)}
@@ -67,7 +69,22 @@ export default function CalendarPage() {
</Box>
)}
<div>
<EventInfo event={event} />
<EventInfo
event={event}
edit={() =>
setEventToEdit({
...event,
date: event.date.toISOString(),
// TODO: remove this if later other event types than tournament are allowed
// currently in the validator we accept the properties as if you can only submit
// tournaments but database is prepared to accept other kind of events
// this makes TS freak out a bit
discordInviteUrl: event.discordInviteUrl!,
tags: event.tags as any,
format: event.format as any,
})
}
/>
</div>
</Fragment>
);

View File

@@ -26,12 +26,12 @@ import { EVENT_FORMATS, TAGS } from "../utils";
interface EventInfoProps {
event: Unpacked<Events>;
edit: () => void;
}
const TournamentInfo = ({ event }: EventInfoProps) => {
const TournamentInfo = ({ event, edit }: EventInfoProps) => {
const { secondaryBgColor, gray, themeColorShade } = useMyTheme();
const [expanded, setExpanded] = useState(false);
const [showModal, setShowModal] = useState(false);
const poster = event.poster;
const [user] = useUser();
@@ -164,7 +164,7 @@ const TournamentInfo = ({ event }: EventInfoProps) => {
<Button
leftIcon={<FiEdit />}
size="sm"
onClick={() => setShowModal(true)}
onClick={edit}
variant="outline"
>
Edit event

View File

@@ -30,7 +30,7 @@ import * as z from "zod";
import { EVENT_FORMATS } from "../utils";
import TagsSelector from "./TagsSelector";
type FormData = z.infer<typeof eventSchema>;
export type FormData = z.infer<typeof eventSchema>;
export function EventModal({
onClose,
@@ -38,14 +38,16 @@ export function EventModal({
refetchQuery,
}: {
onClose: () => void;
event: boolean;
event?: { id: number } & FormData;
refetchQuery: () => void;
}) {
const toast = useToast();
const { i18n } = useLingui();
const { handleSubmit, errors, register, watch, control } = useForm<FormData>({
resolver: zodResolver(eventSchema),
defaultValues: event,
});
const addEvent = trpc.useMutation("calendar.addEvent", {
onSuccess() {
toast(getToastOptions(t`Event added`, "success"));
@@ -57,19 +59,39 @@ export function EventModal({
toast(getToastOptions(error.message, "error"));
},
});
const editEvent = trpc.useMutation("calendar.editEvent", {
onSuccess() {
toast(getToastOptions(t`Event updated`, "success"));
refetchQuery();
onClose();
},
onError(error) {
console.log({ error });
toast(getToastOptions(error.message, "error"));
},
});
const deleteEvent = trpc.useMutation("calendar.deleteEvent", {
onSuccess() {
toast(getToastOptions(t`Event deleted`, "success"));
refetchQuery();
onClose();
},
onError(error) {
console.log({ error });
toast(getToastOptions(error.message, "error"));
},
});
const watchDescription = watch("description", /*team.bio*/ "");
const watchDescription = watch("description", event?.description ?? "");
const onSubmit = async (values: FormData) => {
console.log("jaa");
addEvent.mutate(values);
console.log({ values });
event
? editEvent.mutate({ event: values, eventId: event.id })
: addEvent.mutate(values);
};
const onDelete = async () => {
console.log("delete");
};
console.log({ errors });
const onDelete = (eventId: number) => deleteEvent.mutate({ eventId });
return (
<Modal isOpen onClose={onClose} size="xl" closeOnOverlayClick={false}>
@@ -91,9 +113,10 @@ export function EventModal({
variant="outline"
color="red.500"
mb={6}
//isLoading={deleting}
isLoading={deleteEvent.status === "loading"}
onClick={async () => {
if (window.confirm(t`Delete the event?`)) await onDelete();
if (window.confirm(t`Delete the event?`))
onDelete(event.id);
}}
>
<Trans>Delete event</Trans>
@@ -128,19 +151,18 @@ export function EventModal({
</FormLabel>
<FormControl isInvalid={!!errors.date}>
{/* <Controller
<Controller
name="date"
control={control}
defaultValue={new Date()}
render={({ onChange, value }) => {
return <DatePicker date={value} onChange={onChange} />;
}}
/> */}
<Input
type="datetime-local"
name="date"
ref={register}
min={new Date().toISOString()}
defaultValue={new Date().toISOString()}
render={({ onChange, value }) => (
<Input
type="datetime-local"
value={value.substring(0, 16)}
onChange={onChange}
min={new Date().toISOString()}
/>
)}
/>
<FormHelperText>
<Trans>Input the time in your local time zone:</Trans>{" "}
@@ -219,7 +241,10 @@ export function EventModal({
<Button
mr={3}
type="submit"
isLoading={addEvent.status === "loading"}
isLoading={
addEvent.status === "loading" ||
editEvent.status === "loading"
}
>
<Trans>Save</Trans>
</Button>

View File

@@ -34,7 +34,7 @@ const TagsSelector: React.FC<TagsSelectorProps> = (props) => {
label: i18n._(tag.name),
value: tag.code,
}))}
//value={getValue()}
value={props.value?.map((value) => ({ value, label: getLabel(value) }))}
setValue={props.setValue}
isClearable
isMulti
@@ -45,6 +45,11 @@ const TagsSelector: React.FC<TagsSelectorProps> = (props) => {
}}
/>
);
function getLabel(value: string) {
const tag = TAGS.find(({ code }) => code === value);
return tag?.name;
}
};
export default TagsSelector;

View File

@@ -1,5 +1,8 @@
import { Prisma } from "@prisma/client";
import { httpError } from "@trpc/server";
import prisma from "prisma/client";
import { ADMIN_ID } from "utils/constants";
import { userBasicSelection } from "utils/prisma";
import { eventSchema } from "utils/validators/event";
import * as z from "zod";
@@ -7,8 +10,20 @@ export type Events = Prisma.PromiseReturnType<typeof events>;
const events = () => {
return prisma.calendarEvent.findMany({
select: {
date: true,
description: true,
discordInviteUrl: true,
eventUrl: true,
format: true,
id: true,
name: true,
tags: true,
poster: {
select: userBasicSelection,
},
},
where: { date: { gt: new Date() } },
include: { poster: true },
orderBy: { date: "asc" },
});
};
@@ -30,7 +45,52 @@ const addEvent = async ({
});
};
const editEvent = async ({
event,
userId,
eventId,
}: {
event: z.infer<typeof eventSchema>;
eventId: number;
userId: number;
}) => {
const existingEvent = await prisma.calendarEvent.findFirst({
where: { AND: [{ id: eventId }, { posterId: userId }] },
});
if (!existingEvent || userId === ADMIN_ID) {
throw httpError.badRequest("no event to edit");
}
return prisma.calendarEvent.update({
where: { id: eventId },
data: {
...event,
isTournament: true,
date: new Date(event.date),
},
});
};
const deleteEvent = async ({
userId,
eventId,
}: {
eventId: number;
userId: number;
}) => {
const existingEvent = await prisma.calendarEvent.findFirst({
where: { AND: [{ id: eventId }, { posterId: userId }] },
});
if (!existingEvent || userId === ADMIN_ID) {
throw httpError.badRequest("no event to delete");
}
return prisma.calendarEvent.delete({ where: { id: eventId } });
};
export default {
events,
addEvent,
editEvent,
deleteEvent,
};

View File

@@ -258,7 +258,7 @@ const ProfileModal: React.FC<Props> = ({ onClose, user }) => {
name="weaponPool"
control={control}
defaultValue={[]}
render={({ onChange, value, name }) => (
render={({ onChange, value }) => (
<WeaponSelector
value={value}
setValue={onChange}