Skip to content

Commit a35024d

Browse files
committed
STNDP-144 Handle todo comments and miscellaneous bugs - cleanup (#92)
* STNDP-144 Handle todo comments and miscellaneous bugs - cleanup
1 parent 4023557 commit a35024d

27 files changed

Lines changed: 1065 additions & 657 deletions

apps/api/drizzle-dev.config.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@ import { defineConfig } from 'drizzle-kit';
22
import * as dotenv from 'dotenv';
33
import path from 'path';
44

5-
// TODO: Seems like this is not working. It always uses the .env file. Make it work.
6-
dotenv.config({ path: path.join(__dirname, '.env.development.local') });
5+
dotenv.config({
6+
path: path.join(__dirname, '.env.development.local'),
7+
override: true,
8+
});
79

810
export default defineConfig({
911
out: './src/libs/db/migrations',

apps/api/src/app.controller.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Controller, Get } from '@nestjs/common';
1+
import { Controller, Get, InternalServerErrorException } from '@nestjs/common';
22
import { AppService } from './app.service';
33

44
@Controller()
@@ -7,6 +7,11 @@ export class AppController {
77

88
@Get()
99
getHello(): string {
10-
return this.appService.getHello();
10+
try {
11+
return this.appService.getHello();
12+
} catch (error) {
13+
console.error(error);
14+
throw new InternalServerErrorException();
15+
}
1116
}
1217
}

apps/api/src/auth/auth-service.types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ export interface User {
3838
display_name: string | null;
3939
selected_team: Team | null;
4040
selected_team_id: string | null;
41-
profile_image_url: string;
41+
profile_image_url: string | null;
4242
client_metadata: Record<string, any> | null;
4343
client_read_only_metadata: ClientReadOnlyMetadata | null;
4444
server_metadata: Record<string, any> | null;
Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,23 @@
1-
import { Controller, Post, UseGuards } from '@nestjs/common';
1+
import {
2+
Controller,
3+
Post,
4+
UseGuards,
5+
InternalServerErrorException,
6+
} from '@nestjs/common';
27
import { AuthGuard } from '../guards/auth.guard';
38

49
@Controller('auth/token')
510
export class TokenController {
611
@UseGuards(AuthGuard)
712
@Post('verify')
813
async verifyToken() {
9-
return {
10-
valid: true,
11-
};
14+
try {
15+
return {
16+
valid: true,
17+
};
18+
} catch (error) {
19+
console.error(error);
20+
throw new InternalServerErrorException();
21+
}
1222
}
1323
}

apps/api/src/auth/users/users.controller.ts

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,17 +19,26 @@ export class UsersController {
1919

2020
@UseGuards(AuthGuard)
2121
@Get('me')
22-
getMe(@Req() request: AuthenticatedRequest) {
23-
const userId = request.userId;
24-
return this.usersService.get(userId);
22+
async getMe(@Req() request: AuthenticatedRequest) {
23+
try {
24+
const userId = request.userId;
25+
return await this.usersService.get(userId);
26+
} catch (error) {
27+
console.error(error);
28+
throw new InternalServerErrorException();
29+
}
2530
}
2631

2732
@UseGuards(AuthGuard)
2833
@Get('me/boards')
29-
getMyBoards(@Req() request: AuthenticatedRequest): Promise<Board[]> {
30-
const userId = request.userId;
31-
// TODO: handle error case
32-
return this.usersService.getBoardsOfUser(userId);
34+
async getMyBoards(@Req() request: AuthenticatedRequest): Promise<Board[]> {
35+
try {
36+
const userId = request.userId;
37+
return await this.usersService.getBoardsOfUser(userId);
38+
} catch (error) {
39+
console.error(error);
40+
throw new InternalServerErrorException();
41+
}
3342
}
3443

3544
@UseGuards(AuthGuard)

apps/api/src/auth/users/users.service.ts

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -78,12 +78,23 @@ export class UsersService {
7878
},
7979
},
8080
);
81-
const user: User = await response.json(); // TODO: handle error case
81+
82+
if (!response.ok) {
83+
throw new Error(
84+
`Failed to fetch user: ${response.status} ${response.statusText}`,
85+
);
86+
}
87+
88+
const data: User | { error: string; code: string } = await response.json();
89+
90+
if ('error' in data) {
91+
throw new Error(data.error);
92+
}
8293

8394
// NOTE: This is to avoid the server_metadata property from being included in the response
84-
user.server_metadata = null;
95+
data.server_metadata = null;
8596

86-
return user;
97+
return data;
8798
}
8899

89100
async list(params?: {
@@ -158,9 +169,19 @@ export class UsersService {
158169
},
159170
);
160171

161-
const updatedUser: User = await response.json(); // TODO: handle error case
172+
if (!response.ok) {
173+
throw new Error(
174+
`Failed to update user: ${response.status} ${response.statusText}`,
175+
);
176+
}
177+
178+
const data: User | { error: string; code: string } = await response.json();
179+
180+
if ('error' in data) {
181+
throw new Error(data.error);
182+
}
162183

163-
return updatedUser;
184+
return data;
164185
}
165186

166187
async updateClientReadOnlyMetadata(

apps/api/src/libs/db/schema.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ import {
88
serial,
99
primaryKey,
1010
jsonb,
11-
AnyPgColumn,
1211
pgEnum,
12+
type AnyPgColumn,
1313
} from 'drizzle-orm/pg-core';
1414

1515
// TODO: add owner role

apps/api/src/types/index.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// Database types from Drizzle schema
2+
export type {
3+
Board,
4+
InsertBoard,
5+
StandupForm,
6+
InsertStandupForm,
7+
Standup,
8+
InsertStandup,
9+
UsersToBoards,
10+
InsertUsersToBoards,
11+
Invitation,
12+
InsertInvitation,
13+
UsersToBoardsRole,
14+
} from '../libs/db/schema';
15+
16+
// Auth service types
17+
export type {
18+
User,
19+
ListUser,
20+
Team,
21+
ClientReadOnlyMetadata,
22+
ViewType,
23+
OtpSignInResponse,
24+
} from '../auth/auth-service.types';
25+
26+
// Add other domain types here as the project grows
27+
// export type { SomeType } from '../other/module';
Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
import { useEffect, useRef } from "react";
2+
3+
// Credit: Based on https://github.com/uidotdev/usehooks/blob/experimental/index.js
4+
// Enhanced for keyboard shortcuts with key combinations and input field detection
5+
6+
interface UseKeyPressOptions {
7+
event?: "keydown" | "keyup";
8+
target?: Window | Document | HTMLElement;
9+
eventOptions?: boolean | AddEventListenerOptions;
10+
}
11+
12+
// Helper function to parse key combinations like "Meta+Enter" or "Control+Enter"
13+
function parseKeySpec(keySpec: string): {
14+
key: string;
15+
ctrlKey?: boolean;
16+
metaKey?: boolean;
17+
shiftKey?: boolean;
18+
altKey?: boolean;
19+
} {
20+
const parts = keySpec.split("+");
21+
const result: {
22+
key: string;
23+
ctrlKey?: boolean;
24+
metaKey?: boolean;
25+
shiftKey?: boolean;
26+
altKey?: boolean;
27+
} = {
28+
key: "", // Will be set when we find the actual key
29+
};
30+
31+
for (const part of parts) {
32+
const normalizedPart = part.toLowerCase();
33+
34+
switch (normalizedPart) {
35+
case "ctrl":
36+
case "control":
37+
result.ctrlKey = true;
38+
break;
39+
case "meta":
40+
case "cmd":
41+
case "command":
42+
result.metaKey = true;
43+
break;
44+
case "shift":
45+
result.shiftKey = true;
46+
break;
47+
case "alt":
48+
case "option":
49+
result.altKey = true;
50+
break;
51+
default:
52+
result.key = part; // Keep original case for the actual key
53+
}
54+
}
55+
56+
return result;
57+
}
58+
59+
// Helper function to check if event matches the key specification
60+
function matchesKeySpec(event: KeyboardEvent, keySpec: string): boolean {
61+
const spec = parseKeySpec(keySpec);
62+
63+
// Check the main key
64+
if (spec.key && event.key !== spec.key) {
65+
return false;
66+
}
67+
68+
// Ensure modifiers match exactly. If not in spec, they must be false.
69+
if (event.ctrlKey !== (spec.ctrlKey ?? false)) {
70+
return false;
71+
}
72+
if (event.metaKey !== (spec.metaKey ?? false)) {
73+
return false;
74+
}
75+
if (event.shiftKey !== (spec.shiftKey ?? false)) {
76+
return false;
77+
}
78+
if (event.altKey !== (spec.altKey ?? false)) {
79+
return false;
80+
}
81+
82+
return true;
83+
}
84+
85+
export function useKeyPress(
86+
key: string | string[],
87+
callback: (event: KeyboardEvent) => void,
88+
options: UseKeyPressOptions = {}
89+
) {
90+
const {
91+
event = "keydown",
92+
target = typeof window !== "undefined" ? window : null,
93+
eventOptions,
94+
} = options;
95+
96+
const callbackRef = useRef(callback);
97+
98+
// Keep callback ref up to date
99+
useEffect(() => {
100+
callbackRef.current = callback;
101+
});
102+
103+
useEffect(() => {
104+
if (!target) return;
105+
106+
const handler = (evt: Event) => {
107+
const event = evt as KeyboardEvent;
108+
const keys = Array.isArray(key) ? key : [key];
109+
110+
// Check if any of the specified keys match
111+
for (const keySpec of keys) {
112+
if (matchesKeySpec(event, keySpec)) {
113+
callbackRef.current(event);
114+
break;
115+
}
116+
}
117+
};
118+
119+
const targetElement = target as EventTarget;
120+
targetElement.addEventListener(event, handler, eventOptions);
121+
122+
return () => {
123+
targetElement.removeEventListener(event, handler, eventOptions);
124+
};
125+
}, [key, target, event, eventOptions]);
126+
}

apps/web/app/radix.css

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,6 @@
1414
--default-font-family: "Inter", -apple-system, BlinkMacSystemFont,
1515
"Segoe UI (Custom)", Roboto, "Helvetica Neue", "Open Sans (Custom)",
1616
system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji";
17+
18+
--color-background: var(--accent-1);
1719
}

0 commit comments

Comments
 (0)