Skip to content

feat: translate quick start#8875

Open
mikeallisonJS wants to merge 52 commits intomainfrom
26-00-MA-feat-translate-quick-start
Open

feat: translate quick start#8875
mikeallisonJS wants to merge 52 commits intomainfrom
26-00-MA-feat-translate-quick-start

Conversation

@mikeallisonJS
Copy link
Copy Markdown
Collaborator

@mikeallisonJS mikeallisonJS commented Mar 18, 2026

Summary by CodeRabbit

Release Notes

  • New Features
    • AI-powered translation for journey customization descriptions and fields during language duplication
    • Enhanced language selection interface with improved data loading
    • Translation progress dialog providing visual feedback during AI translation
    • Support for user language preferences as target language for translations
    • Improved quick start template with multi-step journey structure and customizable elements

Kneesal and others added 15 commits November 17, 2025 02:13
- Added .cursorignore to exclude environment files.
- Updated .gitignore to include new personal Claude rules and Strapi CMS config files.
- Modified .prettierignore to include Kubernetes manifests.
- Updated package.json with new dependencies and version upgrades.
- Added new rules and guidelines for backend, frontend, and infrastructure in .claude directory.
- Updated workflows to improve dependency installation and notifications.
- Adjusted TypeScript configuration for better module resolution.
…put structure

- Replaced instances of generateObject with generateText in translation logic.
- Adjusted mock implementations and test cases to reflect the new function usage.
- Updated return values to align with the new Output structure in the translation schema.
- Enhanced error handling in LanguageScreen for guest and signed-in users during journey duplication.
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 18, 2026

Walkthrough

This PR extends the journey AI translation pipeline to include customization field and description translation. It introduces a new translation module for customizing field values and descriptions, adds a GraphQL mutation for translating customization descriptions, updates the AI translate input schema to accept optional user language preferences, and integrates translation flows into multiple UI components for language selection and journey duplication.

Changes

Cohort / File(s) Summary
Translation Helpers
apis/api-journeys-modern/src/schema/journeyAiTranslate/translateCustomizationFields/translateCustomizationFields.ts, translateCustomizationFields/index.ts
New module implementing translateCustomizationFields, translateValue, and translateCustomizationDescription functions for batch-translating customization field values and descriptions using Gemini AI with Zod schema validation.
Core Translation Logic
apis/api-journeys-modern/src/schema/journeyAiTranslate/journeyAiTranslate.ts
Extended AI translate mutation/subscription to fetch and process journeyCustomizationFields, call translateCustomizationFields with journey analysis context, and persist translated field values and descriptions.
Customization Description Mutation
apis/api-journeys-modern/src/schema/journeyAiTranslate/journeyCustomizationDescriptionTranslate.mutation.ts
New standalone GraphQL mutation to translate journey customization descriptions and field default values with authorization checks and conditional processing.
GraphQL Schema Updates
apis/api-journeys-modern/schema.graphql, apis/api-gateway/schema.graphql
Added optional userLanguageId and userLanguageName to JourneyAiTranslateInput; introduced new JourneyCustomizationDescriptionTranslateInput input type and journeyCustomizationDescriptionTranslate mutation field.
Subscription & Cache Layer
libs/journeys/ui/src/libs/useJourneyAiTranslateSubscription/useJourneyAiTranslateSubscription.ts
Updated subscription variables to accept optional user language parameters and extended cache writes to include journeyCustomizationDescription and journeyCustomizationFields.
Language Selection UI
apps/journeys-admin/src/components/TemplateCustomization/MultiStepForm/Screens/LanguageScreen/LanguageScreen.tsx
Added AI translation flow with progress tracking, user language preference passing to subscription, best-effort customization description translation mutation, and TranslationDialogWrapper for progress display.
Journey Duplication UI
apps/journeys-admin/src/components/JourneyList/JourneyCard/JourneyCardMenu/TranslateJourneyDialog/TranslateJourneyDialog.tsx, apps/journeys-admin/src/components/Team/CopyToTeamMenuItem/CopyToTeamMenuItem.tsx, libs/journeys/ui/src/components/TemplateView/CreateJourneyButton/CreateJourneyButton.tsx
Updated to pass userLanguageId and userLanguageName alongside journey language data during translation subscription initiation.
Configuration & Localization
apps/journeys-admin/middleware.ts, libs/locales/en/journeys-ui.json
Added LOCALE_LANGUAGES mapping in middleware and new locale keys for translation UI ("Journey Translated", "Translate with AI", "Search Language", etc.).
Database Seeding
apis/api-journeys/db/seed.ts, apis/api-journeys/db/seeds/quickStartTemplate.ts
Updated seed invocation with reset parameter; expanded quick-start template with multi-step journey structure, customization field batching, and template variable placeholders.
Test Coverage
apis/api-journeys-modern/src/schema/journeyAiTranslate/journeyAiTranslate.spec.ts, translateCustomizationFields/translateCustomizationFields.spec.ts, journeyCustomizationDescriptionTranslate.mutation.spec.ts, apps/journeys-admin/src/components/.../...spec.tsx
Comprehensive test suites validating translation function behavior, mutation authorization/error handling, subscription progress, and UI component integration with mocked AI calls and GraphQL responses.
Module Exports
apis/api-journeys-modern/src/schema/journeyAiTranslate/index.ts
Added import of customization description translate mutation module for side-effect registration.

Sequence Diagram

sequenceDiagram
    participant User
    participant UI as LanguageScreen UI
    participant Apollo as Apollo Cache
    participant API as API GraphQL
    participant AIModel as AI Service (Gemini)
    participant DB as Database

    User->>UI: Select translation language
    UI->>API: Start JourneyAiTranslateCreateSubscription<br/>(with userLanguageId, userLanguageName)
    API->>DB: Fetch journey with customization fields
    API->>AIModel: translateCardBlocks(blocks, language)
    AIModel-->>API: Translated block updates
    API->>AIModel: translateCustomizationFields(fields, language)
    AIModel-->>API: Translated field values & description
    API->>DB: Update blocks with translations
    API->>DB: Update journeyCustomizationFields
    API->>DB: Update journeyCustomizationDescription
    API-->>UI: Progress: 100% (subscription update)
    UI->>Apollo: updateCacheWithTranslatedJourney()
    Apollo->>Apollo: Write journeyCustomizationDescription<br/>journeyCustomizationFields to cache
    UI->>User: Show translation complete, navigate
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • feat: translation mutation as subscription #6761: Parallels the subscription-based translation flow and extends the same AI translate subscription hook with customization field/description support, cache updates, and UI component integration across multiple duplication and language selection surfaces.
🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 40.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'feat: translate quick start' is vague and does not clearly convey the scope or main purpose of the substantial changeset involving customization field translation, AI translation infrastructure, language selection, and quick start template updates. Consider a more descriptive title such as 'feat: add AI translation support for journey customization fields and quick start template' or 'feat: implement customization field and description translation with language selection UI' to better reflect the comprehensive changes.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch 26-00-MA-feat-translate-quick-start

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@infracost
Copy link
Copy Markdown

