Skip to content
Merged
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
2 changes: 1 addition & 1 deletion apps/api/src/db/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ export const eventsTable = pgTable(
date: timestamp("date").notNull(),
aboutMarkdown: text("about_markdown"),
location: text("location"),
locationUrl: text("location_url"),
locationURL: text("location_url"),
form: json("form").$type<Array<CustomField>>(),
createdAt: timestamp("created_at").defaultNow(),
updatedAt: timestamp("updated_at")
Expand Down
16 changes: 8 additions & 8 deletions apps/api/src/modules/events/route.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -127,8 +127,8 @@ describe("Event route", () => {
expect(response.statusCode).toBe(200);
expect(data).toHaveLength(2);

expect(data[0].id).toBe("event-5");
expect(data[1].id).toBe("event-4");
expect(data[0].id).toBe("event-1");
expect(data[1].id).toBe("event-2");
});

it("should return the second page correctly", async () => {
Expand All @@ -143,7 +143,7 @@ describe("Event route", () => {
expect(data).toHaveLength(2);

expect(data[0].id).toBe("event-3");
expect(data[1].id).toBe("event-2");
expect(data[1].id).toBe("event-4");
});

it("should return an empty array if page is out of bounds", async () => {
Expand Down Expand Up @@ -257,7 +257,7 @@ describe("Event route", () => {
});
});

describe("PUT /v1/events/:id", () => {
describe("POST /v1/events/:id", () => {
beforeEach(async () => {
await db.insert(eventsTable).values({
id: "existing-event",
Expand All @@ -273,7 +273,7 @@ describe("Event route", () => {
setMockAuth({ userId: null, sessionClaims: null });

const response = await app.inject({
method: "PUT",
method: "POST",
url: "/v1/events/existing-event",
payload: { title: "Updated Title" },
});
Expand All @@ -289,7 +289,7 @@ describe("Event route", () => {
});

const response = await app.inject({
method: "PUT",
method: "POST",
url: "/v1/events/existing-event",
payload: { title: "Updated Title" },
});
Expand All @@ -304,7 +304,7 @@ describe("Event route", () => {
});

const response = await app.inject({
method: "PUT",
method: "POST",
url: "/v1/events/existing-event",
payload: {
title: "Updated Event Title",
Expand All @@ -327,7 +327,7 @@ describe("Event route", () => {
});

const response = await app.inject({
method: "PUT",
method: "POST",
url: "/v1/events/non-existing-event",
payload: { title: "Updated Title" },
});
Expand Down
5 changes: 3 additions & 2 deletions apps/api/src/modules/events/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ export const eventRoutes = async (server: FastifyInstance) => {
return reply.status(201).send(newEvent);
});

server.put("/:id", async (request, reply) => {
server.post("/:id", async (request, reply) => {
Copy link

Copilot AI Jan 5, 2026

Choose a reason for hiding this comment

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

The HTTP method for updating an event should typically be PUT or PATCH, not POST. Using POST for updates is not RESTful and can be confusing. Consider changing this to PUT to follow REST conventions.

Suggested change
server.post("/:id", async (request, reply) => {
server.put("/:id", async (request, reply) => {

Copilot uses AI. Check for mistakes.
const { userId, sessionClaims } = getAuth(request);
const role = sessionClaims?.metadata?.role;

Expand All @@ -77,8 +77,9 @@ export const eventRoutes = async (server: FastifyInstance) => {
const dto = UpdateEventContractSchema.parse(request.body);

const data = UpdateEventSchema.parse({
id,
...dto,
id,
...(dto.date && { date: new Date(dto.date) }),
Comment on lines 79 to +82
Copy link

Copilot AI Jan 5, 2026

Choose a reason for hiding this comment

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

The order of object spreading here could lead to the 'id' being overwritten if it exists in 'dto'. The id should be applied after spreading dto to ensure it takes precedence, or extract it from dto first to avoid conflicts.

Copilot uses AI. Check for mistakes.
});

const updatedEvent = await eventService.updateEvent({
Expand Down
17 changes: 6 additions & 11 deletions apps/api/src/modules/events/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,7 @@ import { eventStore } from "./store.js";
import { SqlContext } from "../../db/db.js";
import { CreateEvent, EventId, EventsQueryFilter, UpdateEvent } from "./schema.js";
import { NotFoundError, UnauthorizedError } from "../../lib/errors.js";
import {
UserRole as UserRoleConst,
EventState,
Nullable,
UserRole,
} from "@events.comp-soc.com/shared";
import { UserRole, EventState, Nullable } from "@events.comp-soc.com/shared";

export const eventService = {
async getEvents({
Expand All @@ -19,7 +14,7 @@ export const eventService = {
filters: EventsQueryFilter;
role: Nullable<UserRole>;
}) {
const isCommittee = role === UserRoleConst.Committee;
const isCommittee = role === UserRole.Committee;

const authorisedFilters = {
...filters,
Expand All @@ -40,7 +35,7 @@ export const eventService = {
}) {
const { id } = data;
const event = await eventStore.findById({ db, data });
const isCommittee = role === UserRoleConst.Committee;
const isCommittee = role === UserRole.Committee;

if (!event || (!isCommittee && event.state === EventState.Draft)) {
throw new NotFoundError(`Event with ${id} not found`);
Expand All @@ -50,7 +45,7 @@ export const eventService = {
},

async createEvent({ db, data, role }: { db: SqlContext; data: CreateEvent; role: UserRole }) {
if (role !== UserRoleConst.Committee) {
if (role !== UserRole.Committee) {
throw new UnauthorizedError("Only committee can create an event");
}

Expand All @@ -60,7 +55,7 @@ export const eventService = {
async updateEvent({ db, data, role }: { db: SqlContext; data: UpdateEvent; role: UserRole }) {
const { id } = data;

if (role !== UserRoleConst.Committee) {
if (role !== UserRole.Committee) {
throw new UnauthorizedError("Only committee members can update events");
}

Expand All @@ -75,7 +70,7 @@ export const eventService = {
async deleteEvent({ db, data, role }: { db: SqlContext; data: EventId; role: UserRole }) {
const { id } = data;

if (role !== UserRoleConst.Committee) {
if (role !== UserRole.Committee) {
throw new UnauthorizedError("Only committee can delete an event");
}

Expand Down
4 changes: 2 additions & 2 deletions apps/api/src/modules/events/store.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { desc, eq } from "drizzle-orm";
import { eq } from "drizzle-orm";
import { SqlContext } from "../../db/db.js";
import { CreateEvent, EventId, EventsQueryFilter, UpdateEvent } from "./schema.js";
import { eventsTable } from "../../db/schema.js";
Expand Down Expand Up @@ -41,7 +41,7 @@ export const eventStore = {
.where(state ? eq(eventsTable.state, state) : undefined)
.limit(limit)
.offset(offset)
.orderBy(desc(eventsTable.date));
.orderBy(eventsTable.date);
},

async findById({ db, data }: { db: SqlContext; data: EventId }) {
Expand Down
8 changes: 4 additions & 4 deletions apps/api/src/modules/users/service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { SqlContext } from "../../db/db.js";
import { CreateUser, UserId, UpdateUser } from "./schema.js";
import { NotFoundError, UnauthorizedError } from "../../lib/errors.js";
import { userStore } from "./store.js";
import { Nullable, UserRole as UserRoleConst, UserRole } from "@events.comp-soc.com/shared";
import { Nullable, UserRole } from "@events.comp-soc.com/shared";

export const userService = {
async getUserById({
Expand All @@ -23,7 +23,7 @@ export const userService = {
throw new NotFoundError(`User with ${id} not found`);
}

if (role === UserRoleConst.Committee || user.id === requesterId) {
if (role === UserRole.Committee || user.id === requesterId) {
return user;
}

Expand All @@ -48,7 +48,7 @@ export const userService = {
}) {
const { id } = data;

const isCommittee = role === UserRoleConst.Committee;
const isCommittee = role === UserRole.Committee;
const isSelf = id === requesterId;

if (!isCommittee && !isSelf) {
Expand Down Expand Up @@ -76,7 +76,7 @@ export const userService = {
}) {
const { id } = data;

const isCommittee = role === UserRoleConst.Committee;
const isCommittee = role === UserRole.Committee;
const isSelf = id === requesterId;

if (!isCommittee && !isSelf) {
Expand Down
4 changes: 2 additions & 2 deletions apps/shared/src/events/schemas.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { z } from "zod";
import { Sigs } from "../core/constants";
import { EventPriority, EventState } from "./constants";
import { Sigs } from "../core/constants.js";
import { EventPriority, EventState } from "./constants.js";

export const CustomFieldSchema = z
.object({
Expand Down
6 changes: 5 additions & 1 deletion apps/shared/src/events/types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import { z } from "zod";
import { CustomFieldSchema, type EventContractSchema, type EventResponseSchema } from "./schemas";
import {
CustomFieldSchema,
type EventContractSchema,
type EventResponseSchema,
} from "./schemas.js";

export type CreateEventRequest = z.infer<typeof EventContractSchema>;
export type UpdateEventRequest = Partial<CreateEventRequest>;
Expand Down
2 changes: 1 addition & 1 deletion apps/shared/src/registrations/schemas.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { z } from "zod";
import { RegistrationStatus } from "./constants";
import { RegistrationStatus } from "./constants.js";

export const RegistrationAnswerSchema = z.record(
z.string(),
Expand Down
2 changes: 1 addition & 1 deletion apps/shared/src/registrations/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import {
RegistrationResponseSchema,
RegistrationAnswerSchema,
RegistrationUpdateContractSchema,
} from "./schemas";
} from "./schemas.js";

export type CreateRegistrationRequest = z.infer<typeof RegistrationContractSchema>;
export type UpdateRegistrationRequest = z.infer<typeof RegistrationUpdateContractSchema>;
Expand Down
2 changes: 1 addition & 1 deletion apps/shared/src/users/types.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { z } from "zod";
import { UserContractSchema, UserResponseSchema } from "./schemas";
import { UserContractSchema, UserResponseSchema } from "./schemas.js";

export type CreateUserRequest = z.infer<typeof UserContractSchema>;
export type UpdateUserRequest = Partial<CreateUserRequest>;
Expand Down
2 changes: 1 addition & 1 deletion apps/web/.cta.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"projectName": "web",
"projectName": "@events.comp-soc.com/web",
"mode": "file-router",
"typescript": true,
"tailwind": true,
Expand Down
1 change: 1 addition & 0 deletions apps/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
},
"dependencies": {
"@clerk/clerk-react": "^5.49.0",
"@clerk/tanstack-react-start": "^0.27.8",
"@clerk/themes": "^2.4.46",
"@events.comp-soc.com/shared": "workspace:*",
"@radix-ui/react-label": "^2.1.8",
Expand Down
9 changes: 9 additions & 0 deletions apps/web/src/components/draft-badge.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
function DraftBadge() {
return (
<span className="text-[10px] uppercase tracking-wider font-bold text-neutral-500 border border-neutral-800 px-1.5 py-0.5 rounded">
Draft
</span>
)
}

export default DraftBadge
Loading