Skip to content

Commit 4b4e66a

Browse files
committed
logging
1 parent 737eed7 commit 4b4e66a

File tree

6 files changed

+184
-9
lines changed

6 files changed

+184
-9
lines changed

apps/api/src/trpc/routers/team.ts

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -69,11 +69,40 @@ export const teamRouter = createTRPCRouter({
6969
create: protectedProcedure
7070
.input(createTeamSchema)
7171
.mutation(async ({ ctx: { db, session }, input }) => {
72-
return createTeam(db, {
73-
...input,
72+
const requestId = `trpc_team_create_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
73+
74+
console.log(`[${requestId}] TRPC team creation request`, {
7475
userId: session.user.id,
75-
email: session.user.email!,
76+
userEmail: session.user.email,
77+
teamName: input.name,
78+
baseCurrency: input.baseCurrency,
79+
countryCode: input.countryCode,
80+
switchTeam: input.switchTeam,
81+
timestamp: new Date().toISOString(),
7682
});
83+
84+
try {
85+
const teamId = await createTeam(db, {
86+
...input,
87+
userId: session.user.id,
88+
email: session.user.email!,
89+
});
90+
91+
console.log(`[${requestId}] TRPC team creation successful`, {
92+
teamId,
93+
userId: session.user.id,
94+
});
95+
96+
return teamId;
97+
} catch (error) {
98+
console.error(`[${requestId}] TRPC team creation failed`, {
99+
error: error instanceof Error ? error.message : String(error),
100+
stack: error instanceof Error ? error.stack : undefined,
101+
userId: session.user.id,
102+
input,
103+
});
104+
throw error;
105+
}
77106
}),
78107

79108
leave: protectedProcedure

apps/dashboard/sentry.edge.config.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,5 +19,9 @@ if (process.env.NODE_ENV === "production") {
1919

2020
// Disable debug
2121
debug: false,
22+
23+
// Enable session replay for debugging team creation issues
24+
replaysSessionSampleRate: 0.1, // 10% of sessions
25+
replaysOnErrorSampleRate: 1.0, // 100% of sessions with errors
2226
});
2327
}

apps/dashboard/sentry.server.config.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,5 +18,8 @@ if (process.env.NODE_ENV === "production") {
1818

1919
// Setting this option to true will print useful information to the console while you're setting up Sentry.
2020
debug: false,
21+
22+
replaysSessionSampleRate: 0.1, // 10% of sessions
23+
replaysOnErrorSampleRate: 1.0, // 100% of sessions with errors
2124
});
2225
}

apps/dashboard/src/components/forms/create-team-form.tsx

Lines changed: 81 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ const formSchema = z.object({
2929
baseCurrency: z.string(),
3030
});
3131

32+
type FormValues = z.infer<typeof formSchema>;
33+
3234
type Props = {
3335
defaultCurrencyPromise: Promise<string>;
3436
defaultCountryCodePromise: Promise<string>;
@@ -47,23 +49,80 @@ export function CreateTeamForm({
4749

4850
const createTeamMutation = useMutation(
4951
trpc.team.create.mutationOptions({
50-
onSuccess: async () => {
52+
onSuccess: async (teamId) => {
53+
const successId = `team_creation_success_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
54+
55+
console.log(`[${successId}] Team creation mutation successful`, {
56+
teamId,
57+
timestamp: new Date().toISOString(),
58+
url: window.location.href,
59+
});
60+
5161
// Lock the form permanently - never reset on success
5262
setIsLoading(true);
5363
isSubmittedRef.current = true;
5464

5565
try {
5666
// Invalidate all queries to ensure fresh data everywhere
67+
console.log(`[${successId}] Invalidating queries`);
5768
await queryClient.invalidateQueries();
69+
5870
// Revalidate server-side paths and redirect
71+
console.log(`[${successId}] Revalidating server-side paths`);
5972
await revalidateAfterTeamChange();
73+
74+
console.log(
75+
`[${successId}] Team creation flow completed successfully`,
76+
);
6077
} catch (error) {
61-
// Even if redirect fails, keep the form locked to prevent duplicates
62-
console.error("Redirect failed, but keeping form locked:", error);
78+
// Check if this is a Next.js redirect (expected behavior)
79+
if (error instanceof Error && error.message === "NEXT_REDIRECT") {
80+
console.log(
81+
`[${successId}] Team creation completed successfully - redirecting to home`,
82+
);
83+
// This is expected - Next.js redirects work by throwing this error
84+
return;
85+
}
86+
87+
// Only log actual errors, not expected redirects
88+
console.error(`[${successId}] Team creation flow failed:`, {
89+
error: error instanceof Error ? error.message : String(error),
90+
stack: error instanceof Error ? error.stack : undefined,
91+
teamId,
92+
});
6393
}
6494
// Note: We NEVER reset loading state on success - user should be redirected
6595
},
66-
onError: () => {
96+
onError: (error) => {
97+
const errorId = `team_creation_error_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
98+
99+
const errorContext = {
100+
error: error instanceof Error ? error.message : String(error),
101+
stack: error instanceof Error ? error.stack : undefined,
102+
timestamp: new Date().toISOString(),
103+
url: window.location.href,
104+
userAgent: navigator.userAgent,
105+
};
106+
107+
console.error(
108+
`[${errorId}] Team creation mutation failed`,
109+
errorContext,
110+
);
111+
112+
// Capture error in Sentry for debugging
113+
if (error instanceof Error && process.env.NODE_ENV === "production") {
114+
import("@sentry/nextjs").then((Sentry) => {
115+
Sentry.captureException(error, {
116+
extra: {
117+
...errorContext,
118+
errorId,
119+
component: "CreateTeamForm",
120+
action: "team_creation_mutation",
121+
},
122+
});
123+
});
124+
}
125+
67126
setIsLoading(false);
68127
isSubmittedRef.current = false; // Reset on error to allow retry
69128
},
@@ -81,11 +140,28 @@ export function CreateTeamForm({
81140
// Computed loading state that can never be reset unexpectedly
82141
const isFormLocked = isLoading || isSubmittedRef.current;
83142

84-
function onSubmit(values: z.infer<typeof formSchema>) {
143+
function onSubmit(values: FormValues) {
85144
if (isFormLocked) {
145+
console.warn("Team creation form submission blocked - form is locked", {
146+
isFormLocked,
147+
isLoading,
148+
isSubmittedRef: isSubmittedRef.current,
149+
formValues: values,
150+
});
86151
return;
87152
}
88153

154+
const submissionId = `form_submission_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
155+
156+
console.log(`[${submissionId}] Team creation form submission started`, {
157+
teamName: values.name,
158+
baseCurrency: values.baseCurrency,
159+
countryCode: values.countryCode,
160+
timestamp: new Date().toISOString(),
161+
userAgent: navigator.userAgent,
162+
url: window.location.href,
163+
});
164+
89165
setIsLoading(true);
90166
isSubmittedRef.current = true; // Permanent flag that survives re-renders
91167

apps/dashboard/src/instrumentation-client.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ if (process.env.NODE_ENV === "production") {
2020

2121
// Disable debug
2222
debug: false,
23+
24+
replaysSessionSampleRate: 0.1, // 10% of sessions
25+
replaysOnErrorSampleRate: 1.0, // 100% of sessions with errors
2326
});
2427

2528
onRouterTransitionStart = Sentry.captureRouterTransitionStart;

packages/db/src/queries/teams.ts

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,10 +152,40 @@ async function createSystemCategoriesForTeam(
152152
}
153153

154154
export const createTeam = async (db: Database, params: CreateTeamParams) => {
155+
const startTime = Date.now();
156+
const teamCreationId = `team_creation_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
157+
158+
console.log(
159+
`[${teamCreationId}] Starting team creation for user ${params.userId}`,
160+
{
161+
teamName: params.name,
162+
baseCurrency: params.baseCurrency,
163+
countryCode: params.countryCode,
164+
email: params.email,
165+
switchTeam: params.switchTeam,
166+
timestamp: new Date().toISOString(),
167+
},
168+
);
169+
155170
// Use transaction to ensure atomicity and prevent race conditions
156171
return await db.transaction(async (tx) => {
157172
try {
173+
// Check if user already has teams to prevent duplicate creation
174+
const existingTeams = await tx
175+
.select({ id: teams.id, name: teams.name })
176+
.from(usersOnTeam)
177+
.innerJoin(teams, eq(teams.id, usersOnTeam.teamId))
178+
.where(eq(usersOnTeam.userId, params.userId));
179+
180+
console.log(
181+
`[${teamCreationId}] User existing teams count: ${existingTeams.length}`,
182+
{
183+
existingTeams: existingTeams.map((t) => ({ id: t.id, name: t.name })),
184+
},
185+
);
186+
158187
// Create the team
188+
console.log(`[${teamCreationId}] Creating team record`);
159189
const [newTeam] = await tx
160190
.insert(teams)
161191
.values({
@@ -171,28 +201,58 @@ export const createTeam = async (db: Database, params: CreateTeamParams) => {
171201
throw new Error("Failed to create team.");
172202
}
173203

204+
console.log(
205+
`[${teamCreationId}] Team created successfully with ID: ${newTeam.id}`,
206+
);
207+
174208
// Add user to team membership (atomic with team creation)
209+
console.log(`[${teamCreationId}] Adding user to team membership`);
175210
await tx.insert(usersOnTeam).values({
176211
userId: params.userId,
177212
teamId: newTeam.id,
178213
role: "owner",
179214
});
180215

181216
// Create system categories for the new team (atomic)
217+
console.log(`[${teamCreationId}] Creating system categories`);
182218
// @ts-expect-error - tx is a PgTransaction
183219
await createSystemCategoriesForTeam(tx, newTeam.id, params.countryCode);
184220

185221
// Optionally switch user to the new team (atomic)
186222
if (params.switchTeam) {
223+
console.log(`[${teamCreationId}] Switching user to new team`);
187224
await tx
188225
.update(users)
189226
.set({ teamId: newTeam.id })
190227
.where(eq(users.id, params.userId));
191228
}
192229

230+
const duration = Date.now() - startTime;
231+
console.log(
232+
`[${teamCreationId}] Team creation completed successfully in ${duration}ms`,
233+
{
234+
teamId: newTeam.id,
235+
duration,
236+
},
237+
);
238+
193239
return newTeam.id;
194240
} catch (error) {
195-
console.error("Team creation failed:", error);
241+
const duration = Date.now() - startTime;
242+
console.error(
243+
`[${teamCreationId}] Team creation failed after ${duration}ms:`,
244+
{
245+
error: error instanceof Error ? error.message : String(error),
246+
stack: error instanceof Error ? error.stack : undefined,
247+
params: {
248+
userId: params.userId,
249+
teamName: params.name,
250+
baseCurrency: params.baseCurrency,
251+
countryCode: params.countryCode,
252+
},
253+
duration,
254+
},
255+
);
196256

197257
// Re-throw with more specific error messages
198258
if (error instanceof Error) {

0 commit comments

Comments
 (0)