Skip to content

Comments

feat: QWC file regen: landing page#1755

Open
JustARatherRidiculouslyLongUsername wants to merge 2 commits intoallow-qwc-regenerationfrom
qwc-regen-landing-page
Open

feat: QWC file regen: landing page#1755
JustARatherRidiculouslyLongUsername wants to merge 2 commits intoallow-qwc-regenerationfrom
qwc-regen-landing-page

Conversation

@JustARatherRidiculouslyLongUsername
Copy link
Contributor

@JustARatherRidiculouslyLongUsername JustARatherRidiculouslyLongUsername commented Feb 13, 2026

Description

localhost_4200_integrations_qbd_direct_main_configuration_qwc_file (1) localhost_4200_integrations_qbd_direct_main_configuration_qwc_file (3)

Clickup

https://app.clickup.com/t/86d1x1yra

Summary by CodeRabbit

  • New Features

    • Added QWC file management to QBD Direct configuration with a new landing screen.
    • Users can now regenerate QWC files using existing or new company file paths.
    • Copy-to-clipboard functionality for QWC file paths.
    • Enhanced dashboard messaging for successful export notifications.
  • Style

    • Updated disabled state styling and theming system.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 13, 2026

Walkthrough

A new QWC (Quickbooks Company File) landing feature is introduced to the QBD Direct integration, encompassing a new component, routing, form model, localization strings, and styling updates. The feature enables users to manage QWC file regeneration by choosing between existing or new file paths, with metadata display and clipboard integration.

Changes

Cohort / File(s) Summary
Type and Model Definitions
src/app/core/models/qbd-direct/qbd-direct-configuration/qbd-direct-connector.model.ts, src/app/core/models/qbd-direct/qbd-direct-configuration/qbd-direct-qwc-file.model.ts
Added last_file_generated_by field to QbdConnectorGet type and created new NextStepOption enum with RegenerateQwcForm interface for form control type-safety.
Routing and Navigation
src/app/integrations/qbd-direct/qbd-direct-main/qbd-direct-configuration/qbd-direct-configuration-routing.module.ts, src/app/integrations/qbd-direct/qbd-direct-main/qbd-direct-configuration/qbd-direct-configuration.component.ts
Introduced new child route qwc_file mapping to QbdDirectQwcFileLandingComponent and added corresponding QWC File navigation item to configuration step list.
QWC File Landing Component
src/app/integrations/qbd-direct/qbd-direct-shared/qbd-direct-qwc-file/qbd-direct-qwc-file-landing/qbd-direct-qwc-file-landing.component.ts, src/app/integrations/qbd-direct/qbd-direct-shared/qbd-direct-qwc-file/qbd-direct-qwc-file-landing/qbd-direct-qwc-file-landing.component.html, src/app/integrations/qbd-direct/qbd-direct-shared/qbd-direct-qwc-file/qbd-direct-qwc-file-landing/qbd-direct-qwc-file-landing.component.spec.ts
New landing component with form-driven UI for selecting QWC file path options (existing vs. new), clipboard copy action, loading state management, and service integration for fetching connector settings.
Styling and Theme Updates
src/app/shared/components/core/svg-icon/svg-icon.component.scss, src/app/shared/components/core/svg-icon/svg-icon.component.ts, src/app/integrations/qbd-direct/qbd-direct-shared/qbd-direct-qwc-file/qbd-direct-qwc-file-landing/qbd-direct-qwc-file-landing.component.scss, src/assets/themes/fyle/fdl.scss, tailwind.config.js
Removed inline-block display from svg-icon class, added copy icon mapping, introduced .shadow-card styling class, added --disabled CSS custom property, and extended Tailwind color aliases with disabled color token.
Localization
src/assets/i18n/en.json
Added new i18n keys under qbdDirectAdvancedSettings.QWCFile, dashboard.exportLogSubHeaderSuccessful, and comprehensive qbdDirectQwcFileLanding block containing step labels, descriptions, option text, benefit explanations, and action labels.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~35 minutes

Poem

🐰 A file path hops into view, with copy and click so true,
Two choices sprout: old path or new—which shall we choose for you?
With landing page and form so bright, disabled tokens catching light,
The shadows dance, the routes align—integration's working fine! ✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Description check ❓ Inconclusive The PR description includes images demonstrating the feature but lacks a written description of the implementation, changes made, or technical details as required by the template. Add a written description explaining the implementation details, component structure, routing changes, and any other technical aspects beyond the visual screenshots.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat: QWC file regen: landing page' clearly and concisely describes the main feature being added: a new landing page for QWC file regeneration, which aligns with the substantial changes across multiple files.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

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

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch qwc-regen-landing-page

Tip

Issue Planner is now in beta. Read the docs and try it out! Share your feedback on Discord.


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.

@github-actions
Copy link

🔁 Code Duplication Report - Angular

Format Files analyzed Total lines Total tokens Clones found Duplicated lines Duplicated tokens
typescript 788 58507 535833 348 5758 (9.84%) -0.02% 🚀 54031 (10.08%) -0.02% 🚀
scss 69 3801 20561 7 148 (3.89%) -0.01% 🚀 769 (3.74%) -0.01% 🚀
markup 355 17958 168861 160 2127 (11.84%) +0.01% ❗ 18072 (10.70%) +0.07% ❗
python 4 727 5680 3 32 (4.40%) 368 (6.48%)
yaml 3 155 905 0 0 (0.00%) 0 (0.00%)
javascript 6 397 3497 0 0 (0.00%) 0 (0.00%)
markdown 2 96 366 0 0 (0.00%) 0 (0.00%)
bash 2 69 804 0 0 (0.00%) 0 (0.00%)
Total: 1229 81710 736507 518 8065 (9.87%) -0.01% 🚀 73240 (9.94%)

