Conversation
| onSuccess: (_data, variables) => { | ||
| showToast(t("custom_reason_deleted"), "success"); | ||
|
|
||
| const currentReasonId = getValues("reasonId"); |
There was a problem hiding this comment.
This is imp cause when we delete, even after invalidating the list, if the deleted item was previously selected and we click on save anyway, it will throw foreign key error as the data would not be existing by then
| export const outOfOfficeReasonList = async ({ ctx }: GetOptions) => { | ||
| const outOfOfficeReasons = await prisma.outOfOfficeReason.findMany({ | ||
| where: { | ||
| enabled: true, |
There was a problem hiding this comment.
making sure if X has created custom reasons, it does not get showed in Y's list. Only defaults and the user created reasons
| model OutOfOfficeReason { | ||
| id Int @id @default(autoincrement()) | ||
| emoji String | ||
| reason String @unique |
There was a problem hiding this comment.
this was important so that reasons which are created by users as custom, is avl for other users to create too. So made the combo of userId and reason as unique. When userId is null, then only the reasons will be unique and this will only happen in the global reasons (like what we had previously)
| const existingSystemDefault = await prisma.outOfOfficeReason.findFirst({ | ||
| where: { | ||
| userId: null, | ||
| reason: input.reason, | ||
| }, | ||
| }); | ||
|
|
||
| if (existingSystemDefault) { | ||
| throw new TRPCError({ | ||
| code: "CONFLICT", | ||
| message: "This reason already exists as a system default", | ||
| }); | ||
| } |
There was a problem hiding this comment.
🟡 System default duplicate check is ineffective because it compares user text against translation keys
The duplicate-detection query at outOfOfficeCreateReason.handler.ts:29-34 searches for a system default with reason: input.reason, where input.reason is user-typed plain text (e.g., "Vacation"). However, system default reasons are stored in the database as i18n translation keys (e.g., "ooo_reasons_vacation"), as seen in the seed migration at packages/prisma/migrations/20240305054508_default_reasons_out_of_office/migration.sql.
Root Cause
The findFirst query compares the user's plain-text input directly against the DB reason column:
const existingSystemDefault = await prisma.outOfOfficeReason.findFirst({
where: {
userId: null,
reason: input.reason, // e.g. "Vacation"
},
});But the stored system defaults use translation keys:
INSERT INTO "OutOfOfficeReason" ... VALUES ('🏝️', 'ooo_reasons_vacation', true);
INSERT INTO "OutOfOfficeReason" ... VALUES ('🛫', 'ooo_reasons_travel', true);"Vacation" will never match "ooo_reasons_vacation", so this check is dead code. Users can freely create custom reasons that duplicate system defaults (e.g., a custom "Vacation" reason alongside the built-in one), which is the exact scenario this code was designed to prevent.
The unit test at outOfOfficeCreateReason.handler.test.ts:88-111 masks this by mocking the system default's reason field as "Vacation" instead of the real value "ooo_reasons_vacation".
Impact: The intended guard against duplicate system-default reasons never fires. Users see both a translated system "Vacation" and their custom "Vacation" in the reason dropdown.
Prompt for agents
In packages/trpc/server/routers/viewer/ooo/outOfOfficeCreateReason.handler.ts, the system default duplicate check at lines 29-41 compares user-typed plain text (e.g. "Vacation") against DB-stored translation keys (e.g. "ooo_reasons_vacation"). To fix this, either: (1) Fetch all system defaults (userId: null), translate their reason keys server-side, and compare the translated text against input.reason (case-insensitive). Or (2) Store a mapping of known system default display names and check input against that list. Also update the test at outOfOfficeCreateReason.handler.test.ts lines 88-111 to use real translation keys like "ooo_reasons_vacation" for the mocked system default's reason field.
Was this helpful? React with 👍 or 👎 to provide feedback.
|
@bandhan-majumder can you address the Devin comment? |
What does this PR do?
Visual Demo (For contributors especially)
A visual demonstration is strongly recommended, for both the original and new change (video / image - any one).
Video Demo (if applicable):
Screencast.From.2026-02-14.01-47-26.mp4
Image Demo (if applicable):
Mandatory Tasks (DO NOT REMOVE)
How should this be tested?
Checklist
Summary by cubic
Adds custom out-of-office reasons (emoji + short text) and displays them on the booking page for custom entries. Meets Linear CAL-7199 by letting users create, select, and manage personal reasons.
New Features
Migration
Written for commit 230c90e. Summary will update on new commits.