Skip to content

Commit 2f23a72

Browse files
committed
chore: ui improvements and get guild route
1 parent 12c18e5 commit 2f23a72

13 files changed

Lines changed: 132 additions & 37 deletions

File tree

apps/website/src/app/dashboard/[id]/ama/amas/_components/AMASessionCard.tsx

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,7 @@ export function AMASessionCard({ data }: AMASessionCardProps) {
2121
</div>
2222
<div className="mt-auto flex items-center gap-2">
2323
<span
24-
className={`rounded px-2 py-1 text-xs font-medium ${
25-
data.ended
26-
? 'bg-red-100 text-red-800 dark:bg-red-900/30 dark:text-red-200'
27-
: 'bg-green-100 text-green-800 dark:bg-green-900/30 dark:text-green-200'
28-
}`}
24+
className={`rounded px-2 py-1 text-xs font-medium ${data.ended ? 'text-misc-danger' : 'text-misc-accent'}`}
2925
>
3026
{data.ended ? 'Ended' : 'Active'}
3127
</span>

apps/website/src/app/dashboard/[id]/ama/amas/new/_components/CreateAMAForm.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ export function CreateAMAForm() {
8181
const newErrors: FormErrors = {};
8282

8383
if (!formData.title.trim()) {
84-
newErrors.title = 'Title is required';
84+
newErrors.title = 'This field is required';
8585
} else if (formData.title.length > 255) {
8686
newErrors.title = 'Title must be at most 255 characters';
8787
}
@@ -191,11 +191,11 @@ export function CreateAMAForm() {
191191
id="title"
192192
maxLength={255}
193193
onChange={(e) => setFormData({ ...formData, title: e.target.value })}
194-
placeholder="My AMA Session"
194+
placeholder="AMA with renowed JP VA John Doe"
195195
type="text"
196196
value={formData.title}
197197
/>
198-
{errors.title && <p className="mt-1 text-sm text-red-500">{errors.title}</p>}
198+
{errors.title && <p className="mt-1 text-sm text-misc-danger">{errors.title}</p>}
199199
</div>
200200

201201
<SnowflakeInput

apps/website/src/app/dashboard/[id]/ama/amas/new/_components/NormalPromptFields.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ export function NormalPromptFields({
4141
type="text"
4242
value={plainText}
4343
/>
44-
{errors.plainText && <p className="mt-1 text-sm text-red-500">{errors.plainText}</p>}
44+
{errors.plainText && <p className="mt-1 text-sm text-misc-danger">{errors.plainText}</p>}
4545
</div>
4646

4747
<div>
@@ -57,7 +57,7 @@ export function NormalPromptFields({
5757
rows={4}
5858
value={description}
5959
/>
60-
{errors.description && <p className="mt-1 text-sm text-red-500">{errors.description}</p>}
60+
{errors.description && <p className="mt-1 text-sm text-misc-danger">{errors.description}</p>}
6161
</div>
6262

6363
<div>
@@ -72,7 +72,7 @@ export function NormalPromptFields({
7272
type="url"
7373
value={imageURL}
7474
/>
75-
{errors.imageURL && <p className="mt-1 text-sm text-red-500">{errors.imageURL}</p>}
75+
{errors.imageURL && <p className="mt-1 text-sm text-misc-danger">{errors.imageURL}</p>}
7676
</div>
7777

7878
<div>
@@ -90,7 +90,7 @@ export function NormalPromptFields({
9090
type="url"
9191
value={thumbnailURL}
9292
/>
93-
{errors.thumbnailURL && <p className="mt-1 text-sm text-red-500">{errors.thumbnailURL}</p>}
93+
{errors.thumbnailURL && <p className="mt-1 text-sm text-misc-danger">{errors.thumbnailURL}</p>}
9494
</div>
9595
</div>
9696
);

apps/website/src/app/dashboard/[id]/ama/amas/new/_components/SnowflakeInput.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ export function SnowflakeInput({
3030
type="text"
3131
value={value}
3232
/>
33-
{error && <p className="mt-1 text-sm text-red-500">{error}</p>}
33+
{error && <p className="mt-1 text-sm text-misc-danger">{error}</p>}
3434
</div>
3535
);
3636
}

services/api/src/routes/_types/routeTypes.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,5 @@ export type * from '../auth/discord.js';
55
export type * from '../auth/discordCallback.js';
66
export type * from '../auth/logout.js';
77
export type * from '../auth/me.js';
8+
9+
export type * from '../guilds/get.js';

services/api/src/routes/ama/createAMA.ts

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,12 @@ import { Route, RouteMethod } from '../route.js';
1515

1616
const promptSchema = z.union([
1717
z.strictObject({
18-
prompt: z
19-
.object({
20-
description: z.string().max(4_000).optional(),
21-
plainText: z.string().max(100).optional(),
22-
imageURL: z.url().optional(),
23-
thumbnailURL: z.url().optional(),
24-
})
25-
.strict(),
18+
prompt: z.strictObject({
19+
description: z.string().max(4_000).optional(),
20+
plainText: z.string().max(100).optional(),
21+
imageURL: z.url().optional(),
22+
thumbnailURL: z.url().optional(),
23+
}),
2624
}),
2725
z.strictObject({
2826
prompt_raw: z.strictObject({

services/api/src/routes/ama/getAMAs.ts

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,9 @@ import { isAuthed } from '../../middleware/isAuthed.js';
77
import type { TRequest } from '../route.js';
88
import { Route, RouteMethod } from '../route.js';
99

10-
const querySchema = z
11-
.object({
12-
include_ended: z.stringbool().optional().default(false),
13-
})
14-
.strict();
10+
const querySchema = z.strictObject({
11+
include_ended: z.stringbool().optional().default(false),
12+
});
1513

1614
export type GetAMAsQuery = z.input<typeof querySchema>;
1715

services/api/src/routes/auth/discordCallback.ts

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,10 @@ import type { TRequest } from '../route.js';
1414
import { Route, RouteMethod } from '../route.js';
1515
import { DISCORD_AUTH_SCOPES } from './discord.js';
1616

17-
const querySchema = z
18-
.object({
19-
code: z.string(),
20-
state: z.string(),
21-
})
22-
.strict();
17+
const querySchema = z.strictObject({
18+
code: z.string(),
19+
state: z.string(),
20+
});
2321

2422
export default class GetAuthDiscordCallback extends Route<never, typeof querySchema> {
2523
public readonly info = {

services/api/src/routes/auth/me.ts

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,9 @@ import { Route, RouteMethod } from '../route.js';
88

99
export type { Me, MeGuild } from '../../util/me.js';
1010

11-
const querySchema = z
12-
.object({
13-
force_fresh: z.stringbool().optional().default(false),
14-
})
15-
.strict();
11+
const querySchema = z.strictObject({
12+
force_fresh: z.stringbool().optional().default(false),
13+
});
1614
export type GetAuthMeQuery = z.input<typeof querySchema>;
1715

1816
export default class GetAuthMe extends Route<Me, typeof querySchema> {
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import { BOTS } from '@chatsift/backend-core';
2+
import { internal } from '@hapi/boom';
3+
import type { NextHandler, Response } from 'polka';
4+
import z from 'zod';
5+
import { context } from '../../context.js';
6+
import { isAuthed } from '../../middleware/isAuthed.js';
7+
import { fetchGuildChannels, type GuildChannelInfo } from '../../util/channels.js';
8+
import { APIMapping } from '../../util/discordAPI.js';
9+
import { fetchMe } from '../../util/me.js';
10+
import type { TRequest } from '../route.js';
11+
import { Route, RouteMethod } from '../route.js';
12+
13+
export type { GuildChannelInfo } from '../../util/channels.js';
14+
15+
const querySchema = z.strictObject({
16+
for_bot: z.enum(BOTS),
17+
force_fresh: z.stringbool().optional().default(false),
18+
});
19+
export type GetGuildQuery = z.input<typeof querySchema>;
20+
21+
export interface GetGuildResult {
22+
channels: GuildChannelInfo[];
23+
}
24+
25+
export default class GetGuild extends Route<GetGuildResult, typeof querySchema> {
26+
public readonly info = {
27+
method: RouteMethod.get,
28+
path: '/v3/guilds/:guildId',
29+
} as const;
30+
31+
public override readonly queryValidationSchema = querySchema;
32+
33+
public override readonly middleware = [
34+
...isAuthed({ fallthrough: false, isGlobalAdmin: false, isGuildManager: true }),
35+
];
36+
37+
public override async handle(req: TRequest<typeof querySchema>, res: Response, next: NextHandler) {
38+
const { guildId } = req.params as { guildId: string };
39+
const { force_fresh, for_bot } = req.query;
40+
41+
const me = await fetchMe(req.tokens!.access.discordAccessToken, force_fresh);
42+
const guild = me.guilds.find((g) => g.id === guildId);
43+
if (!guild) {
44+
// Should be impossible because of the auth checks
45+
context.logger.error({ guildId, userId: me.id }, 'guild not found for user in auth-gated route');
46+
return next(internal());
47+
}
48+
49+
const channels = await fetchGuildChannels(guild, APIMapping[for_bot], force_fresh);
50+
const result: GetGuildResult = {
51+
channels,
52+
};
53+
54+
res.statusCode = 200;
55+
res.setHeader('Content-Type', 'application/json');
56+
return res.end(JSON.stringify(result));
57+
}
58+
}

0 commit comments

Comments
 (0)