📊 No significant change in code duplication.

Copy link
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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/app/shared/components/core/svg-icon/svg-icon.component.ts (1)

4-31: ⚠️ Potential issue | 🟠 Major

Add mappings for icons used in QWC regeneration UI

The template uses check-circle-fill and question-square-fill icons. When the C1 branding config enables useCustomIcon: true, these keys must be mapped through ICON_MAPPING (line 126); missing entries resolve to undefined, breaking the sprite URL. Add these mappings to the ICON_MAPPING object.

🤖 Fix all issues with AI agents
In
`@src/app/integrations/qbd-direct/qbd-direct-main/qbd-direct-configuration/qbd-direct-configuration.component.ts`:
- Line 32: The i18n key used in the navigation item is inconsistent: change the
translate key from 'qbd_direct.configuration.QWCFile.stepName' to
'qbd_direct.configuration.qwcFile.stepName' in
qbd-direct-configuration.component (the line referencing
this.translocoService.translate), and update the corresponding key in the
en.json (and any other locale files) to use "qwcFile" (camelCase) so it matches
the other keys like exportSetting/importSetting/advancedSettings.

In
`@src/app/integrations/qbd-direct/qbd-direct-shared/qbd-direct-regenerate-qwc-file/qbd-direct-regenerate-qwc-file.component.scss`:
- Around line 1-6: Update the .shadow-card &:hover rule to use modern color
notation and drop px on zero offsets: replace "box-shadow: 0px 4px 4px 0px
rgba(44, 48, 78, 0.10);" in the .shadow-card hover block with a lint-compliant
form such as "box-shadow: 0 4px 4px 0 rgb(44 48 78 / 0.1);" so it uses RGB
slash-alpha and no unit on zero values.

In
`@src/app/integrations/qbd-direct/qbd-direct-shared/qbd-direct-regenerate-qwc-file/qbd-direct-regenerate-qwc-file.component.spec.ts`:
- Around line 9-18: The spec fails because QbdDirectConnectorService (and other
deps used in ngOnInit) are not provided; update the TestBed for
QbdDirectRegenerateQWCFileComponent to either provide mocked providers for
QbdDirectConnectorService (mocking getQBDConnectorSettings to return an
observable), FormBuilder, BrandingService, IntegrationsToastService, and
TranslocoService (mirror the mocks used in
qbd-direct-assisted-setup.component.spec.ts), or mark the suite as skipped with
xdescribe to match other specs; ensure the mock for QbdDirectConnectorService is
referenced so ngOnInit’s call to getQBDConnectorSettings().subscribe() won’t
throw.

In
`@src/app/integrations/qbd-direct/qbd-direct-shared/qbd-direct-regenerate-qwc-file/qbd-direct-regenerate-qwc-file.component.ts`:
- Around line 85-88: The subscription to
qbdDirectConnectorService.getQBDConnectorSettings() in the
QbdDirectRegenerateQwcFileComponent lacks error handling so isLoading can remain
true on failure; update the call (getQBDConnectorSettings()) to handle errors by
adding an error callback or using pipe/catchError, ensure this.isLoading is set
to false in the error branch, and surface the failure (e.g., call the
component's notification/toast method or log the error) while leaving
connectorSettings unchanged.
- Around line 42-44: The getter selectedNextStep can return null even though
it’s typed as NextStepOption; update it to accurately reflect possible null by
changing its signature to return NextStepOption | null (or provide a safe
default), and return the control value without force-casting (e.g. return
this.regenerateQwcForm.get('nextStep')?.value as NextStepOption | null or
coalesce to a default like ?? DEFAULT_OPTION). Ensure callers/templates handle
the nullable type accordingly or use a non-null default constant referenced
here.

In `@src/assets/i18n/en.json`:
- Around line 2733-2766: Reorder the child keys alphabetically in the two i18n
objects: under qbd_direct.configuration ensure the three keys are ordered as
advancedSettings, exportSetting, QWCFile (instead of exportSetting,
advancedSettings, QWCFile), and under qbdDirectRegenerateQwcFile sort all 23
keys alphabetically (so contentText appears before stepName); update the JSON
key ordering only (no text changes) to maintain alphabetical order for merge
hygiene.
🧹 Nitpick comments (2)
src/app/integrations/qbd-direct/qbd-direct-shared/qbd-direct-regenerate-qwc-file/qbd-direct-regenerate-qwc-file.component.ts (2)

58-67: navigator.clipboard.writeText can reject — handle the promise.

Clipboard access may be denied (e.g., non-HTTPS, permission policy). The unhandled promise rejection will silently fail while the success toast still shows.

Proposed fix
   copyQwcFilePath() {
     if (!this.connectorSettings?.file_location) {
       return;
     }
-    navigator.clipboard.writeText(this.connectorSettings.file_location);
-    this.toastService.displayToastMessage(
-      ToastSeverity.SUCCESS,
-      this.translocoService.translate('qbdDirectRegenerateQwcFile.qwcFilePathCopied')
-    );
+    navigator.clipboard.writeText(this.connectorSettings.file_location).then(() => {
+      this.toastService.displayToastMessage(
+        ToastSeverity.SUCCESS,
+        this.translocoService.translate('qbdDirectRegenerateQwcFile.qwcFilePathCopied')
+      );
+    });
   }

69-83: Group-level validator is over-engineered for a single required control.

A simple Validators.required on the FormControl achieves the same result more idiomatically.

Simplified form setup
     this.regenerateQwcForm = this.formBuilder.group<RegenerateQwcForm>({
-      nextStep: new FormControl(null)
-    }, {
-      validators: (form) => {
-        const errors: ValidationErrors = {};
-
-        if (form.get('nextStep')?.value === null) {
-          errors.nextStep = { required: true };
-        }
-
-        return Object.keys(errors).length > 0 ? errors : null;
-      }
+      nextStep: new FormControl(null, { validators: [Validators.required] })
     });

Note: you'll need to add Validators to the existing @angular/forms import (it's already partially imported).

