Skip to content

Commit c2b52ea

Browse files
authored
Merge pull request #68 from Team3132/create-command
2 parents 54d062b + 9b19b42 commit c2b52ea

File tree

14 files changed

+186
-15
lines changed

14 files changed

+186
-15
lines changed

.vscode/settings.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
11
{
2-
"typescript.tsdk": "node_modules/typescript/lib"
2+
"typescript.tsdk": "node_modules/typescript/lib",
3+
"editor.formatOnSave": true,
4+
"editor.codeActionsOnSave": {
5+
"source.fixAll.eslint": true
6+
},
37
}

packages/backend/src/bot/bot.module.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { RsvpButton } from './buttons/RsvpButton';
77
import { RsvpsButton } from './buttons/RsvpsButton';
88
import { AttendanceCommand } from './commands/Attendance.command';
99
import { CheckinCommand } from './commands/Checkin.command';
10+
import { CreateCommand } from './commands/Create.command';
1011
import { MeetingsCommand } from './commands/Meetings.command';
1112
import { RequestRsvpCommand } from './commands/RequestRsvp.command';
1213
import { RsvpCommand } from './commands/Rsvp.command';
@@ -27,6 +28,7 @@ import { DelayModal } from './modals/Delay.modal';
2728
RequestRsvpCommand,
2829
RsvpCommand,
2930
RsvpsCommand,
31+
CreateCommand,
3032
DelayModal,
3133
],
3234
exports: [BotService],
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import { AuthenticatorService } from '@/authenticator/authenticator.service';
2+
import { PrismaService } from '@/prisma/prisma.service';
3+
import { Injectable, Logger, UseInterceptors } from '@nestjs/common';
4+
import { ConfigService } from '@nestjs/config';
5+
import { EmbedBuilder, hideLinkEmbed, PermissionFlagsBits } from 'discord.js';
6+
import { SlashCommand, Context, SlashCommandContext, Options } from 'necord';
7+
import { URLSearchParams } from 'url';
8+
import { CreateDto } from '../dto/create.dto';
9+
import { EventAutocompleteInterceptor } from '../interceptors/event.interceptor';
10+
11+
@Injectable()
12+
export class CreateCommand {
13+
private readonly logger = new Logger(CreateCommand.name);
14+
15+
constructor(
16+
private readonly db: PrismaService,
17+
private readonly config: ConfigService,
18+
private readonly authenticator: AuthenticatorService,
19+
) {}
20+
21+
@UseInterceptors(EventAutocompleteInterceptor)
22+
@SlashCommand({
23+
name: 'create',
24+
description: 'Create a new event on the spot.',
25+
guilds: [process.env['GUILD_ID']],
26+
defaultMemberPermissions: PermissionFlagsBits.ManageRoles,
27+
})
28+
public async onCreate(
29+
@Context() [interaction]: SlashCommandContext,
30+
@Options() { eventName, eventType, role, allday, description }: CreateDto,
31+
) {
32+
const frontendUrl = this.config.getOrThrow<string>('FRONTEND_URL');
33+
34+
const startDate = interaction.createdAt.toISOString();
35+
36+
const endDate = new Date(startDate);
37+
38+
endDate.setHours(interaction.createdAt.getHours() + 3);
39+
40+
const endDateIso = endDate.toISOString();
41+
42+
const params = new URLSearchParams();
43+
44+
params.append('startDate', startDate);
45+
params.append('endDay', endDateIso);
46+
47+
if (allday) {
48+
params.append('allDay', allday.toString());
49+
}
50+
51+
if (eventType) {
52+
params.append('eventType', eventType);
53+
}
54+
55+
if (eventName) {
56+
params.append('eventName', eventName);
57+
}
58+
59+
if (role) {
60+
params.append('role', role.id);
61+
}
62+
63+
if (description) {
64+
params.append('description', description);
65+
}
66+
67+
const createUrl = `${frontendUrl}/event/create?${params.toString()}`;
68+
69+
const successEmbed = new EmbedBuilder()
70+
.setTitle('Success')
71+
.setColor('Green').setDescription(`
72+
Click on the link below to create the event,
73+
74+
${hideLinkEmbed(createUrl)}
75+
`);
76+
77+
return interaction.reply({ embeds: [successEmbed], ephemeral: true });
78+
}
79+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import { EventTypes } from '@prisma/client';
2+
import { Role } from 'discord.js';
3+
import { BooleanOption, RoleOption, StringOption } from 'necord';
4+
5+
export class CreateDto {
6+
@StringOption({
7+
name: 'eventname',
8+
description: 'The name of the event',
9+
})
10+
eventName: string;
11+
@StringOption({
12+
choices: Object.entries(EventTypes).map(([name, value]) => ({
13+
name,
14+
value,
15+
})),
16+
name: 'eventtype',
17+
description: 'Choose the type of event',
18+
required: false,
19+
})
20+
eventType?: 'Outreach' | 'Regular' | 'Social';
21+
@RoleOption({
22+
name: 'role',
23+
description: 'The primary role for this event',
24+
required: false,
25+
})
26+
role?: Role;
27+
@BooleanOption({
28+
name: 'allday',
29+
description: 'Whether the event lasts all day.',
30+
required: false,
31+
})
32+
allday?: boolean;
33+
@StringOption({
34+
name: 'description',
35+
description: 'Description of the event',
36+
required: false,
37+
})
38+
description?: string;
39+
}

packages/backend/src/event/dto/create-event.dto.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ export class CreateEventDto {
3535
@ApiProperty({ enum: EventTypes })
3636
type?: EventTypes;
3737
@IsOptional()
38+
@ApiProperty({ required: false })
3839
@IsString({
3940
each: true,
4041
})

packages/frontend/src/features/event/pages/CreateEventPage.tsx

Lines changed: 44 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,44 @@ import useCreateEvent from "../hooks/useCreateEvent";
2020
const CreateEventScreen: React.FC = () => {
2121
const navigate = useNavigate();
2222
const [searchParams, setSearchParams] = useSearchParams();
23-
const startQuery = searchParams.get("startDate");
23+
24+
const startDateQuery = searchParams.get("startDate");
25+
const startDate = startDateQuery
26+
? new Date(startDateQuery).toISOString()
27+
: new Date().toISOString();
28+
2429
const endQuery = searchParams.get("endDate");
30+
const endDate = endQuery
31+
? new Date(endQuery).toISOString()
32+
: new Date().toISOString();
33+
34+
const allDayQuery = searchParams.get("allDay");
35+
const allDay: boolean = allDayQuery
36+
? allDayQuery === "true"
37+
? true
38+
: allDayQuery === "false"
39+
? false
40+
: false
41+
: false;
42+
43+
const role = searchParams.get("role");
44+
45+
const eventTypeQuery = searchParams.get("eventType");
46+
47+
const eventType = eventTypeQuery
48+
? eventTypeQuery === CreateEventDto.type.OUTREACH
49+
? CreateEventDto.type.OUTREACH
50+
: eventTypeQuery === CreateEventDto.type.REGULAR
51+
? CreateEventDto.type.REGULAR
52+
: eventTypeQuery === CreateEventDto.type.SOCIAL
53+
? CreateEventDto.type.SOCIAL
54+
: undefined
55+
: undefined;
56+
57+
const description = searchParams.get("description") ?? undefined
58+
59+
const eventNameQuery = searchParams.get("eventName")
60+
2561
const {
2662
register,
2763
handleSubmit,
@@ -32,14 +68,13 @@ const CreateEventScreen: React.FC = () => {
3268
setValue,
3369
} = useForm<CreateEventDto>({
3470
defaultValues: {
35-
title: "Event Title",
36-
startDate: startQuery
37-
? new Date(startQuery).toISOString()
38-
: new Date().toISOString(),
39-
endDate: endQuery
40-
? new Date(endQuery).toISOString()
41-
: new Date().toISOString(),
42-
allDay: !!searchParams.get("allDay") ?? false,
71+
title: eventNameQuery ?? "Event Title",
72+
type: eventType,
73+
startDate,
74+
endDate,
75+
allDay,
76+
roles: role ? [role] : undefined,
77+
description,
4378
},
4479
});
4580

packages/frontend/src/features/rsvp/components/RSVPButtonRow.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
1-
import { Button, ButtonGroup, ButtonGroupProps, ButtonProps } from "@chakra-ui/react";
1+
import {
2+
Button,
3+
ButtonGroup,
4+
ButtonGroupProps,
5+
ButtonProps,
6+
} from "@chakra-ui/react";
27
import { Rsvp } from "@generated";
38
import useEventRSVPStatus from "../hooks/useEventRSVPStatus";
49
import useUpdateEventRSVPStatus from "../hooks/useUpdateEventRSVPStatus";
@@ -46,5 +51,4 @@ export const RSVPButtonRow: React.FC<RSVPButtonRowProps> = ({
4651
);
4752
};
4853

49-
5054
export default RSVPButtonRow;

packages/frontend/src/features/rsvp/components/RSVPList.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ const columns = [
5757
DateTime.fromISO(props.getValue()).toLocaleString(
5858
DateTime.DATETIME_MED
5959
),
60-
})
60+
}),
6161
],
6262
}),
6363
];
@@ -69,7 +69,7 @@ const readableStatus = (status: RsvpUser.status | null) => {
6969
return "Coming";
7070
} else if (status === "MAYBE") {
7171
return "Maybe";
72-
} else if(status === "LATE") {
72+
} else if (status === "LATE") {
7373
return "Late";
7474
} else {
7575
return "Not Coming";

packages/frontend/src/features/rsvp/components/RSVPSelect.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,13 @@ export const RSVPSelect: React.FC<
1919
if (
2020
currentValue === statusEnum.MAYBE ||
2121
currentValue === statusEnum.NO ||
22-
currentValue === statusEnum.YES
22+
currentValue === statusEnum.YES || currentValue === statusEnum.LATE
2323
) {
2424
mutateEventRSVP({
2525
eventId,
2626
rsvp: {
2727
status: currentValue,
28+
delay: null,
2829
},
2930
});
3031
} else {
@@ -42,6 +43,7 @@ export const RSVPSelect: React.FC<
4243
<option value={statusEnum.MAYBE}>Maybe</option>
4344
<option value={statusEnum.NO}>No</option>
4445
<option value={statusEnum.YES}>Yes</option>
46+
<option value={statusEnum.LATE}>Late</option>
4547
</Select>
4648
);
4749
};

packages/frontend/src/features/rsvp/components/StatusButton.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ export default function StatusButton({
2222
eventId,
2323
rsvp: {
2424
status,
25+
delay: null,
2526
},
2627
});
2728
};

0 commit comments

Comments
 (0)