diff --git a/projects/admin/src/app.tsx b/projects/admin/src/app.tsx
index fe9e0f9..d2552f4 100644
--- a/projects/admin/src/app.tsx
+++ b/projects/admin/src/app.tsx
@@ -1,8 +1,9 @@
-import { Admin, ListGuesser, Resource } from "react-admin";
+import { Admin, Resource } from "react-admin";
import { dataProvider } from "./data-provider/data-provider";
-import { UserEdit, UserList } from "./users";
+import { UserEdit, UserList } from "./resources/users";
import { LoginPage } from "./login";
import { authProvider } from "./auth-provider";
+import { EventCreate, EventEdit, EventList } from "./resources/events";
export const App = () => {
return (
@@ -17,7 +18,12 @@ export const App = () => {
edit={UserEdit}
recordRepresentation="email"
/>
-
+
);
};
diff --git a/projects/admin/src/auth-provider.ts b/projects/admin/src/auth-provider.ts
index 8190abb..0561659 100644
--- a/projects/admin/src/auth-provider.ts
+++ b/projects/admin/src/auth-provider.ts
@@ -57,6 +57,10 @@ export const authProvider: AuthProvider = {
logout: async () => {
try {
+ const signedIn = await trpc.validateSession.query();
+ if (!signedIn) {
+ return;
+ }
await trpc.authentication.signOut.mutate();
} catch (error) {
console.error(error);
diff --git a/projects/admin/src/data-provider/create.ts b/projects/admin/src/data-provider/create.ts
index 8b3e79e..9f8fe4f 100644
--- a/projects/admin/src/data-provider/create.ts
+++ b/projects/admin/src/data-provider/create.ts
@@ -5,8 +5,9 @@ import { z } from "zod";
const createEventSchema = z.object({
name: z.string(),
- startsAt: z.string().datetime(),
- endsAt: z.string().datetime(),
+ startsAt: z.date().transform((date) => date.toISOString()),
+ endsAt: z.date().transform((date) => date.toISOString()),
+ marathonTypes: z.array(z.enum(["ONLINE", "ONSITE"])),
});
export const create = async (
diff --git a/projects/admin/src/data-provider/get-list.ts b/projects/admin/src/data-provider/get-list.ts
index 9358024..f750374 100644
--- a/projects/admin/src/data-provider/get-list.ts
+++ b/projects/admin/src/data-provider/get-list.ts
@@ -6,7 +6,7 @@ export const getList = async (
resource: string,
params: GetListParams,
): Promise => {
- const query = {
+ const input = {
order: lowercase(params.sort.order),
orderBy: params.sort.field,
skip: (params.pagination.page - 1) * params.pagination.perPage,
@@ -14,9 +14,21 @@ export const getList = async (
};
switch (resource) {
case "users": {
- const res = await trpc.admin.users.list.query(query);
+ const res = await trpc.admin.users.list.query(input);
return { data: res.data, total: res.count };
}
+ case "events": {
+ const res = await trpc.admin.events.list.query(input);
+ return {
+ data: res.data.map((item) => ({
+ ...item,
+ marathonTypes: item.eventMarathonTypes.map((type) => ({
+ name: type.marathonType,
+ })),
+ })),
+ total: res.count,
+ };
+ }
default:
throw new Error(`unknown resource ${resource}`);
}
diff --git a/projects/admin/src/data-provider/get-one.ts b/projects/admin/src/data-provider/get-one.ts
index 150070c..3d3adb8 100644
--- a/projects/admin/src/data-provider/get-one.ts
+++ b/projects/admin/src/data-provider/get-one.ts
@@ -16,6 +16,17 @@ export const getOne = async (
const data = await trpc.admin.users.get.query({ id });
return { data };
}
+ case "events": {
+ const data = await trpc.admin.events.get.query({ id });
+ return {
+ data: {
+ ...data,
+ marathonTypes: data.eventMarathonTypes.map(
+ (type) => type.marathonType,
+ ),
+ },
+ };
+ }
default:
throw new Error(`unknown resource ${resource}`);
}
diff --git a/projects/admin/src/resources/events.tsx b/projects/admin/src/resources/events.tsx
new file mode 100644
index 0000000..7420956
--- /dev/null
+++ b/projects/admin/src/resources/events.tsx
@@ -0,0 +1,71 @@
+import {
+ Create,
+ DateTimeInput,
+ SimpleForm,
+ TextInput,
+ required,
+ BooleanField,
+ Datagrid,
+ DateField,
+ List,
+ TextField,
+ CheckboxGroupInput,
+ ArrayField,
+ SingleFieldList,
+ ChipField,
+ Edit,
+ BooleanInput,
+} from "react-admin";
+
+export const EventCreate = () => {
+ return (
+
+
+
+
+
+
+
+
+ );
+};
+
+export const EventList = () => (
+
+
+
+
+
+
+
+
+
+
+
+
+
+);
+
+export const EventEdit = () => (
+
+
+
+
+
+
+
+
+
+);
diff --git a/projects/admin/src/users.tsx b/projects/admin/src/resources/users.tsx
similarity index 100%
rename from projects/admin/src/users.tsx
rename to projects/admin/src/resources/users.tsx
diff --git a/projects/server/prisma/migrations/20240110142600_/migration.sql b/projects/server/prisma/migrations/20240110142600_/migration.sql
new file mode 100644
index 0000000..ea949cd
--- /dev/null
+++ b/projects/server/prisma/migrations/20240110142600_/migration.sql
@@ -0,0 +1,2 @@
+-- AlterTable
+ALTER TABLE "User" ALTER COLUMN "username" DROP NOT NULL;
diff --git a/projects/server/prisma/seed.ts b/projects/server/prisma/seed.ts
index a2b09da..ee4fbc5 100644
--- a/projects/server/prisma/seed.ts
+++ b/projects/server/prisma/seed.ts
@@ -1,4 +1,4 @@
-import { PrismaClient, Role } from "@prisma/client";
+import { MarathonType, PrismaClient, Role } from "@prisma/client";
const prisma = new PrismaClient();
@@ -20,3 +20,19 @@ await prisma.user.create({
username: "dev-normal",
},
});
+
+await prisma.event.create({
+ data: {
+ name: "RTA in Japan Sample 1995",
+ startsAt: new Date("1995-08-08T12:00:00+0900"),
+ endsAt: new Date("1995-08-15T18:00:00+0900"),
+ eventMarathonTypes: {
+ createMany: {
+ data: [
+ { marathonType: MarathonType.ONLINE },
+ { marathonType: MarathonType.ONSITE },
+ ],
+ },
+ },
+ },
+});
diff --git a/projects/server/src/routes/admin/events.ts b/projects/server/src/routes/admin/events.ts
index 814aab3..27a9e2f 100644
--- a/projects/server/src/routes/admin/events.ts
+++ b/projects/server/src/routes/admin/events.ts
@@ -4,6 +4,13 @@ import { adminProcedure, router } from "../../trpc.js";
import { listSchema } from "./utils.js";
import { MarathonType } from "@prisma/client";
+const createEventSchema = z.object({
+ name: z.string(),
+ startsAt: z.string().datetime(),
+ endsAt: z.string().datetime(),
+ marathonTypes: z.array(z.enum([MarathonType.ONLINE, MarathonType.ONSITE])),
+});
+
export const eventsRouter = router({
list: adminProcedure.input(listSchema).query(async ({ input }) => {
const data = await prisma.event.findMany({
@@ -15,6 +22,9 @@ export const eventsRouter = router({
endsAt: input.orderBy === "endsAt" ? input.order : undefined,
published: input.orderBy === "published" ? input.order : undefined,
},
+ include: {
+ eventMarathonTypes: true,
+ },
});
const count = await prisma.event.count();
return { data, count };
@@ -26,6 +36,9 @@ export const eventsRouter = router({
where: {
id: input.id,
},
+ include: {
+ eventMarathonTypes: true,
+ },
});
if (!event) {
throw new Error("event not found");
@@ -33,19 +46,7 @@ export const eventsRouter = router({
return event;
}),
create: adminProcedure
- .input(
- z.object({
- name: z.string(),
- startsAt: z
- .string()
- .datetime()
- .transform((date) => new Date(date)),
- endsAt: z
- .string()
- .datetime()
- .transform((date) => new Date(date)),
- }),
- )
+ .input(createEventSchema)
.mutation(async ({ input }) => {
const event = await prisma.event.create({
data: {
@@ -54,6 +55,12 @@ export const eventsRouter = router({
endsAt: input.endsAt,
},
});
+ await prisma.eventMarathonType.createMany({
+ data: input.marathonTypes.map((type) => ({
+ eventId: event.id,
+ marathonType: type,
+ })),
+ });
return event;
}),
update: adminProcedure
@@ -83,6 +90,7 @@ export const eventsRouter = router({
published: input.data.published,
},
});
+ // TODO: update event types
return event;
}),
delete: adminProcedure