infracost bot commented Mar 18, 2026

💰 Infracost report

Monthly estimate increased by $155 📈

Changed project Baseline cost Usage cost* Total change New monthly cost
infrastructure +$155 - +$155 (+10%) $1,770

*Usage costs can be estimated by updating Infracost Cloud settings, see docs for other options.

Estimate details (includes details of unsupported resources)
Key: * usage cost, ~ changed, + added, - removed

──────────────────────────────────
Project: infrastructure
Module path: infrastructure

~ module.prod.module.eks.aws_eks_node_group.az_2a_ondemand
  +$63 ($125 → $188)

    ~ Instance usage (Linux/UNIX, on-demand, t3.large)
      +$61 ($121 → $182)

    ~ Storage (general purpose SSD, gp2)
      +$2 ($4 → $6)

~ module.stage.module.eks.aws_eks_node_group.az_2a_ondemand
  +$63 ($125 → $188)

    ~ Instance usage (Linux/UNIX, on-demand, t3.large)
      +$61 ($121 → $182)

    ~ Storage (general purpose SSD, gp2)
      +$2 ($4 → $6)

~ module.prod.module.arclight.module.ecs-task.aws_ecs_service.ecs_service
  +$36 ($36 → $72)

    ~ Per GB per hour
      +$6 ($6 → $13)

    ~ Per vCPU per hour
      +$30 ($30 → $59)

+ module.prod.module.cms.module.ecs-task.aws_ecs_service.ecs_service
  +$36

    + Per GB per hour
      +$6

    + Per vCPU per hour
      +$30

+ module.stage.module.cms.module.ecs-task.aws_ecs_service.ecs_service
  +$36

    + Per GB per hour
      +$6

    + Per vCPU per hour
      +$30

~ module.stage.module.api-journeys.module.ecs-task.aws_ecs_service.ecs_service
  -$36 ($72 → $36)

    ~ Per GB per hour
      -$6 ($13 → $6)

    ~ Per vCPU per hour
      -$30 ($59 → $30)

~ module.stage.module.api-gateway-stage.module.ecs-task.aws_ecs_service.ecs_service
  -$43 ($85 → $43)

    ~ Per GB per hour
      -$13 ($26 → $13)

    ~ Per vCPU per hour
      -$30 ($59 → $30)

Monthly cost change for infrastructure (Module path: infrastructure)
Amount:  +$155 ($1,615 → $1,770)
Percent: +10%

──────────────────────────────────
Key: * usage cost, ~ changed, + added, - removed

*Usage costs can be estimated by updating Infracost Cloud settings, see docs for other options.

918 cloud resources were detected:
∙ 126 were estimated
∙ 790 were free
∙ 2 are not supported yet, see https://infracost.io/requested-resources:
  ∙ 1 x aws_s3_bucket_accelerate_configuration
  ∙ 1 x aws_s3_bucket_request_payment_configuration

Infracost estimate: Monthly estimate increased by $155 ↑
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━┳━━━━━━━━━━━━━━┓
┃ Changed project                                    ┃ Baseline cost ┃ Usage cost* ┃ Total change ┃
┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╋━━━━━━━━━━━━━━━╋━━━━━━━━━━━━━╋━━━━━━━━━━━━━━┫
┃ infrastructure                                     ┃         +$155 ┃           - ┃ +$155 (+10%) ┃
┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┻━━━━━━━━━━━━━━━┻━━━━━━━━━━━━━┻━━━━━━━━━━━━━━┛
This comment will be updated when code changes.

@jesus-film-bot
Copy link
Copy Markdown

Ran Plan for dir: infrastructure workspace: default

Show Output
Terraform used the selected providers to generate the following execution
plan. Resource actions are indicated with the following symbols:
~ update in-place
- destroy
+/- create replacement and then destroy

Terraform will perform the following actions:

  # module.prod.module.api-analytics.module.ecs-task.aws_ecs_service.ecs_service will be updated in-place
~ resource "aws_ecs_service" "ecs_service" {
        id                                 = "arn:aws:ecs:us-east-2:410965620680:service/jfp-ecs-cluster-prod/api-analytics-prod-service"
        name                               = "api-analytics-prod-service"
        tags                               = {}
      ~ task_definition                    = "arn:aws:ecs:us-east-2:410965620680:task-definition/jfp-api-analytics-prod:9" -> (known after apply)
        # (18 unchanged attributes hidden)

        # (4 unchanged blocks hidden)
    }

  # module.prod.module.api-analytics.module.ecs-task.aws_ecs_task_definition.ecs_task_definition must be replaced
