refactor: translate journey dialog#6742
Conversation
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the You can disable this status message by setting the WalkthroughThis update introduces comprehensive AI-powered journey translation and language detection features across the backend and frontend. It extends GraphQL schemas, adds Prisma model fields, and implements new mutations for translation and language detection. The frontend gains new dialogs, hooks, and UI controls for translating and copying journeys, with enhanced localization and prompt security mechanisms. Supporting utilities and tests are included throughout. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant AdminUI
participant Backend(GQL)
participant AIService
participant DB
User->>AdminUI: Click "Translate" on journey
AdminUI->>AdminUI: Open TranslateJourneyDialog
User->>AdminUI: Select language, confirm translation
AdminUI->>Backend(GQL): journeyAiTranslateCreate(input)
Backend(GQL)->>DB: Fetch journey and blocks
Backend(GQL)->>AIService: Send translation prompt (title, desc, blocks)
AIService-->>Backend(GQL): Return translated content
Backend(GQL)->>DB: Update journey and blocks with translations
Backend(GQL)-->>AdminUI: Return updated journey
AdminUI->>User: Show translated journey
sequenceDiagram
participant User
participant AdminUI
participant Backend(GQL)
participant AIService
participant DB
User->>AdminUI: Click "Copy to Team", enable translation
AdminUI->>AdminUI: Show CopyToTeamDialog with translation options
User->>AdminUI: Select team and language, confirm
AdminUI->>Backend(GQL): Duplicate journey
Backend(GQL)->>DB: Duplicate journey and blocks
Backend(GQL)-->>AdminUI: Return duplicated journey ID
AdminUI->>Backend(GQL): journeyAiTranslateCreate(input) (if translation enabled)
Backend(GQL)->>DB: Fetch duplicated journey and blocks
Backend(GQL)->>AIService: Send translation prompt
AIService-->>Backend(GQL): Return translated content
Backend(GQL)->>DB: Update duplicated journey and blocks
Backend(GQL)-->>AdminUI: Return translated journey
AdminUI->>User: Show confirmation and translated journey
Possibly related PRs
Suggested reviewers
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
|
The latest updates on your projects.
|
|
The latest updates on your projects.
|
|
The latest updates on your projects.
|
|
The latest updates on your projects.
|
|
The latest updates on your projects.
|
|
The latest updates on your projects.
|
|
The latest updates on your projects.
|
…es-244-refactor-translate-journey-dialog
There was a problem hiding this comment.
Caution
Inline review comments failed to post. This is likely due to GitHub's limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.
🛑 Comments failed to post (29)
libs/journeys/ui/src/components/SearchBar/SearchBar.spec.tsx (1)
140-140: 🛠️ Refactor suggestion
Test description and implementation mismatch
The test is titled "should have language icon" but is now checking for the SearchBar element itself rather than a language or globe icon. Consider updating the test name to match what it's actually testing.
- it('should have language icon', async () => { + it('should render SearchBar component', async () => {📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.it('should render SearchBar component', async () => { expect(screen.getByTestId('SearchBar')).toBeInTheDocument() })🤖 Prompt for AI Agents
In libs/journeys/ui/src/components/SearchBar/SearchBar.spec.tsx at line 140, the test name "should have language icon" does not match the assertion which checks for the presence of the SearchBar element. Update the test name to accurately describe that it is verifying the SearchBar element is in the document, or alternatively, modify the test to check for the language or globe icon if that was the original intent.apis/api-journeys-modern/src/schema/journeyAiTranslate/getCardBlocksContent/getButtonBlockContent.ts (1)
1-9: 💡 Verification agent
🧩 Analysis chain
Potential issue with accessing
block.labelpropertyThe function attempts to access
block.labelon line 7, but based on the provided code snippets, theBlocktype might not have alabelproperty defined. This could lead to accessing an undefined property.Consider adding type safety by checking if the label exists before using it:
export function getButtonBlockContent({ block }: { block: Block }): string { return ` ## Button: - Block ID: ${block.id} - - Button Label: ${block.label} + - Button Label: ${block.label ?? 'No label'} ` }
🏁 Script executed:
#!/bin/bash # Check if the Block type in Prisma schema has a label property echo "Checking if Block type has label property in Prisma schema..." fd --type f --extension prisma | xargs grep -l "model Block" | xargs cat # Also check TypeScript interfaces/types for Block echo "Checking TypeScript interfaces/types for Block..." fd --type f --extension ts --extension tsx | xargs grep -l "type Block" | xargs cat fd --type f --extension ts --extension tsx | xargs grep -l "interface Block" | xargs catLength of output: 108280
Handle optional
labelproperty onBlockThe
Blocktype generated by Prisma includeslabel?: string | null, so accessingblock.labelwill never be a compile-time error but may produce"null"at runtime if it’s not set. It’s best to provide a fallback:• File:
apis/api-journeys-modern/src/schema/journeyAiTranslate/getCardBlocksContent/getButtonBlockContent.tsSuggested change:
export function getButtonBlockContent({ block }: { block: Block }): string { - return ` -## Button: -- Block ID: ${block.id} -- Button Label: ${block.label} -` -} + const label = block.label ?? 'No label'; + return ` +## Button: +- Block ID: ${block.id} +- Button Label: ${label} +`; +}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.import { Block } from '.prisma/api-journeys-modern-client' export function getButtonBlockContent({ block }: { block: Block }): string { const label = block.label ?? 'No label'; return ` ## Button: - Block ID: ${block.id} - Button Label: ${label} `; }🤖 Prompt for AI Agents
In apis/api-journeys-modern/src/schema/journeyAiTranslate/getCardBlocksContent/getButtonBlockContent.ts around lines 1 to 9, the function accesses block.label which may be undefined or null according to the Block type. To fix this, update the code to safely handle the optional label property by providing a fallback value (e.g., an empty string or a placeholder) when block.label is null or undefined before including it in the returned string.apis/api-journeys-modern/src/schema/journeyAiTranslate/getCardBlocksContent/getRadioQuestionBlockContent.ts (1)
3-23: 🛠️ Refactor suggestion
Add null/undefined handling for block labels.
The function should handle cases where
block.labelorquestion.labelmight be null or undefined to prevent "undefined" from appearing in the output string.Consider adding null checks:
- - Text: ${block.label} + - Text: ${block.label ?? ''} - result += `- ${question.label}\n` + result += `- ${question.label ?? ''}\n`📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.export function getRadioQuestionBlockContent({ blocks, block }: { blocks: Block[] block: Block }): string { let result = ` ## Radio Question List: - Block ID: ${block.id} - Text: ${block.label ?? ''} ### Questions: ` const questions = blocks.filter( (childBlock) => childBlock.typename === 'RadioQuestionBlock' ) for (const question of questions) { result += `- ${question.label ?? ''}\n` } return result }🤖 Prompt for AI Agents
In apis/api-journeys-modern/src/schema/journeyAiTranslate/getCardBlocksContent/getRadioQuestionBlockContent.ts between lines 3 and 23, the function does not handle cases where block.label or question.label might be null or undefined, which can cause "undefined" to appear in the output string. Update the code to check if block.label and question.label are null or undefined and replace them with an empty string or a default placeholder before including them in the result string.apis/api-journeys-modern/src/schema/journeyAiTranslate/getCardBlocksContent/getTextResponseBlockContent.ts (1)
10-16: 🛠️ Refactor suggestion
Avoid type assertions for Block properties.
Using
(block as any)to access properties suggests a typing issue. Consider updating the Block type or creating a more specific interface.- - Input Placeholder Text: ${(block as any).placeholder ?? ''} - - Required: ${(block as any).required ?? false} + - Input Placeholder Text: ${block.placeholder ?? ''} + - Required: ${block.required ?? false}This requires updating the Block type definition or extending it:
interface TextResponseBlock extends Block { placeholder?: string required?: boolean }🤖 Prompt for AI Agents
In apis/api-journeys-modern/src/schema/journeyAiTranslate/getCardBlocksContent/getTextResponseBlockContent.ts around lines 10 to 16, avoid using type assertions like (block as any) to access placeholder and required properties. Instead, define a more specific interface extending Block (e.g., TextResponseBlock) that includes these optional properties, then update the function to use this interface for the block parameter to ensure proper typing without assertions.apps/journeys-admin/src/components/JourneyList/JourneyCard/JourneyCardMenu/TranslateJourneyDialog/TranslateJourneyDialog.spec.tsx (2)
45-46: 🛠️ Refactor suggestion
Expand test coverage beyond the TODO comment
As noted in the comment, additional tests are needed. Consider adding tests for:
- Language selection functionality
- The Create button and actual translation process
- Loading state during translation
- Error handling
These would provide more comprehensive coverage of the component's behavior.
it('should call duplicateAndTranslate when Create is clicked with a selected language', async () => { // Mock implementation const handleClose = jest.fn() const duplicateAndTranslateMock = jest.fn().mockResolvedValue(undefined) jest.mock('@core/journeys/ui/libs/useJourneyDuplicateAndTranslate', () => ({ useJourneyDuplicateAndTranslate: () => ({ duplicateAndTranslate: duplicateAndTranslateMock, loading: false }) })) render( <MockedProvider mocks={[getLanguagesMock]}> <JourneyProvider value={{ journey: defaultJourney }}> <TranslateJourneyDialog open={true} onClose={handleClose} /> </JourneyProvider> </MockedProvider> ) // Select a language (implementation depends on your component) // ... fireEvent.click(screen.getByText('Create')) expect(duplicateAndTranslateMock).toHaveBeenCalled() })🤖 Prompt for AI Agents
In apps/journeys-admin/src/components/JourneyList/JourneyCard/JourneyCardMenu/TranslateJourneyDialog/TranslateJourneyDialog.spec.tsx around lines 45 to 46, the test coverage is minimal with only a placeholder comment. Expand the tests by adding cases that verify language selection functionality, simulate clicking the Create button to ensure the duplicateAndTranslate function is called, test the loading state during translation, and handle error scenarios. Mock necessary hooks and functions, render the component with required providers, simulate user interactions like selecting a language and clicking buttons, and assert expected behaviors to provide comprehensive coverage.
10-11: 🛠️ Refactor suggestion
Ensure the Media Query mock is properly implemented
The file mocks
useMediaQuerybut doesn't provide an implementation. Consider adding a mock implementation to ensure consistent test behavior.jest.mock('@mui/material/useMediaQuery') +jest.mock('@mui/material/useMediaQuery', () => ({ + __esModule: true, + default: () => true // or a function that returns the desired value +}))📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.jest.mock('@mui/material/useMediaQuery') jest.mock('@mui/material/useMediaQuery', () => ({ __esModule: true, default: () => true // or a function that returns the desired value }))🤖 Prompt for AI Agents
In apps/journeys-admin/src/components/JourneyList/JourneyCard/JourneyCardMenu/TranslateJourneyDialog/TranslateJourneyDialog.spec.tsx at lines 10 to 11, the jest.mock call for '@mui/material/useMediaQuery' lacks a mock implementation. Add a mock implementation for useMediaQuery that returns a consistent boolean value to ensure predictable and stable test behavior across different environments.apis/api-journeys-modern/src/schema/journeyAiTranslate/getCardBlocksContent/getVideoBlockContent.ts (2)
17-26: 🛠️ Refactor suggestion
Add null check for empty src strings
The current null check for
videoCoverBlock.srcmight not handle empty strings correctly. Ifsrcis an empty string, it will pass the null check but could cause issues with the image description service.- if (videoCoverBlock && videoCoverBlock.src != null) { + if (videoCoverBlock && videoCoverBlock.src != null && videoCoverBlock.src !== '') {🤖 Prompt for AI Agents
In apis/api-journeys-modern/src/schema/journeyAiTranslate/getCardBlocksContent/getVideoBlockContent.ts around lines 17 to 26, the check for videoCoverBlock.src only verifies if it is not null but does not handle empty strings. Update the condition to also check that videoCoverBlock.src is not an empty string before calling getImageDescription, ensuring the image description service is not called with invalid input.
14-16: 💡 Verification agent
🧩 Analysis chain
Verify Block type has posterBlockId property
The code looks for a
posterBlockIdproperty on the Block type, but this property isn't visible in the provided Block type definition from the relevant snippets. Ensure this property exists in the Block type to avoid runtime errors.
🏁 Script executed:
#!/bin/bash # Check if posterBlockId exists in the Block type definition # Search for Block type definition with posterBlockId echo "Searching for posterBlockId in Block type definition..." rg -A 15 "type Block" --type=ts # Search for posterBlockId in Prisma schema echo "Searching for posterBlockId in Prisma schema..." fd "schema.prisma" -x cat {} | grep -A 20 "model Block"Length of output: 65190
Replace
block.posterBlockIdwith the existingblock.coverBlockIdThe Block model (Prisma and generated GraphQL types) defines
coverBlockId, notposterBlockId. Referencing a non-existentposterBlockIdwill lead toundefinedat runtime.• File: apis/api-journeys-modern/src/schema/journeyAiTranslate/getCardBlocksContent/getVideoBlockContent.ts
Lines 14–16Suggested change:
- const videoCoverBlock = blocks.find( - (childBlock) => childBlock.id === block.posterBlockId - ) + const videoCoverBlock = blocks.find( + (childBlock) => childBlock.id === block.coverBlockId + )📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.const videoCoverBlock = blocks.find( (childBlock) => childBlock.id === block.coverBlockId )🤖 Prompt for AI Agents
In apis/api-journeys-modern/src/schema/journeyAiTranslate/getCardBlocksContent/getVideoBlockContent.ts around lines 14 to 16, the code incorrectly references block.posterBlockId which does not exist on the Block type. Replace block.posterBlockId with block.coverBlockId to match the actual property defined in the Block model and avoid runtime errors.apis/api-journeys-modern/src/schema/builder.ts (2)
119-119:
⚠️ Potential issueMissing JourneyStatus scalar registration
While you've added
JourneyStatusas a scalar type, there's no correspondingaddScalarTypecall for it like there is for the other scalar types. Either add a registration call or remove the scalar definition if it's intended to be an enum.#!/bin/bash # Verify if JourneyStatus is intended to be a scalar or enum echo "Checking for JourneyStatus usage in schema files..." rg "JourneyStatus" --type=graphql echo "Checking how JourneyStatus is used in code..." rg "JourneyStatus" --type=ts --glob=!**/__generated__/**🤖 Prompt for AI Agents
In apis/api-journeys-modern/src/schema/builder.ts at line 119, the JourneyStatus scalar type is missing registration with addScalarType like other scalars such as DateTime. Verify if JourneyStatus is intended as a scalar or enum; if scalar, add a builder.addScalarType('JourneyStatus', JourneyStatusResolver) call to register it properly. If it is an enum, remove the scalar definition to avoid confusion and ensure consistent schema usage.
67-70:
⚠️ Potential issueInconsistency between JourneyStatus definitions
There's an inconsistency in how
JourneyStatusis defined. Inbuilder.tsit's defined as a scalar type, but injourney.tsit's defined as an enum type. This could lead to schema inconsistencies or runtime errors.Scalars: { Date: { Input: Date; Output: Date } DateTimeISO: { Input: Date; Output: Date } DateTime: { Input: Date; Output: Date } ID: { Input: string; Output: number | string } - JourneyStatus: { Input: string; Output: string } }Consider removing the scalar definition since you're already defining this as an enum type in
journey.ts.📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.Scalars: { Date: { Input: Date; Output: Date } DateTimeISO: { Input: Date; Output: Date } DateTime: { Input: Date; Output: Date } ID: { Input: string; Output: number | string } }🤖 Prompt for AI Agents
In apis/api-journeys-modern/src/schema/builder.ts around lines 67 to 70, the JourneyStatus is defined as a scalar type, which conflicts with its enum definition in journey.ts. To fix this, remove the JourneyStatus scalar type definition from builder.ts to avoid schema inconsistencies and rely solely on the enum type defined in journey.ts.apis/api-journeys-modern/src/schema/journeyAiTranslate/getCardBlocksContent/getCardBlocksContent.ts (1)
39-41: 💡 Verification agent
🧩 Analysis chain
Investigate redundant check
The condition
block.id === cardBlock.idseems unnecessary sincecardBlocksChildren[i]should only contain children of the card, not the card itself. This could indicate a misunderstanding of the data structure or a defensive check for unexpected data.
🏁 Script executed:
#!/bin/bash # Check if cards can appear as their own children echo "Checking for potential circular references in the block structure..." fd "schema.prisma" -x cat {} | grep -A 30 "model Block" echo "Checking for cases where a card might reference itself..." rg "parentBlockId.*id" --type=ts --glob=!**/__generated__/**Length of output: 99291
🏁 Script executed:
#!/bin/bash # Display the contents of getCardBlocksContent.ts for review sed -n '1,200p' apis/api-journeys-modern/src/schema/journeyAiTranslate/getCardBlocksContent/getCardBlocksContent.tsLength of output: 1447
Remove redundant self‐reference check
The
block.id === cardBlock.idguard is unnecessary becausecardBlocksChildrenis built by filteringblocksonparentBlockId === cardBlock.id. A block cannot appear in its own children list unless the data is malformed. You can safely remove these lines.• File: apis/api-journeys-modern/src/schema/journeyAiTranslate/getCardBlocksContent/getCardBlocksContent.ts
• Remove at lines 39–41Diff:
for (let j = 0; j < orderedBlocks.length; j++) { const block = orderedBlocks[j] - if (block.id === cardBlock.id) { - continue - } cardResult += await getBlockContent({ blocks, block }) }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.for (let j = 0; j < orderedBlocks.length; j++) { const block = orderedBlocks[j] cardResult += await getBlockContent({ blocks, block }) }🤖 Prompt for AI Agents
In apis/api-journeys-modern/src/schema/journeyAiTranslate/getCardBlocksContent/getCardBlocksContent.ts at lines 39 to 41, remove the redundant check for block.id === cardBlock.id since cardBlocksChildren is already filtered to only include children of cardBlock, making this self-reference check unnecessary. Simply delete the if condition and continue statement to clean up the code.apis/api-journeys-modern/src/schema/journey/journey.ts (1)
39-47: 🛠️ Refactor suggestion
Add error handling to reference resolver
The
resolveReferencefunction doesn't handle the case where a journey with the given ID doesn't exist. Consider adding error handling or returning null explicitly.resolveReference: async (ref) => { - return prisma.journey.findUnique({ + const journey = await prisma.journey.findUnique({ where: { id: ref.id } }) + if (!journey) { + console.warn(`Journey with ID ${ref.id} not found during reference resolution`) + } + return journey }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.// Register as a federated entity builder.asEntity(JourneyRef, { key: builder.selection<{ id: string }>('id'), resolveReference: async (ref) => { const journey = await prisma.journey.findUnique({ where: { id: ref.id } }) if (!journey) { console.warn(`Journey with ID ${ref.id} not found during reference resolution`) } return journey } })🤖 Prompt for AI Agents
In apis/api-journeys-modern/src/schema/journey/journey.ts around lines 39 to 47, the resolveReference function lacks error handling for cases when no journey is found for the given ID. Modify the function to explicitly check if the result of prisma.journey.findUnique is null or undefined, and handle this case by either returning null or throwing a descriptive error. This ensures the resolver behaves predictably when the referenced journey does not exist.apis/api-journeys-modern/src/schema/journeyAiTranslate/getLanguageName.ts (1)
24-27: 💡 Verification agent
🧩 Analysis chain
Avoid hard-coding the “English” locale – make it configurable
'529'is apparently the language-id for English, but by hard-coding it you force all callers to fetch the English translation of the language name.
If tomorrow we need the UI to run in another locale (e.g. Spanish) the code must be patched and redeployed.-export async function getLanguageName( - apollo: ApolloClient<NormalizedCacheObject>, - languageId: string, - logger?: Logger -): Promise<string | null> { +// `localizedToLanguageId` defaults to English but can be overridden. +export async function getLanguageName( + apollo: ApolloClient<NormalizedCacheObject>, + languageId: string, + logger?: Logger, + localizedToLanguageId = '529' +): Promise<string | null> { ... - languageId: '529' + languageId: localizedToLanguageId ... }This keeps the current behaviour (callers can omit the 4th arg) while unlocking multi-locale usage.
Don’t forget to update the call-sites.
I’d like to confirm where and how many places
getLanguageNameis invoked before finalizing the recommendation. Let’s search for the hard-coded locale and all call-sites:
🏁 Script executed:
#!/bin/bash set -e # Show the hard-coded '529' in the function rg -n "languageId: '529'" -C2 apis/api-journeys-modern/src/schema/journeyAiTranslate/getLanguageName.ts # Find every place getLanguageName(...) is called rg -n "getLanguageName\s*\(" -C1 .Length of output: 580
Avoid hard-coding English locale – make it configurable
The hard-coded
'529'forces every caller ofgetLanguageNameto fetch English translations only. To support other locales without redeploying this function, add a fourth parameter with a default of'529':--- a/apis/api-journeys-modern/src/schema/journeyAiTranslate/getLanguageName.ts +++ b/apis/api-journeys-modern/src/schema/journeyAiTranslate/getLanguageName.ts @@ -export async function getLanguageName( - apollo: ApolloClient<NormalizedCacheObject>, - languageId: string, - logger?: Logger -): Promise<string | null> { +// `localizedToLanguageId` defaults to English but can be overridden for other locales. +export async function getLanguageName( + apollo: ApolloClient<NormalizedCacheObject>, + languageId: string, + logger?: Logger, + localizedToLanguageId = '529' +): Promise<string | null> { const result = await apollo.query<{ language: { name: string } }>({ @@ variables: { id: languageId, - languageId: '529' + languageId: localizedToLanguageId } })• No existing call-sites are found in this repo, so you can merge this change now.
• When you introduce real calls togetLanguageName(e.g., from UI code), pass a differentlocalizedToLanguageIdto fetch names in other languages.🤖 Prompt for AI Agents
In apis/api-journeys-modern/src/schema/journeyAiTranslate/getLanguageName.ts around lines 24 to 27, the languageId '529' is hard-coded to fetch English translations only. Modify the function to accept a fourth parameter for the locale languageId with a default value of '529'. Replace the hard-coded '529' in the variables object with this parameter. Update all call-sites to optionally pass this parameter to support fetching language names in different locales without redeploying.libs/journeys/ui/src/libs/useJourneyAiTranslateMutation/useJourneyAiTranslateMutation.ts (1)
44-70: 🛠️ Refactor suggestion
options.updatesilently overrides the built-in cache updateBecause the spread operator comes after the custom
update, any caller-suppliedupdateprop will completely replace the cache write-back logic that appends the translated journey. This is easy to miss and will lead to missing items in the UI.- >(JOURNEY_AI_TRANSLATE_CREATE, { - update(cache, { data }) { - … - }, - ...options - }) + >(JOURNEY_AI_TRANSLATE_CREATE, { + ...options, + update(cache, result) { + // 1️⃣ first run the built-in update + if (result.data?.journeyAiTranslateCreate != null) { + cache.modify({ … }) + } + // 2️⃣ then allow the caller to extend/override behaviour + options?.update?.(cache, result) + } + })This preserves the default behaviour while still giving callers full control.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.>(JOURNEY_AI_TRANSLATE_CREATE, { ...options, update(cache, result) { // 1️⃣ first run the built-in update if (result.data?.journeyAiTranslateCreate != null) { cache.modify({ fields: { adminJourneys(existingAdminJourneyRefs = []) { const translatedJourneyRef = cache.writeFragment({ data: result.data.journeyAiTranslateCreate, fragment: gql` fragment TranslatedJourney on Journey { id title description languageId createdAt updatedAt } ` }) return [...existingAdminJourneyRefs, translatedJourneyRef] } } }) } // 2️⃣ then allow the caller to extend/override behaviour options?.update?.(cache, result) } })🤖 Prompt for AI Agents
In libs/journeys/ui/src/libs/useJourneyAiTranslateMutation/useJourneyAiTranslateMutation.ts between lines 44 and 70, the current code spreads the caller-supplied options after defining a custom update function, causing any provided update function to override the built-in cache update logic. To fix this, merge the caller's update function with the built-in update by calling the built-in update first and then the caller's update within a new combined update function, ensuring the default cache modification always runs while still allowing callers to customize the update behavior.apis/api-journeys-modern/src/schema/journeyLanguageAiDetect/journeyLanguageAiDetect.ts (2)
22-40:
⚠️ Potential issueEnforce access control – currently any authenticated user can probe any journey
withAuth({ isAuthenticated: true })only checks login status.
A user who is not on the journey’s team can still call this mutation and leak up to 5 block contents to the AI vendor.Add an ability/ACL check similar to
journeyAiTranslateCreate, e.g.const ability = await abilityForUser(user) if (!ability.can('read', journey)) { throw new ForbiddenError('You do not have access to this journey.') }Failing early also avoids sending private content to the AI provider.
🤖 Prompt for AI Agents
In apis/api-journeys-modern/src/schema/journeyLanguageAiDetect/journeyLanguageAiDetect.ts around lines 22 to 40, the code lacks an access control check to verify if the authenticated user has permission to read the journey, allowing unauthorized users to access sensitive block content. To fix this, after fetching the journey, obtain the user's ability object using abilityForUser(user) and check if the user can read the journey with ability.can('read', journey). If the check fails, throw a ForbiddenError to prevent unauthorized access and avoid sending private content to the AI provider.
12-18: 🛠️ Refactor suggestion
Remove unused
namefield from the mutation input
input.nameis never referenced in the resolver. Keeping unused required fields:• inflates the client payload
• breaks backward compatibility if we later drop it
• misleads consumers about its purpose- name: t.input.string({ required: true }),If the value will be needed in future, keep it but mark as optional for now.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.input: { journeyId: t.input.id({ required: true }), journeyLanguageName: t.input.string({ required: true }), textLanguageId: t.input.id({ required: true }), textLanguageName: t.input.string({ required: true }) },🤖 Prompt for AI Agents
In apis/api-journeys-modern/src/schema/journeyLanguageAiDetect/journeyLanguageAiDetect.ts between lines 12 and 18, the input field 'name' is defined as required but is never used in the resolver. To fix this, remove the 'name' field entirely from the input object if it is not needed, or alternatively, change it to be optional by removing the 'required: true' flag if it might be used in the future. This will prevent unnecessary payload inflation and avoid misleading API consumers.apps/journeys-admin/src/components/JourneyList/JourneyCard/JourneyCardMenu/DuplicateJourneyMenuItem/DuplicateJourneyMenuItem.spec.tsx (1)
292-330: 🛠️ Refactor suggestion
Simplify dialog button selection in tests
The current approach to finding and clicking the right button in the dialog is complex with multiple fallback strategies, suggesting potential unpredictability in the component under test.
Consider adding a specific data-testid to the primary action button in the CopyToTeamDialog component to make testing more straightforward:
// In the CopyToTeamDialog component: <Button variant="contained" onClick={handleCopy} + data-testid="copy-to-team-action-button" > {t('Copy')} </Button> // Then in the test: const copyButton = within(getByTestId('CopyToTeamDialog')).getByTestId('copy-to-team-action-button') fireEvent.click(copyButton)This would replace the entire complex button finding logic.
Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In apps/journeys-admin/src/components/JourneyList/JourneyCard/JourneyCardMenu/DuplicateJourneyMenuItem/DuplicateJourneyMenuItem.spec.tsx around lines 292 to 330, the test uses a complex multi-step approach to find and click the primary action button in the CopyToTeamDialog, which is fragile and hard to maintain. To fix this, update the CopyToTeamDialog component to add a unique data-testid attribute to the primary action button, then simplify the test by directly querying and clicking that button using this data-testid, removing all fallback logic and console logs.apps/journeys-admin/src/components/JourneyList/JourneyCard/JourneyCardMenu/TranslateJourneyDialog/TranslateJourneyDialog.tsx (1)
93-106: 🛠️ Refactor suggestion
Add error handling to translation process
The
handleTranslatefunction doesn't handle errors that might occur during the translation process, potentially leaving users without feedback if something goes wrong.Add error handling to provide feedback to users:
const handleTranslate = async (): Promise<void> => { if ( selectedLanguage == null || journeyData == null || activeTeam?.id == null ) return + try { await duplicateAndTranslate({ teamId: activeTeam.id, selectedLanguage, shouldTranslate: true }) + } catch (error) { + // Consider adding a notification or alert mechanism + console.error('Translation failed:', error) + } }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.const handleTranslate = async (): Promise<void> => { if ( selectedLanguage == null || journeyData == null || activeTeam?.id == null ) return try { await duplicateAndTranslate({ teamId: activeTeam.id, selectedLanguage, shouldTranslate: true }) } catch (error) { // Consider adding a notification or alert mechanism console.error('Translation failed:', error) } }🤖 Prompt for AI Agents
In apps/journeys-admin/src/components/JourneyList/JourneyCard/JourneyCardMenu/TranslateJourneyDialog/TranslateJourneyDialog.tsx around lines 93 to 106, the handleTranslate function lacks error handling for the asynchronous duplicateAndTranslate call. Wrap the await duplicateAndTranslate call in a try-catch block to catch any errors, and in the catch block, provide user feedback such as displaying an error message or notification to inform the user that the translation process failed.apis/api-journeys-modern/src/schema/journeyEventsExportLog/journeyEventsExportLog.spec.ts (1)
104-104: 🛠️ Refactor suggestion
Avoid banned
{}generic – use generated default args insteadThe static-analysis hint is correct:
Prisma.JourneyGetPayload<{}>widens the type to “any non-nullish value”.
Prefer the Prisma-generated default type so IntelliSense and compile-time checks still work:-type Journey = Prisma.JourneyGetPayload<{}> +type Journey = Prisma.JourneyGetPayload<Prisma.JourneyDefaultArgs>This keeps the payload definition precise and removes the lint error.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.-type Journey = Prisma.JourneyGetPayload<{}> +type Journey = Prisma.JourneyGetPayload<Prisma.JourneyDefaultArgs>🧰 Tools
🪛 Biome (1.9.4)
[error] 104-104: Don't use '{}' as a type.
Prefer explicitly define the object shape. '{}' means "any non-nullable value".
(lint/complexity/noBannedTypes)
🤖 Prompt for AI Agents
In apis/api-journeys-modern/src/schema/journeyEventsExportLog/journeyEventsExportLog.spec.ts at line 104, replace the use of the banned empty object generic `{}` in `Prisma.JourneyGetPayload<{}>` with the Prisma-generated default argument type by omitting the generic parameter entirely. This will ensure the type remains precise, enable proper IntelliSense and compile-time checks, and resolve the lint error.apps/journeys-admin/src/components/Team/CopyToTeamMenuItem/CopyToTeamMenuItem.tsx (1)
53-65:
⚠️ Potential issueEarly-return guard erroneously blocks duplicated journeys when
idprop is omitted
handleDuplicateJourneyexits ifidisnull, yetidis unrelated to duplication (the mutation already knows the journey viajourneyData.id).
When this component is used without the optionalidprop, duplication silently fails.- if (id == null || journeyData == null) return + if (journeyData == null) returnYou can also remove
idfromCopyToTeamMenuItemPropsentirely if it’s no longer used elsewhere.📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.const handleDuplicateJourney = async ( teamId: string, selectedLanguage?: JourneyLanguage, showTranslation?: boolean ): Promise<void> => { if (journeyData == null) return await duplicateAndTranslate({ teamId, selectedLanguage, shouldTranslate: showTranslation }) }🤖 Prompt for AI Agents
In apps/journeys-admin/src/components/Team/CopyToTeamMenuItem/CopyToTeamMenuItem.tsx around lines 53 to 65, the early return in handleDuplicateJourney incorrectly checks if id is null and blocks duplication, but id is not needed for duplication since journeyData.id is used. Remove the id null check from the guard to allow duplication to proceed even if id is omitted. Also, remove id from the CopyToTeamMenuItemProps if it is not used elsewhere in the component.libs/journeys/ui/src/libs/useJourneyDuplicateAndTranslate/useJourneyDuplicateAndTranslate.ts (1)
52-72:
⚠️ Potential issueAdd defensive
try/catchto guaranteeloadingis cleared and users are notified on network/APOLLO errors
await journeyDuplicatewill throw on network, GraphQL, or auth errors.
Because the call isn’t wrapped in atry/catch, the promise rejection will bubble up, the hook will exit early,loadingwill remaintrue, and the user will never see feedback.- setLoading(true) - const { data: duplicateData } = await journeyDuplicate({ - variables: { id: journeyId, teamId } - }) - - if (!duplicateData?.journeyDuplicate?.id) { - setLoading(false) - enqueueSnackbar(t('Journey duplication failed'), { - variant: 'error', - preventDuplicate: true - }) - onError?.() - return - } + setLoading(true) + let duplicateData + try { + ;({ data: duplicateData } = await journeyDuplicate({ + variables: { id: journeyId, teamId } + })) + if (!duplicateData?.journeyDuplicate?.id) { + throw new Error('Journey duplication failed') + } + } catch (err: unknown) { + enqueueSnackbar( + err instanceof Error ? err.message : t('Journey duplication failed'), + { variant: 'error', preventDuplicate: true } + ) + onError?.() + setLoading(false) + return + }This also removes the duplication of
setLoading(false)and keeps the UX consistent in every failure path.📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.const duplicateAndTranslate = async ({ teamId, selectedLanguage, shouldTranslate }: DuplicateAndTranslateProps): Promise<string | undefined> => { if (journeyId == null) return setLoading(true) let duplicateData try { ;({ data: duplicateData } = await journeyDuplicate({ variables: { id: journeyId, teamId } })) if (!duplicateData?.journeyDuplicate?.id) { throw new Error('Journey duplication failed') } } catch (err: unknown) { enqueueSnackbar( err instanceof Error ? err.message : t('Journey duplication failed'), { variant: 'error', preventDuplicate: true } ) onError?.() setLoading(false) return } // …rest of your successful‐duplication/translatation flow…🤖 Prompt for AI Agents
In libs/journeys/ui/src/libs/useJourneyDuplicateAndTranslate/useJourneyDuplicateAndTranslate.ts around lines 52 to 72, the async call to journeyDuplicate is not wrapped in a try/catch block, causing loading state to remain true and no user feedback on errors like network or GraphQL failures. Wrap the entire duplicateAndTranslate function logic inside a try block, move setLoading(true) before the try, and in a finally block call setLoading(false) to ensure loading is cleared. In the catch block, enqueue an error snackbar and call onError if provided to notify users of the failure.apis/api-journeys-modern/src/schema/journey/journey.acl.spec.ts (1)
185-191:
⚠️ Potential issueTest expectation contradicts its description – probable copy-paste error
The test is named “denies when user is publisher but has no userTeam or userJourneys” yet it asserts
toBe(true):expect(can(Action.Manage, templateJourney, publisherUser)).toBe(true)If the intended behaviour is to deny, the expectation should be
false; if the behaviour is to allow, rename the test to avoid confusion.- expect(can(Action.Manage, templateJourney, publisherUser)).toBe(true) + expect(can(Action.Manage, templateJourney, publisherUser)).toBe(false)A mismatch between the test name and assertion makes future maintenance error-prone and may hide real regressions.
🤖 Prompt for AI Agents
In apis/api-journeys-modern/src/schema/journey/journey.acl.spec.ts around lines 185 to 191, the test name states it should deny access when the user is a publisher without userTeam or userJourneys, but the assertion expects true. To fix this, either change the assertion to expect false to match the deny behavior described, or rename the test to reflect that access is allowed if the current behavior is correct. Ensure the test name and expectation are consistent to prevent confusion.libs/journeys/ui/src/components/CopyToTeamDialog/CopyToTeamDialog.tsx (1)
71-90: 🛠️ Refactor suggestion
handleSubmitcontinues optimistic UI updates even whensubmitActionfails
submitActioncan throw (e.g. network error during copy/translate).
The current implementation updates the active team, pushesUPDATE_LAST_ACTIVE_TEAM_ID, resets the form and closes the dialog regardless of success.Wrap the call in
try/catchand short-circuit follow-up mutations on failure:- async function handleSubmit( + async function handleSubmit( values: FormValues, { resetForm }: FormikHelpers<FormValues> ): Promise<void> { - await submitAction( + try { + await submitAction( values.teamSelect, values.languageSelect, values.showTranslation ) - - setActiveTeam(teams.find((team) => team.id === values.teamSelect) ?? null) - void updateLastActiveTeamId({ + setActiveTeam( + teams.find((team) => team.id === values.teamSelect) ?? null + ) + void updateLastActiveTeamId({ variables: { input: { lastActiveTeamId: values.teamSelect } }, onCompleted() { void client.refetchQueries({ include: ['GetAdminJourneys'] }) } - }, - onCompleted() { - void client.refetchQueries({ include: ['GetAdminJourneys'] }) - } - }) - resetForm() - onClose() + }) + resetForm() + onClose() + } catch (err) { + /* Snackbar already shown upstream */ + } }This prevents incorrect UI state (team switch & dialog close) when the underlying operation fails.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.async function handleSubmit( values: FormValues, { resetForm }: FormikHelpers<FormValues> ): Promise<void> { try { await submitAction( values.teamSelect, values.languageSelect, values.showTranslation ) setActiveTeam( teams.find((team) => team.id === values.teamSelect) ?? null ) void updateLastActiveTeamId({ variables: { input: { lastActiveTeamId: values.teamSelect } }, onCompleted() { void client.refetchQueries({ include: ['GetAdminJourneys'] }) } }) resetForm() onClose() } catch (err) { /* Snackbar already shown upstream */ } }🤖 Prompt for AI Agents
In libs/journeys/ui/src/components/CopyToTeamDialog/CopyToTeamDialog.tsx around lines 71 to 90, the handleSubmit function performs optimistic UI updates and subsequent mutations even if submitAction throws an error. To fix this, wrap the await submitAction call in a try/catch block and only proceed with updating the active team, calling updateLastActiveTeamId, resetting the form, and closing the dialog if submitAction succeeds. If an error occurs, catch it and prevent further UI updates to avoid inconsistent state.apis/api-journeys-modern/src/schema/journey/journey.acl.ts (2)
86-90: 🛠️ Refactor suggestion
⚠️ Potential issue
templateexistence check blocks all non-publisherManageactions
if (action === Action.Manage && 'template' in journey) { return false }The
'template' in journeyguard is always true because the field exists on everyJourney, even whentemplate === false.
As a result, non-publisher owners or team managers can never manage any journey.Replace the property-existence check with a value check:
-if (action === Action.Manage && 'template' in journey) { +if (action === Action.Manage && journey.template === true) {and let the switch-case below delegate to the
managehelper for non-template journeys.📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.// Non-publishers cannot manage templates if (action === Action.Manage && journey.template === true) { return false }🤖 Prompt for AI Agents
In apis/api-journeys-modern/src/schema/journey/journey.acl.ts around lines 86 to 90, the current check uses 'template' in journey which always returns true since the property exists on every Journey object, blocking all non-publisher Manage actions incorrectly. Replace this existence check with a value check that verifies if journey.template is truthy, so only template journeys are blocked for non-publishers. This allows the switch-case below to handle non-template journeys properly via the manage helper.
26-33:
⚠️ Potential issue
INCLUDE_JOURNEY_ACLuses a non-existent Prisma helper type
Prisma.BlockInclude<DefaultArgs>does not exist in the generated client (onlyPrisma.<Model>Include).
This will fail type-checking and IDE autocomplete.Consider exposing the concrete include type instead:
-export const INCLUDE_JOURNEY_ACL: Prisma.BlockInclude<DefaultArgs> = { +export const INCLUDE_JOURNEY_ACL: Prisma.JourneyInclude = {If the intention was to create a generic helper, move it to a locally-defined type instead of referencing an unknown Prisma helper.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.export const INCLUDE_JOURNEY_ACL: Prisma.JourneyInclude = { journey: { include: { team: { include: { userTeams: true } }, userJourneys: true } } }🤖 Prompt for AI Agents
In apis/api-journeys-modern/src/schema/journey/journey.acl.ts around lines 26 to 33, the type Prisma.BlockInclude<DefaultArgs> used for INCLUDE_JOURNEY_ACL does not exist in the Prisma client and causes type errors. Replace this with the concrete include type generated by Prisma for the Journey model, such as Prisma.JourneyInclude, to ensure proper type-checking and IDE support. If a generic helper type is needed, define it locally instead of referencing a non-existent Prisma helper type.apis/api-journeys-modern/src/schema/journeyAiTranslate/journeyAiTranslate.ts (2)
271-337: 🛠️ Refactor suggestion
Database hot-loop: thousands of single
updatecalls inside stream
await prisma.block.updateis executed once per partial translation chunk per block, potentially yielding hundreds of separate round-trips.Batch updates per card or collect them and run a single
prisma.$transactionfor better throughput:// collect const pending: Prisma.BlockUpdateArgs[] = [] ... pending.push({ where: { id: cleanBlockId, journeyId: input.journeyId }, data: sanitisedUpdates }) ... // after the stream finishes if (pending.length > 0) { await prisma.$transaction( pending.map((args) => prisma.block.update(args)) ) }This change will drastically reduce DB latency and cost.
Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In apis/api-journeys-modern/src/schema/journeyAiTranslate/journeyAiTranslate.ts between lines 271 and 337, the code performs an individual prisma.block.update call for each block inside the streaming loop, causing many separate database round-trips. To fix this, accumulate all update operations for the blocks in an array during the stream processing, then after the loop completes, execute a single prisma.$transaction call with all the update operations batched together. This will reduce database latency and improve throughput by minimizing the number of separate update calls.
217-247:
⚠️ Potential issueLLM output is applied without schema validation – high risk of over-updating blocks
Inside the streaming loop every
item.updatesobject is passed straight intoprisma.block.update, allowing the model to write any mutable field (e.g.,journeyId,parentBlockId, tracking fields, etc.).Mitigate by whitelisting the permissible keys per block type before persistence:
const allowedKeys: Record<string, string[]> = { TypographyBlock: ['content'], ButtonBlock: ['label'], RadioOptionBlock: ['label'], RadioQuestionBlock: ['label'], TextResponseBlock: ['label', 'placeholder'] } const sanitisedUpdates = Object.fromEntries( Object.entries(item.updates).filter(([key]) => allowedKeys[block.typename]?.includes(key) ) ) if (Object.keys(sanitisedUpdates).length > 0) { await prisma.block.update({ data: sanitisedUpdates, ... }) }Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In apis/api-journeys-modern/src/schema/journeyAiTranslate/journeyAiTranslate.ts between lines 217 and 247, the code applies LLM output updates directly to prisma.block.update without validating the fields, risking unintended overwrites of sensitive or immutable fields. To fix this, implement a whitelist of allowed fields to update for each block type (e.g., "content" for TypographyBlock, "label" for ButtonBlock, etc.) and filter the item.updates object accordingly before passing it to prisma.block.update, ensuring only these permitted fields are updated.apis/api-gateway/schema.graphql (2)
4322-4328: 🛠️ Refactor suggestion
Rename
MutationJourneyLanguageAiDetectInputfor consistencyTo mirror
JourneyAiTranslateInputand avoid the redundantMutationprefix, rename the input type:- input MutationJourneyLanguageAiDetectInput @join__type(graph: API_JOURNEYS_MODERN) { + input JourneyLanguageAiDetectInput @join__type(graph: API_JOURNEYS_MODERN) {And update the mutation signature:
- journeyLanguageAiDetect(input: MutationJourneyLanguageAiDetectInput!) : Boolean! @join__field(graph: API_JOURNEYS_MODERN) + journeyLanguageAiDetect(input: JourneyLanguageAiDetectInput!) : Boolean! @join__field(graph: API_JOURNEYS_MODERN)Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In apis/api-gateway/schema.graphql around lines 4322 to 4328, rename the input type MutationJourneyLanguageAiDetectInput to JourneyLanguageAiDetectInput to maintain naming consistency and remove the redundant Mutation prefix. Also, update any mutation definitions or references that use this input type to reflect the new name JourneyLanguageAiDetectInput.
539-563: 💡 Verification agent
🧩 Analysis chain
Mark
Journeyas an extension in modern graphThe modern journeys graph should extend the base
Journeytype, not redeclare it. Update the@join__typedirective to includeextension: true:- type Journey @join__type(graph: API_JOURNEYS, key: "id") @join__type(graph: API_JOURNEYS_MODERN, key: "id") { + type Journey @join__type(graph: API_JOURNEYS, key: "id") @join__type(graph: API_JOURNEYS_MODERN, key: "id", extension: true) {Also, if the modern graph provides or requires additional fields on
Journey, add corresponding@join__field(graph: API_JOURNEYS_MODERN)directives per field.
🏁 Script executed:
#!/bin/bash set -e # List all @join__type directives in the schema rg "@join__type" -n apis/api-gateway/schema.graphql # Check if any @join__type already uses extension: true rg "extension: true" -n apis/api-gateway/schema.graphqlLength of output: 29705
Mark
Journeyas an extension in the modern graphThe
Journeytype in the modern subgraph must be marked as an extension, just like other shared types (e.g.,Video,Language).• File:
apis/api-gateway/schema.graphql
• Line: ~539- type Journey @join__type(graph: API_JOURNEYS, key: "id") @join__type(graph: API_JOURNEYS_MODERN, key: "id") { + type Journey @join__type(graph: API_JOURNEYS, key: "id") @join__type(graph: API_JOURNEYS_MODERN, key: "id", extension: true) {If the modern graph introduces new fields on
Journey, ensure each is annotated with@join__field(graph: API_JOURNEYS_MODERN)🤖 Prompt for AI Agents
In apis/api-gateway/schema.graphql around lines 539 to 563, the Journey type is declared for both API_JOURNEYS and API_JOURNEYS_MODERN graphs without marking the modern graph declaration as an extension. Update the @join__type directive for API_JOURNEYS_MODERN to include extension: true to indicate it extends the base Journey type. Additionally, if the modern graph adds or requires any new fields on Journey, annotate those fields with @join__field(graph: API_JOURNEYS_MODERN).
…es-244-refactor-translate-journey-dialog
a50260f
into
feature/25-04-MA-feat-ai-translation
This PR refactors the
TranslateJourneyDialogto make use of theuseJourneyDuplicateAndTranslatecustom hook. It also prevents closing of the dialog when translating similar to theCopyToTeamDialog.Updated the custom hook to handle and return the loading state.
Summary by CodeRabbit
New Features
Improvements
Bug Fixes
Chores