{label: this.translocoService.translate('qbd_direct.configuration.importSetting.stepName'), routerLink: '/integrations/qbd_direct/main/configuration/import_settings', value: 'import_settings'},
{label: this.translocoService.translate('qbd_direct.configuration.advancedSettings.stepName'), routerLink: '/integrations/qbd_direct/main/configuration/advanced_settings', value: 'advanced_settings'}
{label: this.translocoService.translate('qbd_direct.configuration.advancedSettings.stepName'), routerLink: '/integrations/qbd_direct/main/configuration/advanced_settings', value: 'advanced_settings'},
{label: this.translocoService.translate('qbd_direct.configuration.QWCFile.stepName'), routerLink: '/integrations/qbd_direct/main/configuration/qwc_file', value: 'qwc_file'}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

i18n key QWCFile should be camelCase (qwcFile) for consistency.

The other keys in this array use camelCase (exportSetting, importSetting, advancedSettings), but this one uses PascalCase QWCFile. This also needs to match the key in en.json.

-      {label: this.translocoService.translate('qbd_direct.configuration.QWCFile.stepName'), routerLink: '/integrations/qbd_direct/main/configuration/qwc_file', value: 'qwc_file'}
+      {label: this.translocoService.translate('qbd_direct.configuration.qwcFile.stepName'), routerLink: '/integrations/qbd_direct/main/configuration/qwc_file', value: 'qwc_file'}

As per coding guidelines, top-level i18n object must be the component/feature folder name in camelCase, with keys using meaningful semantic names.

📝 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
{label: this.translocoService.translate('qbd_direct.configuration.QWCFile.stepName'), routerLink: '/integrations/qbd_direct/main/configuration/qwc_file', value: 'qwc_file'}
{label: this.translocoService.translate('qbd_direct.configuration.qwcFile.stepName'), routerLink: '/integrations/qbd_direct/main/configuration/qwc_file', value: 'qwc_file'}
🤖 Prompt for AI Agents
In
`@src/app/integrations/qbd-direct/qbd-direct-main/qbd-direct-configuration/qbd-direct-configuration.component.ts`
at line 32, The i18n key used in the navigation item is inconsistent: change the
translate key from 'qbd_direct.configuration.QWCFile.stepName' to
'qbd_direct.configuration.qwcFile.stepName' in
qbd-direct-configuration.component (the line referencing
this.translocoService.translate), and update the corresponding key in the
en.json (and any other locale files) to use "qwcFile" (camelCase) so it matches
the other keys like exportSetting/importSetting/advancedSettings.

Comment on lines +1 to +6
.shadow-card {
@apply tw-transition-shadow;

&:hover {
box-shadow: 0px 4px 4px 0px rgba(44, 48, 78, 0.10);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Fix stylelint violations in hover shadow

Stylelint flags the hover box-shadow for legacy color notation and zero units. Update to modern rgb() slash-alpha and drop px on zeros to satisfy the lint rules.

Proposed fix
 .shadow-card {
     `@apply` tw-transition-shadow;

     &:hover {
-        box-shadow: 0px 4px 4px 0px rgba(44, 48, 78, 0.10);
+        box-shadow: 0 4px 4px 0 rgb(44 48 78 / 10%);
     }
 }
📝 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
.shadow-card {
@apply tw-transition-shadow;
&:hover {
box-shadow: 0px 4px 4px 0px rgba(44, 48, 78, 0.10);
}
.shadow-card {
`@apply` tw-transition-shadow;
&:hover {
box-shadow: 0 4px 4px 0 rgb(44 48 78 / 10%);
}
}
🧰 Tools
🪛 Stylelint (17.2.0)

[error] 5-5: Expected "0.10" to be "10%" (alpha-value-notation)

(alpha-value-notation)


[error] 5-5: Expected "rgba" to be "rgb" (color-function-alias-notation)

(color-function-alias-notation)


[error] 5-5: Expected modern color-function notation (color-function-notation)

(color-function-notation)


[error] 5-5: Unexpected unit (length-zero-no-unit)

(length-zero-no-unit)


[error] 5-5: Unexpected unit (length-zero-no-unit)

(length-zero-no-unit)

🤖 Prompt for AI Agents
In
`@src/app/integrations/qbd-direct/qbd-direct-shared/qbd-direct-regenerate-qwc-file/qbd-direct-regenerate-qwc-file.component.scss`
around lines 1 - 6, Update the .shadow-card &:hover rule to use modern color
notation and drop px on zero offsets: replace "box-shadow: 0px 4px 4px 0px
rgba(44, 48, 78, 0.10);" in the .shadow-card hover block with a lint-compliant
form such as "box-shadow: 0 4px 4px 0 rgb(44 48 78 / 0.1);" so it uses RGB
slash-alpha and no unit on zero values.

Comment on lines 9 to 18
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [QbdDirectRegenerateQWCFileComponent]
})
.compileComponents();

fixture = TestBed.createComponent(QbdDirectRegenerateQWCFileComponent);
component = fixture.componentInstance;
await fixture.whenStable();
});
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