+/- resource "aws_ecs_task_definition" "ecs_task_definition" {
      ~ arn                      = "arn:aws:ecs:us-east-2:410965620680:task-definition/jfp-api-analytics-prod:9" -> (known after apply)
      ~ arn_without_revision     = "arn:aws:ecs:us-east-2:410965620680:task-definition/jfp-api-analytics-prod" -> (known after apply)
      ~ container_definitions    = jsonencode(
          ~ [
              ~ {
                    name             = "jfp-api-analytics-prod-app"
                  ~ secrets          = [
                      + {
                          + name      = "DD_API_KEY"
                          + valueFrom = "arn:aws:ssm:us-east-2:410965620680:parameter/terraform/prd/DATADOG_API_KEY"
                        },
                        {
                            name      = "GATEWAY_HMAC_SECRET"
                            valueFrom = "arn:aws:ssm:us-east-2:410965620680:parameter/ecs/api-analytics/prod/GATEWAY_HMAC_SECRET"
                        },
                        # (1 unchanged element hidden)
                        {
                            name      = "PLAUSIBLE_SECRET_KEY_BASE"
                            valueFrom = "arn:aws:ssm:us-east-2:410965620680:parameter/ecs/api-analytics/prod/PLAUSIBLE_SECRET_KEY_BASE"
                        },
                      - {
                          - name      = "PRISMA_LOCATION_ANALYTICS"
                          - valueFrom = "arn:aws:ssm:us-east-2:410965620680:parameter/ecs/api-analytics/prod/PRISMA_LOCATION_ANALYTICS"
                        },
                      - {
                          - name      = "DD_API_KEY"
                          - valueFrom = "arn:aws:ssm:us-east-2:410965620680:parameter/terraform/prd/DATADOG_API_KEY"
                        },
                    ]
                  - systemControls   = []
                    # (9 unchanged attributes hidden)
                },
              ~ {
                  - cpu               = 0
                    name              = "jfp-api-analytics-prod-datadog-agent"
                  - systemControls    = []
                    # (9 unchanged attributes hidden)
                },
              ~ {
                  - cpu                   = 0
                    name                  = "jfp-api-analytics-prod-log-router"
                  - systemControls        = []
                    # (10 unchanged attributes hidden)
                },
            ] # forces replacement
        )
      ~ enable_fault_injection   = false -> (known after apply)
      ~ id                       = "jfp-api-analytics-prod" -> (known after apply)
      ~ revision                 = 9 -> (known after apply)
      - tags                     = {} -> null
      ~ tags_all                 = {} -> (known after apply)
        # (12 unchanged attributes hidden)
    }

  # module.prod.module.api-analytics.module.ecs-task.aws_ssm_parameter.parameters["PRISMA_LOCATION_ANALYTICS"] will be destroyed
  # (because key ["PRISMA_LOCATION_ANALYTICS"] is not in for_each map)
- resource "aws_ssm_parameter" "parameters" {
      - arn             = "arn:aws:ssm:us-east-2:410965620680:parameter/ecs/api-analytics/prod/PRISMA_LOCATION_ANALYTICS" -> null
      - data_type       = "text" -> null
      - id              = "/ecs/api-analytics/prod/PRISMA_LOCATION_ANALYTICS" -> null
      - key_id          = "alias/aws/ssm" -> null
      - name            = "/ecs/api-analytics/prod/PRISMA_LOCATION_ANALYTICS" -> null
      - overwrite       = true -> null
      - region          = "us-east-2" -> null
      - tags            = {
          - "name" = "PRISMA_LOCATION_ANALYTICS"
        } -> null
      - tags_all        = {
          - "name" = "PRISMA_LOCATION_ANALYTICS"
        } -> null
      - tier            = "Standard" -> null
      - type            = "SecureString" -> null
      - value           = (sensitive value) -> null
      - value_wo        = (write-only attribute) -> null
      - version         = 3 -> null
        # (2 unchanged attributes hidden)
    }

  # module.prod.module.api-media.module.ecs-task.aws_appautoscaling_target.service_autoscaling will be updated in-place
~ resource "aws_appautoscaling_target" "service_autoscaling" {
        id                 = "service/jfp-ecs-cluster-prod/api-media-prod-service"
      ~ min_capacity       = 2 -> 1
        tags               = {}
        # (8 unchanged attributes hidden)

        # (1 unchanged block hidden)
    }

  # module.prod.module.api-media.module.ecs-task.aws_ecs_service.ecs_service will be updated in-place
~ resource "aws_ecs_service" "ecs_service" {
      ~ desired_count                      = 2 -> 1
        id                                 = "arn:aws:ecs:us-east-2:410965620680:service/jfp-ecs-cluster-prod/api-media-prod-service"
        name                               = "api-media-prod-service"
        tags                               = {}
        # (18 unchanged attributes hidden)

        # (5 unchanged blocks hidden)
    }

  # module.prod.module.arclight.module.ecs-task.aws_ecs_service.ecs_service will be updated in-place
~ resource "aws_ecs_service" "ecs_service" {
      ~ desired_count                      = 1 -> 2
        id                                 = "arn:aws:ecs:us-east-2:410965620680:service/jfp-ecs-cluster-prod/arclight-prod-service"
        name                               = "arclight-prod-service"
        tags                               = {}
        # (18 unchanged attributes hidden)

        # (4 unchanged blocks hidden)
    }

  # module.stage.module.api-users.module.ecs-task.aws_ecs_service.ecs_service will be updated in-place
~ resource "aws_ecs_service" "ecs_service" {
        id                                 = "arn:aws:ecs:us-east-2:410965620680:service/jfp-ecs-cluster-stage/api-users-stage-service"
        name                               = "api-users-stage-service"
        tags                               = {}
      ~ task_definition                    = "arn:aws:ecs:us-east-2:410965620680:task-definition/jfp-api-users-stage:44" -> (known after apply)
        # (18 unchanged attributes hidden)

        # (4 unchanged blocks hidden)
    }

  # module.stage.module.api-users.module.ecs-task.aws_ecs_task_definition.ecs_task_definition must be replaced
+/- resource "aws_ecs_task_definition" "ecs_task_definition" {
      ~ arn                      = "arn:aws:ecs:us-east-2:410965620680:task-definition/jfp-api-users-stage:44" -> (known after apply)
      ~ arn_without_revision     = "arn:aws:ecs:us-east-2:410965620680:task-definition/jfp-api-users-stage" -> (known after apply)
      ~ container_definitions    = jsonencode(
          ~ [
              ~ {
                    name             = "jfp-api-users-stage-app"
                  ~ secrets          = [
                        # (4 unchanged elements hidden)
                        {
                            name      = "GATEWAY_HMAC_SECRET"
                            valueFrom = "arn:aws:ssm:us-east-2:410965620680:parameter/ecs/api-users/stage/GATEWAY_HMAC_SECRET"
                        },
                      - {
                          - name      = "GATEWAY_URL"
                          - valueFrom = "arn:aws:ssm:us-east-2:410965620680:parameter/ecs/api-users/stage/GATEWAY_URL"
                        },
                        {
                            name      = "GOOGLE_APPLICATION_JSON"
                            valueFrom = "arn:aws:ssm:us-east-2:410965620680:parameter/ecs/api-users/stage/GOOGLE_APPLICATION_JSON"
                        },
                        # (8 unchanged elements hidden)
                    ]
                  - systemControls   = []
                    # (9 unchanged attributes hidden)
                },
              ~ {
                    name              = "jfp-api-users-stage-datadog-agent"
                  - systemControls    = []
                    # (9 unchanged attributes hidden)
                },
              ~ {
                    name                  = "jfp-api-users-stage-log-router"
                  - systemControls        = []
                    # (10 unchanged attributes hidden)
                },
            ] # forces replacement
        )
      ~ enable_fault_injection   = false -> (known after apply)
      ~ id                       = "jfp-api-users-stage" -> (known after apply)
      ~ revision                 = 44 -> (known after apply)
      - tags                     = {} -> null
      ~ tags_all                 = {} -> (known after apply)
        # (12 unchanged attributes hidden)
    }

  # module.stage.module.api-users.module.ecs-task.aws_ssm_parameter.parameters["GATEWAY_URL"] will be destroyed
  # (because key ["GATEWAY_URL"] is not in for_each map)
