diff --git a/app/calendar/api.ts b/app/calendar/api.ts index bbeff9221..e80374d21 100644 --- a/app/calendar/api.ts +++ b/app/calendar/api.ts @@ -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; diff --git a/app/calendar/components/CalendarPage.tsx b/app/calendar/components/CalendarPage.tsx index 054c39ba3..785604b34 100644 --- a/app/calendar/components/CalendarPage.tsx +++ b/app/calendar/components/CalendarPage.tsx @@ -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 ( <>
- - {modalIsOpen && ( + {eventToEdit && ( setModalIsOpen(false)} - event={false} + onClose={() => setEventToEdit(false)} + event={typeof eventToEdit === "boolean" ? undefined : eventToEdit} refetchQuery={events.refetch} /> )} @@ -67,7 +69,22 @@ export default function CalendarPage() { )}
- + + 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, + }) + } + />
); diff --git a/app/calendar/components/EventInfo.tsx b/app/calendar/components/EventInfo.tsx index 626b8820f..a132e0367 100644 --- a/app/calendar/components/EventInfo.tsx +++ b/app/calendar/components/EventInfo.tsx @@ -26,12 +26,12 @@ import { EVENT_FORMATS, TAGS } from "../utils"; interface EventInfoProps { event: Unpacked; + 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) => { diff --git a/app/calendar/components/TagsSelector.tsx b/app/calendar/components/TagsSelector.tsx index 49457948d..03d7c2b9a 100644 --- a/app/calendar/components/TagsSelector.tsx +++ b/app/calendar/components/TagsSelector.tsx @@ -34,7 +34,7 @@ const TagsSelector: React.FC = (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 = (props) => { }} /> ); + + function getLabel(value: string) { + const tag = TAGS.find(({ code }) => code === value); + return tag?.name; + } }; export default TagsSelector; diff --git a/app/calendar/service.ts b/app/calendar/service.ts index 1d5e75b16..77776cc16 100644 --- a/app/calendar/service.ts +++ b/app/calendar/service.ts @@ -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; 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; + 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, }; diff --git a/components/u/ProfileModal.tsx b/components/u/ProfileModal.tsx index b3268179f..2c8573b86 100644 --- a/components/u/ProfileModal.tsx +++ b/components/u/ProfileModal.tsx @@ -258,7 +258,7 @@ const ProfileModal: React.FC = ({ onClose, user }) => { name="weaponPool" control={control} defaultValue={[]} - render={({ onChange, value, name }) => ( + render={({ onChange, value }) => (