find src/app/integrations/qbd-direct/qbd-direct-shared -name "*.component.ts" | head -5

Repository: fylein/fyle-integrations-app

Length of output: 642


🏁 Script executed:

cat -n src/app/integrations/qbd-direct/qbd-direct-shared/qbd-direct-regenerate-qwc-file/qbd-direct-regenerate-qwc-file.component.ts

Repository: fylein/fyle-integrations-app

Length of output: 4067


🏁 Script executed:

find src/app/integrations/qbd-direct/qbd-direct-shared -name "*.spec.ts" -exec sh -c 'echo "=== {} ===" && head -35 "$1"' _ {} \;

Repository: fylein/fyle-integrations-app

Length of output: 8818


Test will fail — missing provider for QbdDirectConnectorService.

ngOnInit immediately calls this.qbdDirectConnectorService.getQBDConnectorSettings().subscribe() at line 85, but QbdDirectConnectorService is not provided in TestBed. Additionally, FormBuilder, BrandingService, IntegrationsToastService, and TranslocoService are not provided or mocked.

Either mock all dependencies (as done in qbd-direct-assisted-setup.component.spec.ts) or skip the spec file using xdescribe to match the project's convention—most other specs in this directory are skipped.

🤖 Prompt for AI Agents
In
`@src/app/integrations/qbd-direct/qbd-direct-shared/qbd-direct-regenerate-qwc-file/qbd-direct-regenerate-qwc-file.component.spec.ts`
around lines 9 - 18, The spec fails because QbdDirectConnectorService (and other
deps used in ngOnInit) are not provided; update the TestBed for
QbdDirectRegenerateQWCFileComponent to either provide mocked providers for
QbdDirectConnectorService (mocking getQBDConnectorSettings to return an
observable), FormBuilder, BrandingService, IntegrationsToastService, and
TranslocoService (mirror the mocks used in
qbd-direct-assisted-setup.component.spec.ts), or mark the suite as skipped with
xdescribe to match other specs; ensure the mock for QbdDirectConnectorService is
referenced so ngOnInit’s call to getQBDConnectorSettings().subscribe() won’t
throw.

Comment on lines +42 to +44
get selectedNextStep(): NextStepOption {
return this.regenerateQwcForm.get('nextStep')?.value as NextStepOption;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Getter can return null despite NextStepOption return type.

When no option is selected, value is null but the cast as NextStepOption silently hides this. Template code relying on this getter may behave unexpectedly.

-  get selectedNextStep(): NextStepOption {
-    return this.regenerateQwcForm.get('nextStep')?.value as NextStepOption;
+  get selectedNextStep(): NextStepOption | null {
+    return this.regenerateQwcForm.get('nextStep')?.value ?? null;
   }
📝 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
get selectedNextStep(): NextStepOption {
return this.regenerateQwcForm.get('nextStep')?.value as NextStepOption;
}
get selectedNextStep(): NextStepOption | null {
return this.regenerateQwcForm.get('nextStep')?.value ?? null;
}
🤖 Prompt for AI Agents
In
`@src/app/integrations/qbd-direct/qbd-direct-shared/qbd-direct-regenerate-qwc-file/qbd-direct-regenerate-qwc-file.component.ts`
around lines 42 - 44, The getter selectedNextStep can return null even though
it’s typed as NextStepOption; update it to accurately reflect possible null by
changing its signature to return NextStepOption | null (or provide a safe
default), and return the control value without force-casting (e.g. return
this.regenerateQwcForm.get('nextStep')?.value as NextStepOption | null or
coalesce to a default like ?? DEFAULT_OPTION). Ensure callers/templates handle
the nullable type accordingly or use a non-null default constant referenced
here.

Comment on lines +85 to +88
this.qbdDirectConnectorService.getQBDConnectorSettings().subscribe((qbdConnectorSettings: QbdConnectorGet) => {
this.connectorSettings = qbdConnectorSettings;
this.isLoading = false;
});
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Missing error handling — loading state stuck on failure.

If getQBDConnectorSettings() errors, isLoading remains true forever and the user is stuck on a spinner. Add error handling to stop loading and surface the failure.

Proposed fix
     this.qbdDirectConnectorService.getQBDConnectorSettings().subscribe((qbdConnectorSettings: QbdConnectorGet) => {
       this.connectorSettings = qbdConnectorSettings;
       this.isLoading = false;
-    });
+    }, () => {
+      this.isLoading = false;
+    });
📝 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
this.qbdDirectConnectorService.getQBDConnectorSettings().subscribe((qbdConnectorSettings: QbdConnectorGet) => {
this.connectorSettings = qbdConnectorSettings;
this.isLoading = false;
});
this.qbdDirectConnectorService.getQBDConnectorSettings().subscribe((qbdConnectorSettings: QbdConnectorGet) => {
this.connectorSettings = qbdConnectorSettings;
this.isLoading = false;
}, () => {
this.isLoading = false;
});
🤖 Prompt for AI Agents
In
`@src/app/integrations/qbd-direct/qbd-direct-shared/qbd-direct-regenerate-qwc-file/qbd-direct-regenerate-qwc-file.component.ts`
around lines 85 - 88, The subscription to
qbdDirectConnectorService.getQBDConnectorSettings() in the
QbdDirectRegenerateQwcFileComponent lacks error handling so isLoading can remain
true on failure; update the call (getQBDConnectorSettings()) to handle errors by
adding an error callback or using pipe/catchError, ensure this.isLoading is set
to false in the error branch, and surface the failure (e.g., call the
component's notification/toast method or log the error) while leaving
connectorSettings unchanged.

Comment on lines 2733 to 2766
},
"QWCFile": {
"stepName": "QWC file"
}
}
},
"dashboard": {
"exportLogSubHeaderSuccessful": "These expenses have been successfully exported to your {{ appName }}."
},
"qbdDirectRegenerateQwcFile": {
"stepName": "Regenerate QWC File",
"contentText": "Regenerate your QuickBooks Web Connector file or set up a new one if your company file path has changed.",
"currentFileHeader": "Current company file path",
"currentFileLastRegenerated": "Last regenerated",
"currentFileBy": " by ",
"currentFileOn": " on ",
"qwcFilePathCopied": "QWC file path copied!",
"nextStepHeader": "Choose your next step:",
"useExistingPath": "Use existing company file path",
"useExistingPathDescription": "Regenerate your QuickBooks Web Connector (.qwc) file and password for the current company file path.",
"useNewPath": "Use new company file path",
"useNewPathDescription": "Generate a new QuickBooks Web Connector (.qwc) file and password for a new or updated company file path.",
"whenToUse": "When to use:",
"existingWhenToUse1": "The QWC file was lost or corrupted",
"existingWhenToUse2": "Another team member needs access (not recommended)",
"newWhenToUse1": "The company file was moved or renamed",
"newWhenToUse2": "QuickBooks Desktop was reinstalled or updated",
"keyBenefits": "Key benefits:",
"existingBenefit1": "Easiest and safest way to restore your connection",
"existingBenefit2": "No changes to your integration configurations",
"newBenefit1": "Ensures your integration matches your current QuickBooks setup",
"newBenefit2": "Resolves path mismatch errors",
"readMore": "Read more"
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# First, list all locale files
echo "=== Available locale files ==="
ls -la src/assets/i18n/*.json 2>/dev/null || echo "Directory not found"

# Check file count
echo ""
echo "=== Locale file count ==="
find src/assets/i18n -name "*.json" -type f | wc -l

Repository: fylein/fyle-integrations-app

Length of output: 206


🏁 Script executed:

#!/bin/bash
# Run the verification script from the review
python - <<'PY'
import json, glob
def flatten(d, prefix=""):
    out = {}
    for k, v in d.items():
        key = f"{prefix}.{k}" if prefix else k
        if isinstance(v, dict):
            out.update(flatten(v, key))
        else:
            out[key] = v
    return out

files = sorted(glob.glob("src/assets/i18n/*.json"))
base = "src/assets/i18n/en.json"
with open(base) as f:
    base_keys = set(flatten(json.load(f)).keys())

for path in files:
    if path == base:
        continue
    with open(path) as f:
        keys = set(flatten(json.load(f)).keys())
    missing = sorted(base_keys - keys)
    if missing:
        print(f"{path}: missing {len(missing)} keys. Example: {missing[:20]}")
PY

Repository: fylein/fyle-integrations-app

Length of output: 54


🏁 Script executed:

#!/bin/bash
# Check alphabetical ordering of keys in the affected sections
python - <<'PY'
import json

with open("src/assets/i18n/en.json") as f:
    data = json.load(f)

# Check qbd_direct.configuration ordering
if "qbd_direct" in data and "configuration" in data["qbd_direct"]:
    config_keys = list(data["qbd_direct"]["configuration"].keys())
    sorted_keys = sorted(config_keys)
    print("=== qbd_direct.configuration key order ===")
    print("Current:", config_keys)
    print("Sorted: ", sorted_keys)
    print("In order:", config_keys == sorted_keys)
    if config_keys != sorted_keys:
        print("\nMisaligned keys:")
        for i, (curr, sort) in enumerate(zip(config_keys, sorted_keys)):
            if curr != sort:
                print(f"  Position {i}: have '{curr}', should be '{sort}'")

# Check qbdDirectRegenerateQwcFile ordering
if "qbdDirectRegenerateQwcFile" in data:
    qwc_keys = list(data["qbdDirectRegenerateQwcFile"].keys())
    sorted_keys = sorted(qwc_keys)
    print("\n=== qbdDirectRegenerateQwcFile key order ===")
    print("Current:", qwc_keys)
    print("Sorted: ", sorted_keys)
    print("In order:", qwc_keys == sorted_keys)
    if qwc_keys != sorted_keys:
        print("\nMisaligned keys:")
        for i, (curr, sort) in enumerate(zip(qwc_keys, sorted_keys)):
            if curr != sort:
                print(f"  Position {i}: have '{curr}', should be '{sort}'")
PY

Repository: fylein/fyle-integrations-app

Length of output: 2683


🏁 Script executed:

#!/bin/bash
# List all files in i18n directory with more detail
echo "=== All files in i18n directory ==="
find src/assets/i18n -type f | sort

echo ""
echo "=== Check file sizes ==="
find src/assets/i18n -type f -exec wc -l {} +

echo ""
echo "=== Check if QWC keys exist in other locales ==="
for file in src/assets/i18n/*.json; do
  echo "File: $file"
  if grep -q "qbdDirectRegenerateQwcFile" "$file" 2>/dev/null; then
    echo "  ✓ Contains qbdDirectRegenerateQwcFile"
  else
    echo "  ✗ Missing qbdDirectRegenerateQwcFile"
  fi
done

Repository: fylein/fyle-integrations-app

Length of output: 447


🏁 Script executed:

#!/bin/bash
# Check what the other en.json files contain - are they full locales or partial configs?
echo "=== Content of co/en.json (first 30 lines) ==="
head -30 src/assets/i18n/co/en.json

echo ""
echo "=== Content of fyle/en.json (first 30 lines) ==="
head -30 src/assets/i18n/fyle/en.json

echo ""
echo "=== Check if co/en.json has dashboard key ==="
grep -c "dashboard" src/assets/i18n/co/en.json || echo "Not found"

echo ""
echo "=== Check if fyle/en.json has dashboard key ==="
grep -c "dashboard" src/assets/i18n/fyle/en.json || echo "Not found"

echo ""
echo "=== Check directory structure ==="
find src/assets/i18n -type d

Repository: fylein/fyle-integrations-app

Length of output: 4495


🏁 Script executed:

#!/bin/bash
# Check if there are actual language-specific files (not org-specific)
echo "=== Check for language files at root level ==="
find src/assets/i18n -maxdepth 1 -name "*.json" -type f | sort

echo ""
echo "=== Check structure - are these org-specific variants? ==="
for file in src/assets/i18n/*/en.json; do
  echo "File: $file"
  tail -5 "$file"
  echo "---"
done

echo ""
echo "=== Verify: Does qbdDirectRegenerateQwcFile exist in co/en.json? ==="
grep -c "qbdDirectRegenerateQwcFile" src/assets/i18n/co/en.json || echo "0"

echo ""
echo "=== Verify: Does qbdDirectRegenerateQwcFile exist in fyle/en.json? ==="
grep -c "qbdDirectRegenerateQwcFile" src/assets/i18n/fyle/en.json || echo "0"

echo ""
echo "=== Check if QWCFile key exists in qbd_direct.configuration for other files ==="
for file in src/assets/i18n/*/en.json; do
  echo "File: $file"
  grep -A50 '"configuration"' "$file" | grep -c "QWCFile" || echo "  Not found"
done

Repository: fylein/fyle-integrations-app

Length of output: 762


🏁 Script executed:

#!/bin/bash
# Let's check if there's any documentation or configuration that explains the i18n structure
echo "=== Check for any i18n setup files ==="
find . -name "*i18n*" -o -name "*locale*" -o -name "*lang*" | grep -E "\.(ts|js|json|md)$" | head -20

echo ""
echo "=== Check the git log to see if co/ and fyle/ were recent additions ==="
git log --oneline --all -- src/assets/i18n/ | head -10

echo ""
echo "=== Check if there's a main en.json file that includes/extends the org variants ==="
head -5 src/assets/i18n/en.json
tail -5 src/assets/i18n/en.json

echo ""
echo "=== Look for translation/language setup in code ==="
rg -l "i18n|translation" src --type ts --type js 2>/dev/null | head -5

Repository: fylein/fyle-integrations-app

Length of output: 1102


🏁 Script executed:

#!/bin/bash
# Check the transloco-http-loader to understand how these locale files are used
echo "=== Content of transloco-http-loader.ts ==="
cat src/app/transloco-http-loader.ts

echo ""
echo "=== Check if co/en.json and fyle/en.json contain all keys or just overrides ==="
echo "Number of top-level keys in en.json:"
jq 'keys | length' src/assets/i18n/en.json

echo ""
echo "Number of top-level keys in co/en.json:"
jq 'keys | length' src/assets/i18n/co/en.json

echo ""
echo "Number of top-level keys in fyle/en.json:"
jq 'keys | length' src/assets/i18n/fyle/en.json

echo ""
echo "=== Check if co and fyle are just partial overrides or complete locales ==="
echo "Top-level keys in co/en.json:"
jq 'keys' src/assets/i18n/co/en.json

echo ""
echo "Top-level keys in fyle/en.json:"
jq 'keys' src/assets/i18n/fyle/en.json

Repository: fylein/fyle-integrations-app

Length of output: 2806


🏁 Script executed:

#!/bin/bash
# Verify one more thing - check if there are any other language files planned or in use
echo "=== Check if there are other language variants mentioned in code ==="
rg "i18n/(es|fr|de|it|pt|ja|zh)" src --type ts --type js 2>/dev/null | head -10

echo ""
echo "=== Check git history for language files ==="
git log --all --name-only --pretty=format: -- "src/assets/i18n/*.json" | sort -u

echo ""
echo "=== Final verification: Current en.json key order for the two sections ==="
python3 - <<'PY'
import json

with open("src/assets/i18n/en.json") as f:
    data = json.load(f)

print("=== qbd_direct.configuration ===")
config = data["qbd_direct"]["configuration"]
keys = list(config.keys())
print(f"Current order: {keys}")
print(f"Sorted order:  {sorted(keys)}")
print(f"Needs fixing: {keys != sorted(keys)}")

print("\n=== qbdDirectRegenerateQwcFile (showing first 10 keys) ===")
qwc = data["qbdDirectRegenerateQwcFile"]
keys = list(qwc.keys())
print(f"Current (first 10): {keys[:10]}")
sorted_keys = sorted(keys)
print(f"Sorted (first 10):  {sorted_keys[:10]}")
print(f"Needs fixing: {keys != sorted_keys}")
PY

Repository: fylein/fyle-integrations-app

Length of output: 1043


Sort i18n keys alphabetically in the two new sections

The keys in qbd_direct.configuration and qbdDirectRegenerateQwcFile are not alphabetically sorted. Per the coding guidelines, keep child keys alphabetically sorted to reduce merge conflicts.

  • qbd_direct.configuration: Should be advancedSettings, exportSetting, QWCFile (currently: exportSetting, advancedSettings, QWCFile)
  • qbdDirectRegenerateQwcFile: All 23 keys need reordering alphabetically starting with contentText instead of stepName
🤖 Prompt for AI Agents
In `@src/assets/i18n/en.json` around lines 2733 - 2766, Reorder the child keys
alphabetically in the two i18n objects: under qbd_direct.configuration ensure
the three keys are ordered as advancedSettings, exportSetting, QWCFile (instead
of exportSetting, advancedSettings, QWCFile), and under
qbdDirectRegenerateQwcFile sort all 23 keys alphabetically (so contentText
appears before stepName); update the JSON key ordering only (no text changes) to
maintain alphabetical order for merge hygiene.

@JustARatherRidiculouslyLongUsername JustARatherRidiculouslyLongUsername changed the base branch from master to allow-qwc-regeneration February 17, 2026 08:40
Copy link
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: 2

🧹 Nitpick comments (4)
src/app/integrations/qbd-direct/qbd-direct-shared/qbd-direct-qwc-file/qbd-direct-qwc-file-landing/qbd-direct-qwc-file-landing.component.html (2)

145-154: Duplicate @if (!isLoading) — consolidate with the main content block.

This is a second @if (!isLoading) outside the root <div>. Consider moving the footer inside the @else block (per the refactor above) to avoid the duplicated condition check.

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

In
`@src/app/integrations/qbd-direct/qbd-direct-shared/qbd-direct-qwc-file/qbd-direct-qwc-file-landing/qbd-direct-qwc-file-landing.component.html`
around lines 145 - 154, There is a duplicate conditional block checking
isLoading for the footer; remove this extra "@if (!isLoading)" and move the
<app-configuration-step-footer> into the main content's non-loading branch (the
existing else/@if block that renders the page content) so the footer is rendered
once when isLoading is false; ensure you keep the same inputs/outputs
([ctaText]="ConfigurationCtaText.CONTINUE", [showBackButton]="false",
[isButtonDisabled]="regenerateQwcForm.invalid", (continue)="continue()") and
delete the redundant conditional wrapper around the footer to consolidate the
logic.

2-7: Use @else instead of a separate @if (!isLoading) block.

Lines 2 and 7 are mutually exclusive conditions. Using @if/@else is cleaner and avoids evaluating both expressions.

Proposed fix
   `@if` (isLoading) {
     <div class="tw-flex tw-justify-center tw-items-center tw-pt-80-px tw-h-[75vh]">
       <app-loader></app-loader>
     </div>
-  }
-  `@if` (!isLoading) {
+  } `@else` {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/app/integrations/qbd-direct/qbd-direct-shared/qbd-direct-qwc-file/qbd-direct-qwc-file-landing/qbd-direct-qwc-file-landing.component.html`
around lines 2 - 7, Replace the two separate Razor/Angular-like conditionals
that check isLoading with a single if/else to avoid evaluating both branches:
update the template in qbd-direct-qwc-file-landing.component.html so the first
conditional uses `@if` (isLoading) { ... } and the mutually exclusive branch
becomes `@else` { ... } instead of `@if` (!isLoading) { ... }; adjust only the
template markup around the isLoading blocks to use `@else` and keep the existing
loader and content nodes (app-loader and the subsequent content) unchanged.
src/app/integrations/qbd-direct/qbd-direct-shared/qbd-direct-qwc-file/qbd-direct-qwc-file-landing/qbd-direct-qwc-file-landing.component.ts (2)

71-83: Custom group-level validator is over-engineered — use Validators.required on the control.

A simple Validators.required on the FormControl achieves the same effect and is more idiomatic for Angular reactive forms.

Proposed fix
     this.regenerateQwcForm = this.formBuilder.group<RegenerateQwcForm>({
-      nextStep: new FormControl(null)
-    }, {
-      validators: (form) => {
-        const errors: ValidationErrors = {};
-
-        if (form.get('nextStep')?.value === null) {
-          errors.nextStep = { required: true };
-        }
-
-        return Object.keys(errors).length > 0 ? errors : null;
-      }
+      nextStep: new FormControl(null, { validators: [Validators.required] })
     });

Also, ValidationErrors can be removed from the import on Line 2 if no longer used.

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

In
`@src/app/integrations/qbd-direct/qbd-direct-shared/qbd-direct-qwc-file/qbd-direct-qwc-file-landing/qbd-direct-qwc-file-landing.component.ts`
around lines 71 - 83, Replace the custom group-level validator on
regenerateQwcForm with a simple required validator on the nextStep control: when
creating the form via formBuilder.group<RegenerateQwcForm> pass nextStep: new
FormControl(null, Validators.required) and remove the validators:{...} block;
update imports to remove ValidationErrors if it becomes unused. Locate the form
initialization in qbd-direct-qwc-file-landing.component (regenerateQwcForm,
formBuilder.group, RegenerateQwcForm, nextStep) and make these changes so
validation is handled at the control level.

54-56: continue is a JavaScript reserved keyword.

While TypeScript allows reserved words as method names on classes and Angular resolves template bindings by string, using continue as a method name is confusing and could trip up developers or certain tooling. Consider renaming to something like onContinue() or handleContinue().

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

In
`@src/app/integrations/qbd-direct/qbd-direct-shared/qbd-direct-qwc-file/qbd-direct-qwc-file-landing/qbd-direct-qwc-file-landing.component.ts`
around lines 54 - 56, The class method named continue() should be renamed to a
non-reserved identifier (e.g., onContinue() or handleContinue()) to avoid
confusion and tooling issues; update the method declaration in
qbd-direct-qwc-file-landing component (replace continue() with
onContinue()/handleContinue()) and then update all references (Angular
templates, event bindings, unit tests, and any direct calls) to use the new name
so behavior remains identical.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@src/app/integrations/qbd-direct/qbd-direct-shared/qbd-direct-qwc-file/qbd-direct-qwc-file-landing/qbd-direct-qwc-file-landing.component.html`:
- Around line 54-61: The custom radio markup hides the native input
(formControlName="nextStep") with tw-opacity-0 but provides no visible focus
indicator for keyboard users; update the template/CSS so when the input (the
radio bound to NextStepOption.EXISTING / selectedNextStep) receives focus you
show a visible focus ring on the parent container or the custom icon (e.g.,
using input:focus + .custom-icon or input:focus ~ .icon-wrapper classes) and
ensure the focus style is accessible (contrast, visible outline) while
preserving current visuals for mouse users.

In
`@src/app/integrations/qbd-direct/qbd-direct-shared/qbd-direct-qwc-file/qbd-direct-qwc-file-landing/qbd-direct-qwc-file-landing.component.ts`:
- Around line 58-67: The copyQwcFilePath method calls
navigator.clipboard.writeText but does not handle its returned Promise; wrap the
call in proper async handling (make copyQwcFilePath async and await writeText or
attach .then/.catch) and handle failures by catching the rejection and showing
an error toast via this.toastService (and optionally logging the error) while
preserving the existing success toast when the write succeeds; update references
to copyQwcFilePath accordingly.

---

Duplicate comments:
In
`@src/app/integrations/qbd-direct/qbd-direct-shared/qbd-direct-qwc-file/qbd-direct-qwc-file-landing/qbd-direct-qwc-file-landing.component.scss`:
- Around line 4-6: Update the &:hover box-shadow declaration to use modern
color-function notation and remove units on zero values: replace "box-shadow:
0px 4px 4px 0px rgba(44, 48, 78, 0.10);" with "box-shadow: 0 4px 4px 0 rgb(44 48
78 / 0.1);" in qbd-direct-qwc-file-landing.component.scss (target the &:hover
rule).

In
`@src/app/integrations/qbd-direct/qbd-direct-shared/qbd-direct-qwc-file/qbd-direct-qwc-file-landing/qbd-direct-qwc-file-landing.component.ts`:
- Around line 42-44: The getter selectedNextStep currently casts a possibly-null
form control value to NextStepOption; change its signature to return
NextStepOption | null and return the control value without forced casting to
honestly represent nullability (update references to selectedNextStep where
non-null is required to handle the null case or add explicit checks). Locate
selectedNextStep and the regenerateQwcForm.get('nextStep') usage in
qbd-direct-qwc-file-landing.component to apply the type change and adjust any
callsites accordingly.
- Around line 85-88: The subscription to
qbdDirectConnectorService.getQBDConnectorSettings() lacks error handling so
isLoading stays true on failure; update the call (in
qbd-direct-qwc-file-landing.component.ts) to handle errors by either providing
an error callback to subscribe or using pipe(finalize(...)) / catchError, ensure
this.isLoading = false in the error/finalize path, and surface the failure
(e.g., set a user-facing error flag/message or log via console/process logger)
while leaving this.connectorSettings unchanged or cleared as appropriate.

---

Nitpick comments:
In
`@src/app/integrations/qbd-direct/qbd-direct-shared/qbd-direct-qwc-file/qbd-direct-qwc-file-landing/qbd-direct-qwc-file-landing.component.html`:
- Around line 145-154: There is a duplicate conditional block checking isLoading
for the footer; remove this extra "@if (!isLoading)" and move the
<app-configuration-step-footer> into the main content's non-loading branch (the
existing else/@if block that renders the page content) so the footer is rendered
once when isLoading is false; ensure you keep the same inputs/outputs
([ctaText]="ConfigurationCtaText.CONTINUE", [showBackButton]="false",
[isButtonDisabled]="regenerateQwcForm.invalid", (continue)="continue()") and
delete the redundant conditional wrapper around the footer to consolidate the
logic.
- Around line 2-7: Replace the two separate Razor/Angular-like conditionals that
check isLoading with a single if/else to avoid evaluating both branches: update
the template in qbd-direct-qwc-file-landing.component.html so the first
conditional uses `@if` (isLoading) { ... } and the mutually exclusive branch
becomes `@else` { ... } instead of `@if` (!isLoading) { ... }; adjust only the
template markup around the isLoading blocks to use `@else` and keep the existing
loader and content nodes (app-loader and the subsequent content) unchanged.

In
`@src/app/integrations/qbd-direct/qbd-direct-shared/qbd-direct-qwc-file/qbd-direct-qwc-file-landing/qbd-direct-qwc-file-landing.component.ts`:
- Around line 71-83: Replace the custom group-level validator on
regenerateQwcForm with a simple required validator on the nextStep control: when
creating the form via formBuilder.group<RegenerateQwcForm> pass nextStep: new
FormControl(null, Validators.required) and remove the validators:{...} block;
update imports to remove ValidationErrors if it becomes unused. Locate the form
initialization in qbd-direct-qwc-file-landing.component (regenerateQwcForm,
formBuilder.group, RegenerateQwcForm, nextStep) and make these changes so
validation is handled at the control level.
- Around line 54-56: The class method named continue() should be renamed to a
non-reserved identifier (e.g., onContinue() or handleContinue()) to avoid
confusion and tooling issues; update the method declaration in
qbd-direct-qwc-file-landing component (replace continue() with
onContinue()/handleContinue()) and then update all references (Angular
templates, event bindings, unit tests, and any direct calls) to use the new name
so behavior remains identical.

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

Labels

size/L Large PR

Development

Successfully merging this pull request may close these issues.

2 participants