- resource "aws_ssm_parameter" "parameters" {
      - arn             = "arn:aws:ssm:us-east-2:410965620680:parameter/ecs/api-users/stage/GATEWAY_URL" -> null
      - data_type       = "text" -> null
      - has_value_wo    = false -> null
      - id              = "/ecs/api-users/stage/GATEWAY_URL" -> null
      - key_id          = "alias/aws/ssm" -> null
      - name            = "/ecs/api-users/stage/GATEWAY_URL" -> null
      - overwrite       = true -> null
      - region          = "us-east-2" -> null
      - tags            = {
          - "name" = "GATEWAY_URL"
        } -> null
      - tags_all        = {
          - "name" = "GATEWAY_URL"
        } -> null
      - tier            = "Standard" -> null
      - type            = "SecureString" -> null
      - value           = (sensitive value) -> null
      - value_wo        = (write-only attribute) -> null
      - version         = 3 -> null
        # (2 unchanged attributes hidden)
    }

Plan: 2 to add, 5 to change, 4 to destroy.
╷
│ Warning: Deprecated Resource
│ 
│   with module.datadog.datadog_integration_aws.sandbox,
│   on modules/aws/datadog/main.tf line 118, in resource "datadog_integration_aws" "sandbox":
│  118: resource "datadog_integration_aws" "sandbox" {
│ 
│ **This resource is deprecated - use the `datadog_integration_aws_account`
│ resource instead**:
│ https://registry.terraform.io/providers/DataDog/datadog/latest/docs/resources/integration_aws_account
╵
╷
│ Warning: Deprecated attribute
│ 
│   on .terraform/modules/datadog.datadog_log_forwarder/modules/log_forwarder/main.tf line 2, in locals:
│    2:   bucket_name = var.bucket_name != "" ? var.bucket_name : "datadog-forwarder-${data.aws_caller_identity.current.account_id}-${data.aws_region.current.name}"
│ 
│ The attribute "name" is deprecated. Refer to the provider documentation for
│ details.
│ 
│ (and 2 more similar warnings elsewhere)
╵
  • ▶️ To apply this plan, comment:
    atlantis apply -d infrastructure
  • 🚮 To delete this plan and lock, click here
  • 🔁 To plan this project again, comment:
    atlantis plan -d infrastructure

Plan: 2 to add, 5 to change, 4 to destroy.


  • ⏩ To apply all unapplied plans from this Pull Request, comment:
    atlantis apply
  • 🚮 To delete all plans and locks from this Pull Request, comment:
    atlantis unlock

… screens

- Added loading states to the Next and Done buttons in various screens (DoneScreen, LanguageScreen, LinksScreen, MediaScreen, SocialScreen, TextScreen) to enhance user experience during asynchronous operations.
- Updated button components to reflect loading states and prevent multiple submissions.
- Ensured that loading states are properly managed in the component state for better UI feedback.
- Removed dotenv and prisma from package.json as they are no longer needed in the project.
@nx-cloud
Copy link
Copy Markdown

nx-cloud bot commented Mar 18, 2026

🤖 Nx Cloud AI Fix Eligible

An automatically generated fix could have helped fix failing tasks for this run, but Self-healing CI is disabled for this workspace. Visit workspace settings to enable it and get automatic fixes in future runs.

To disable these notifications, a workspace admin can disable them in workspace settings.


View your CI Pipeline Execution ↗ for commit b5b35ab

Command Status Duration Result
nx run journeys-e2e:e2e ❌ Failed 3m 26s View ↗
nx run resources-e2e:e2e ✅ Succeeded 23s View ↗
nx run watch-e2e:e2e ✅ Succeeded 26s View ↗
nx run journeys-admin-e2e:e2e ✅ Succeeded 33s View ↗
nx run player-e2e:e2e ✅ Succeeded 3s View ↗
nx run watch-modern-e2e:e2e ✅ Succeeded 3s View ↗
nx run short-links-e2e:e2e ✅ Succeeded 3s View ↗
nx run videos-admin-e2e:e2e ✅ Succeeded 4s View ↗
Additional runs (24) ✅ Succeeded ... View ↗

☁️ Nx Cloud last updated this comment at 2026-04-02 23:11:15 UTC

@nx-cloud
Copy link
Copy Markdown

nx-cloud bot commented Mar 18, 2026

View your CI Pipeline Execution ↗ for commit c052613

Command Status Duration Result
nx run-many --target=upload-sourcemaps --projec... ✅ Succeeded 3s View ↗
nx run-many --target=upload-sourcemaps --projec... ✅ Succeeded <1s View ↗
nx run-many --target=deploy --projects=docs ✅ Succeeded 37s View ↗
nx run-many --target=deploy --projects=short-links ✅ Succeeded 30s View ↗

☁️ Nx Cloud last updated this comment at 2026-03-19 01:03:29 UTC

@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 18, 2026

Warnings
⚠️ ❗ Big PR (3802 changes)

(change count - 3802): Pull Request size seems relatively large. If Pull Request contains multiple changes, split each into separate PR will helps faster, easier review.

Generated by 🚫 dangerJS against b5b35ab

@github-actions github-actions bot temporarily deployed to Preview - watch March 19, 2026 00:00 Inactive
@github-actions github-actions bot temporarily deployed to Preview - resources March 19, 2026 00:00 Inactive
@blacksmith-sh

This comment has been minimized.

@stage-branch-merger
Copy link
Copy Markdown

I see you added the "on stage" label, I'll get this merged to the stage branch!

@stage-branch-merger
Copy link
Copy Markdown

Merge conflict attempting to merge this into stage. Please fix manually.

…ation handling

This commit modifies the quickStartTemplate function to accept a reset action, allowing for improved customization of journey templates. It enhances the customization description with additional fields and updates the creation of customization fields to use createMany for efficiency. Additionally, it refines the handling of curly braces in translation processes to ensure accurate preservation of template variables. These changes streamline the journey creation process and improve localization capabilities.
This update modifies the `resolveJourneyCustomizationString` function to support both "{{ key }}" and "{{ key: value }}" syntaxes. It allows for inline values to be resolved when no matching field exists, improving the flexibility of customization templates. Additionally, test cases have been updated to reflect these changes, ensuring accurate resolution of both quoted and unquoted inline values.
@stage-branch-merger
Copy link
Copy Markdown

Merge conflict attempting to merge this into stage. Please fix manually.

This update clarifies the translation instructions by removing redundant examples and emphasizing the importance of preserving template variables within curly braces. The changes aim to enhance the understanding of translation processes while maintaining cultural appropriateness and conciseness in UI contexts.
@stage-branch-merger
Copy link
Copy Markdown

Merge conflict attempting to merge this into stage. Please fix manually.

This update introduces the `translateValue` function to handle the translation of customization field values alongside the journey customization description. It modifies the `journeyCustomizationDescriptionTranslate` mutation to include logic for translating field values when a description is absent or empty. Additionally, the schema has been updated to include `journeyCustomizationFields` in relevant queries, improving the overall translation capabilities for journey customizations.
…ation

This update modifies the `videoPublishChildren` mutation to include additional parameters: `mode` and `dryRun`, allowing for more flexible publishing options. The result type has been updated to reflect changes in published video IDs and counts, and a new type for unpublished videos has been introduced to handle validation failures. Additionally, the `videoPublishChildrenAndLanguages` mutation has been removed to streamline the API. Tests have been updated accordingly to ensure proper functionality.
… videoPublishChildren types

This update removes the `videoPublishChildrenAndLanguages` mutation to simplify the API. The `videoPublishChildren` mutation has been enhanced with new parameters: `mode` and `dryRun`, and the result type has been updated to include additional fields for published videos and validation failures. A new type for unpublished videos has also been introduced, improving error handling during the publishing process.
@stage-branch-merger
Copy link
Copy Markdown

Merge conflict attempting to merge this into stage. Please fix manually.

@stage-branch-merger
Copy link
Copy Markdown

Merge conflict attempting to merge this into stage. Please fix manually.

@blacksmith-sh

This comment has been minimized.

mikeallisonJS and others added 2 commits April 2, 2026 21:35
- Introduced batch translation for customization fields and descriptions to improve efficiency.
- Updated translation prompts to ensure cultural appropriateness and preserve specific formats.
- Refactored translation logic to handle multiple values in a single AI call, reducing the number of requests.
- Added new schemas for batch translation responses and improved error handling in translation processes.
@mikeallisonJS
Copy link
Copy Markdown
Collaborator Author

@coderabbitai resume

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 2, 2026

✅ Actions performed

Reviews resumed.

@mikeallisonJS
Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 2, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

♻️ Duplicate comments (1)
apps/journeys-admin/src/components/TemplateCustomization/MultiStepForm/Screens/LanguageScreen/LanguageScreen.tsx (1)

362-410: ⚠️ Potential issue | 🟠 Major

Reject guest submissions when the selected language needs AI translation.

When needsTranslation is true, only signed-in users enter the translation path. Guests still duplicate the current journey and continue, so they can pick a language that has no template variant and still receive a draft in the source language.

🛠️ Minimal safe fix
-      if (needsTranslation && isSignedIn) {
+      if (needsTranslation) {
+        if (!isSignedIn) {
+          enqueueSnackbar(
+            t('Sign in to translate templates into a new language.'),
+            { variant: 'error' }
+          )
+          return
+        }
         const sourceLanguageName =
           journey.language.name.find((name) => !name.primary)?.value ?? ''

Also applies to: 413-446

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/journeys-admin/src/components/TemplateCustomization/MultiStepForm/Screens/LanguageScreen/LanguageScreen.tsx`
around lines 362 - 410, The code currently allows guests to duplicate and
proceed even when needsTranslation is true; change the flow so guests are
rejected when translation is required: after computing needsTranslation (use the
existing selectedLanguageId, needsTranslation, journeyId), add a guard that if
needsTranslation && !isSignedIn you abort progressing (do not call
handleJourneyDuplication) and surface an error to the user by setting the
appropriate form error/state (e.g., setTranslationCompleted(false) and set a
validation error on languageSelect or a general form error) and return; ensure
the same guard is applied in the later similar branch around the
handleJourneyDuplication/translation setup so guests cannot enter the
translation path in either location.
🧹 Nitpick comments (1)
apis/api-journeys-modern/src/schema/journeyAiTranslate/journeyAiTranslate.spec.ts (1)

960-969: These subscription tests are vacuous.

Both cases only inspect local mocks/fixtures, so they stay green even if the subscription never calls translateCustomizationFields. Please drive the subscription path through the test client and assert either the mock invocation or the resulting Prisma writes.

Also applies to: 971-994

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apis/api-journeys-modern/src/schema/journeyAiTranslate/journeyAiTranslate.spec.ts`
around lines 960 - 969, The tests currently only assert the presence of the mock
(mockTranslateCustomizationFields) instead of actually driving the subscription
flow; update the spec so the test triggers the real subscription path via the
test client/handler used in other tests (e.g., call the subscription endpoint or
invoke the subscription handler used in the suite), await completion, and then
assert the mock was invoked (mockTranslateCustomizationFields.mock.calls.length
> 0) or assert the expected Prisma writes occurred using the test Prisma/mock
client (e.g., check prismaMock.create/update calls). Ensure you do not just
inspect local fixtures—reset/clear mocks before the test, perform the actual
subscription interaction, then assert on mockTranslateCustomizationFields and/or
the Prisma mock to validate side effects; apply the same change to the other
similar test block that currently only inspects the mock.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@apis/api-journeys-modern/src/schema/journeyAiTranslate/journeyCustomizationDescriptionTranslate.mutation.ts`:
- Around line 91-104: The loop currently always translates field.defaultValue
and writes it into field.value, overwriting any existing customized values;
update the logic in the journey.journeyCustomizationFields handling so you only
translate and write defaults when there is no existing customization (i.e., when
field.value is null/undefined). Constrain the .filter() or add a guard before
calling translateValue to require field.defaultValue != null && (field.value ==
null), then call translateValue(...) and use
prisma.journeyCustomizationField.update(...) only for those fields; keep
function names journey.journeyCustomizationFields, translateValue, and
prisma.journeyCustomizationField.update unchanged.

In
`@apis/api-journeys-modern/src/schema/journeyAiTranslate/translateCustomizationFields/translateCustomizationFields.ts`:
- Around line 10-16: The prompt in translateCustomizationFields forbids
translating "times (time formats, day names, month names)" which prevents
localizing user-facing dates; update the customization prompt used by
translateCustomizationFields to remove or relax that rule so month and weekday
names are allowed to be localized (e.g., change the line "DO NOT translate times
(time formats, day names, month names)" to either remove it or replace with
"Preserve numeric time formats but localize month and weekday names as
appropriate"), ensuring the change is made where the
translateCustomizationFields prompt string/constant is defined so event date
strings like "January 15, 2024" will be translated/localized for target locales.

In `@apis/api-journeys/db/seed.ts`:
- Line 25: The seed entry point is calling quickStartTemplate('reset') which
triggers a destructive delete on every db seed run; remove the hardcoded 'reset'
argument from the seed invocation in seed.ts so quickStartTemplate runs
non-destructively by default, or gate the destructive branch behind an explicit
one-off switch (e.g., check an environment variable like SEED_RESET === 'true'
or a CLI flag and only pass 'reset' to quickStartTemplate when that switch is
present), and ensure any documentation/comments note the required flag for
destructive reset.

In `@apis/api-journeys/db/seeds/quickStartTemplate.ts`:
- Around line 57-82: The createMany call to
prisma.journeyCustomizationField.createMany currently only seeds five keys
(name, church_name, feedback_label, website_label, email_label) but the template
uses additional placeholders (video_title, video_description, response_question,
option_1, option_2, option_3, feedback_hint, connect_title, signup_label) which
must be seeded so they are editable via journeyCustomizationFields; update the
prisma.journeyCustomizationField.createMany(...) invocation(s) in this file (and
the other similar blocks referenced by the reviewer) to include rows for each of
those keys with sensible defaultValue strings, or alternatively change the
template strings back to plain text if they are not intended to be
customizable—ensure you edit the prisma.journeyCustomizationField.createMany
blocks (and any other place that builds journeyCustomizationFields) so the new
placeholder keys are present.

In `@apps/journeys-admin/middleware.ts`:
- Around line 28-29: The locale-to-languageID mapping currently assigns the same
ID ('584') to both keys 'ms' and 'pt', causing Malay and Portuguese to collide;
update the mapping so 'ms' and 'pt' have distinct language IDs (replace one of
the '584' values with the correct ID for Malay or Portuguese) in the locale map
object where 'ms' and 'pt' are defined, and verify the chosen IDs match the
canonical language table used elsewhere (so translation/quick-start lookups
resolve correctly).

In
`@apps/journeys-admin/src/components/JourneyList/JourneyCard/JourneyCardMenu/TranslateJourneyDialog/TranslateJourneyDialog.tsx`:
- Around line 158-170: currentLanguageName is computed by looking only for a
non-primary label and can be empty for languages that expose only a primary
label; update the logic used when calling setTranslationVariables so
userLanguageName falls back to the primary label if currentLanguageName is empty
(e.g., compute primaryLabel = journeyData.language.name.find(({ primary }) =>
primary)?.value and set userLanguageName to currentLanguageName || primaryLabel
|| ''), ensuring this change is applied where setTranslationVariables is invoked
to preserve a usable source-language name.

---

Duplicate comments:
In
`@apps/journeys-admin/src/components/TemplateCustomization/MultiStepForm/Screens/LanguageScreen/LanguageScreen.tsx`:
- Around line 362-410: The code currently allows guests to duplicate and proceed
even when needsTranslation is true; change the flow so guests are rejected when
translation is required: after computing needsTranslation (use the existing
selectedLanguageId, needsTranslation, journeyId), add a guard that if
needsTranslation && !isSignedIn you abort progressing (do not call
handleJourneyDuplication) and surface an error to the user by setting the
appropriate form error/state (e.g., setTranslationCompleted(false) and set a
validation error on languageSelect or a general form error) and return; ensure
the same guard is applied in the later similar branch around the
handleJourneyDuplication/translation setup so guests cannot enter the
translation path in either location.

---

Nitpick comments:
In
`@apis/api-journeys-modern/src/schema/journeyAiTranslate/journeyAiTranslate.spec.ts`:
- Around line 960-969: The tests currently only assert the presence of the mock
(mockTranslateCustomizationFields) instead of actually driving the subscription
flow; update the spec so the test triggers the real subscription path via the
test client/handler used in other tests (e.g., call the subscription endpoint or
invoke the subscription handler used in the suite), await completion, and then
assert the mock was invoked (mockTranslateCustomizationFields.mock.calls.length
> 0) or assert the expected Prisma writes occurred using the test Prisma/mock
client (e.g., check prismaMock.create/update calls). Ensure you do not just
inspect local fixtures—reset/clear mocks before the test, perform the actual
subscription interaction, then assert on mockTranslateCustomizationFields and/or
the Prisma mock to validate side effects; apply the same change to the other
similar test block that currently only inspects the mock.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 9e96cfd5-daa9-47c8-99ec-153726dcbb3e

📥 Commits

Reviewing files that changed from the base of the PR and between c052613 and 8300c61.

⛔ Files ignored due to path filters (14)
  • apis/api-journeys/src/__generated__/graphql.ts is excluded by !**/__generated__/**
  • apps/journeys-admin/__generated__/JourneyAiTranslateCreateSubscription.ts is excluded by !**/__generated__/**
  • apps/journeys-admin/__generated__/JourneyCustomizationDescriptionTranslate.ts is excluded by !**/__generated__/**
  • apps/journeys-admin/__generated__/globalTypes.ts is excluded by !**/__generated__/**
  • apps/journeys/__generated__/JourneyAiTranslateCreateSubscription.ts is excluded by !**/__generated__/**
  • apps/journeys/__generated__/JourneyCustomizationDescriptionTranslate.ts is excluded by !**/__generated__/**
  • apps/journeys/__generated__/globalTypes.ts is excluded by !**/__generated__/**
  • apps/resources/__generated__/JourneyAiTranslateCreateSubscription.ts is excluded by !**/__generated__/**
  • apps/resources/__generated__/JourneyCustomizationDescriptionTranslate.ts is excluded by !**/__generated__/**
  • apps/resources/__generated__/globalTypes.ts is excluded by !**/__generated__/**
  • apps/watch/__generated__/JourneyAiTranslateCreateSubscription.ts is excluded by !**/__generated__/**
  • apps/watch/__generated__/JourneyCustomizationDescriptionTranslate.ts is excluded by !**/__generated__/**
  • apps/watch/__generated__/globalTypes.ts is excluded by !**/__generated__/**
  • libs/journeys/ui/__generated__/globalTypes.ts is excluded by !**/__generated__/**
📒 Files selected for processing (19)
  • apis/api-gateway/schema.graphql
  • apis/api-journeys-modern/schema.graphql
  • apis/api-journeys-modern/src/schema/journeyAiTranslate/index.ts
  • apis/api-journeys-modern/src/schema/journeyAiTranslate/journeyAiTranslate.spec.ts
  • apis/api-journeys-modern/src/schema/journeyAiTranslate/journeyAiTranslate.ts
  • apis/api-journeys-modern/src/schema/journeyAiTranslate/journeyCustomizationDescriptionTranslate.mutation.spec.ts
  • apis/api-journeys-modern/src/schema/journeyAiTranslate/journeyCustomizationDescriptionTranslate.mutation.ts
  • apis/api-journeys-modern/src/schema/journeyAiTranslate/translateCustomizationFields/index.ts
  • apis/api-journeys-modern/src/schema/journeyAiTranslate/translateCustomizationFields/translateCustomizationFields.spec.ts
  • apis/api-journeys-modern/src/schema/journeyAiTranslate/translateCustomizationFields/translateCustomizationFields.ts
  • apis/api-journeys/db/seed.ts
  • apis/api-journeys/db/seeds/quickStartTemplate.ts
  • apps/journeys-admin/middleware.ts
  • apps/journeys-admin/src/components/JourneyList/JourneyCard/JourneyCardMenu/TranslateJourneyDialog/TranslateJourneyDialog.spec.tsx
  • apps/journeys-admin/src/components/JourneyList/JourneyCard/JourneyCardMenu/TranslateJourneyDialog/TranslateJourneyDialog.tsx
  • apps/journeys-admin/src/components/Team/CopyToTeamMenuItem/CopyToTeamMenuItem.spec.tsx
  • apps/journeys-admin/src/components/Team/CopyToTeamMenuItem/CopyToTeamMenuItem.tsx
  • apps/journeys-admin/src/components/TemplateCustomization/MultiStepForm/Screens/LanguageScreen/LanguageScreen.spec.tsx
  • apps/journeys-admin/src/components/TemplateCustomization/MultiStepForm/Screens/LanguageScreen/LanguageScreen.tsx
✅ Files skipped from review due to trivial changes (2)
  • apis/api-journeys-modern/src/schema/journeyAiTranslate/index.ts
  • apis/api-journeys-modern/src/schema/journeyAiTranslate/journeyCustomizationDescriptionTranslate.mutation.spec.ts
🚧 Files skipped from review as they are similar to previous changes (4)
  • apis/api-journeys-modern/src/schema/journeyAiTranslate/translateCustomizationFields/index.ts
  • apps/journeys-admin/src/components/Team/CopyToTeamMenuItem/CopyToTeamMenuItem.tsx
  • apis/api-gateway/schema.graphql
  • apis/api-journeys-modern/src/schema/journeyAiTranslate/journeyAiTranslate.ts

Comment on lines +91 to +104
if (hasFields) {
await Promise.all(
journey.journeyCustomizationFields
.filter((field) => field.defaultValue != null)
.map(async (field) => {
const translatedFieldValue = await translateValue({
value: field.defaultValue!,
sourceLanguageName: input.sourceLanguageName,
targetLanguageName: input.targetLanguageName
})
await prisma.journeyCustomizationField.update({
where: { id: field.id },
data: { value: translatedFieldValue }
})
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Don't overwrite existing customization values with translated defaults.

This loop always translates field.defaultValue and writes it into value. If a journey already has customized values, invoking this mutation will replace them with translated defaults.

🛠️ Minimal safe fix
         if (hasFields) {
           await Promise.all(
             journey.journeyCustomizationFields
-              .filter((field) => field.defaultValue != null)
+              .filter(
+                (field) =>
+                  field.defaultValue != null && field.value == null
+              )
               .map(async (field) => {
                 const translatedFieldValue = await translateValue({
                   value: field.defaultValue!,
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apis/api-journeys-modern/src/schema/journeyAiTranslate/journeyCustomizationDescriptionTranslate.mutation.ts`
around lines 91 - 104, The loop currently always translates field.defaultValue
and writes it into field.value, overwriting any existing customized values;
update the logic in the journey.journeyCustomizationFields handling so you only
translate and write defaults when there is no existing customization (i.e., when
field.value is null/undefined). Constrain the .filter() or add a guard before
calling translateValue to require field.defaultValue != null && (field.value ==
null), then call translateValue(...) and use
prisma.journeyCustomizationField.update(...) only for those fields; keep
function names journey.journeyCustomizationFields, translateValue, and
prisma.journeyCustomizationField.update unchanged.

Comment on lines +10 to +16
You are a professional translation engine.
- Translate text accurately while preserving meaning and cultural appropriateness
- DO NOT translate addresses (street addresses, city names, postal codes, country names)
- DO NOT translate times (time formats, day names, month names)
- DO NOT translate locations (place names, venue names, building names)
- DO NOT translate proper nouns (names of people, organizations, brands)
- Maintain the original format and structure`
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Don't tell the model to keep month/day names in the source language.

This prompt explicitly forbids translating month and day names, but the new customization-field flow needs exactly that for user-facing values like event dates. With this instruction, strings such as January 15, 2024 will tend to stay in the source language instead of localizing for the target audience.

🛠️ Prompt tweak
- - DO NOT translate times (time formats, day names, month names)
+ - Preserve literal clock times and time-zone identifiers when they must remain exact
+ - Localize user-facing dates, including month/day names, when translating copy
📝 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.

Suggested change
You are a professional translation engine.
- Translate text accurately while preserving meaning and cultural appropriateness
- DO NOT translate addresses (street addresses, city names, postal codes, country names)
- DO NOT translate times (time formats, day names, month names)
- DO NOT translate locations (place names, venue names, building names)
- DO NOT translate proper nouns (names of people, organizations, brands)
- Maintain the original format and structure`
You are a professional translation engine.
- Translate text accurately while preserving meaning and cultural appropriateness
- DO NOT translate addresses (street addresses, city names, postal codes, country names)
- Preserve literal clock times and time-zone identifiers when they must remain exact
- Localize user-facing dates, including month/day names, when translating copy
- DO NOT translate locations (place names, venue names, building names)
- DO NOT translate proper nouns (names of people, organizations, brands)
- Maintain the original format and structure`
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apis/api-journeys-modern/src/schema/journeyAiTranslate/translateCustomizationFields/translateCustomizationFields.ts`
around lines 10 - 16, The prompt in translateCustomizationFields forbids
translating "times (time formats, day names, month names)" which prevents
localizing user-facing dates; update the customization prompt used by
translateCustomizationFields to remove or relax that rule so month and weekday
names are allowed to be localized (e.g., change the line "DO NOT translate times
(time formats, day names, month names)" to either remove it or replace with
"Preserve numeric time formats but localize month and weekday names as
appropriate"), ensuring the change is made where the
translateCustomizationFields prompt string/constant is defined so event date
strings like "January 15, 2024" will be translated/localized for target locales.

Comment on lines +57 to 82
// Customization fields parsed from the description
await prisma.journeyCustomizationField.createMany({
data: [
{ journeyId: journey.id, key: 'name', defaultValue: 'Friend' },
{
journeyId: journey.id,
key: 'church_name',
defaultValue: 'Our Church'
},
{
journeyId: journey.id,
key: 'feedback_label',
defaultValue: 'share your thoughts'
},
{
journeyId: journey.id,
key: 'website_label',
defaultValue: 'our website'
},
{
journeyId: journey.id,
key: 'email_label',
defaultValue: 'email us'
}
]
})
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Seed every new quick-start placeholder key.

The template now uses additional {{ key: default }} placeholders like video_title, video_description, response_question, option_1/2/3, feedback_hint, connect_title, and signup_label, but journeyCustomizationField.createMany() still only creates five keys. The new quick-start field translation path in this PR only reads journeyCustomizationFields, so those extra placeholders won't be editable or translatable through that flow. Either add rows for each new key or switch those strings back to plain text if they are not meant to be customizable.

Also applies to: 221-245, 296-357, 419-438

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apis/api-journeys/db/seeds/quickStartTemplate.ts` around lines 57 - 82, The
createMany call to prisma.journeyCustomizationField.createMany currently only
seeds five keys (name, church_name, feedback_label, website_label, email_label)
but the template uses additional placeholders (video_title, video_description,
response_question, option_1, option_2, option_3, feedback_hint, connect_title,
signup_label) which must be seeded so they are editable via
journeyCustomizationFields; update the
prisma.journeyCustomizationField.createMany(...) invocation(s) in this file (and
the other similar blocks referenced by the reviewer) to include rows for each of
those keys with sensible defaultValue strings, or alternatively change the
template strings back to plain text if they are not intended to be
customizable—ensure you edit the prisma.journeyCustomizationField.createMany
blocks (and any other place that builds journeyCustomizationFields) so the new
placeholder keys are present.

Comment on lines +28 to +29
ms: '584', // Malay
pt: '584' // Portuguese
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Give ms and pt distinct language IDs.

These two locales now resolve to the same language ID (584). Unless Malay and Portuguese intentionally share a single language record, one of these locales will map to the wrong language anywhere this table drives quick-start or locale-based translation.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/journeys-admin/middleware.ts` around lines 28 - 29, The
locale-to-languageID mapping currently assigns the same ID ('584') to both keys
'ms' and 'pt', causing Malay and Portuguese to collide; update the mapping so
'ms' and 'pt' have distinct language IDs (replace one of the '584' values with
the correct ID for Malay or Portuguese) in the locale map object where 'ms' and
'pt' are defined, and verify the chosen IDs match the canonical language table
used elsewhere (so translation/quick-start lookups resolve correctly).

Comment on lines +158 to +170
const currentLanguageName =
journeyData.language.name.find(({ primary }) => !primary)?.value ?? ''

// Start the translation subscription
setTranslationVariables({
journeyId: duplicateData.journeyDuplicate.id,
name: `${journeyData.title}`,
journeyLanguageName:
journeyData.language.name.find(({ primary }) => !primary)?.value ??
'',
journeyLanguageName: currentLanguageName,
textLanguageId: selectedLanguage.id,
textLanguageName:
selectedLanguage.nativeName ?? selectedLanguage.localName ?? ''
selectedLanguage.nativeName ?? selectedLanguage.localName ?? '',
userLanguageId: journeyData.language.id,
userLanguageName: currentLanguageName
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Fall back to the primary language name before setting userLanguageName.

This lookup only considers non-primary names. For languages that only expose a primary label, currentLanguageName becomes '', so the new userLanguageName loses the source-language hint even though a usable name exists.

💡 Suggested change
-        const currentLanguageName =
-          journeyData.language.name.find(({ primary }) => !primary)?.value ?? ''
+        const currentLanguageName =
+          journeyData.language.name.find(({ primary }) => !primary)?.value ??
+          journeyData.language.name.find(({ primary }) => primary)?.value

         // Start the translation subscription
         setTranslationVariables({
           journeyId: duplicateData.journeyDuplicate.id,
           name: `${journeyData.title}`,
-          journeyLanguageName: currentLanguageName,
+          journeyLanguageName: currentLanguageName ?? '',
           textLanguageId: selectedLanguage.id,
           textLanguageName:
             selectedLanguage.nativeName ?? selectedLanguage.localName ?? '',
           userLanguageId: journeyData.language.id,
-          userLanguageName: currentLanguageName
+          ...(currentLanguageName != null
+            ? { userLanguageName: currentLanguageName }
+            : {})
         })
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@apps/journeys-admin/src/components/JourneyList/JourneyCard/JourneyCardMenu/TranslateJourneyDialog/TranslateJourneyDialog.tsx`
around lines 158 - 170, currentLanguageName is computed by looking only for a
non-primary label and can be empty for languages that expose only a primary
label; update the logic used when calling setTranslationVariables so
userLanguageName falls back to the primary label if currentLanguageName is empty
(e.g., compute primaryLabel = journeyData.language.name.find(({ primary }) =>
primary)?.value and set userLanguageName to currentLanguageName || primaryLabel
|| ''), ensuring this change is applied where setTranslationVariables is invoked
to preserve a usable source-language name.

@blacksmith-sh
Copy link
Copy Markdown
Contributor

blacksmith-sh bot commented Apr 2, 2026

Found 1 test failure on Blacksmith runners:

Failure

Test View Logs
src/e2e/journeys.spec.ts/journeys View Logs

Fix in Cursor

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants