Skip to content

Angular upgrade to v18#574

Merged
uldisrudzitis merged 6 commits intomasterfrom
enh-angular-upgrade-to-v18
Oct 6, 2025
Merged

Angular upgrade to v18#574
uldisrudzitis merged 6 commits intomasterfrom
enh-angular-upgrade-to-v18

Conversation

@uldisrudzitis
Copy link
Copy Markdown
Collaborator

@uldisrudzitis uldisrudzitis commented Oct 5, 2025

Summary by CodeRabbit

  • New Features

    • Bundled Twig templates and a centralized runtime renderer for templates, improving template availability in the UI.
  • Refactor

    • Unified state selection and modernized reactive streams across the app for more consistent behavior.
  • Bug Fixes

    • Added graceful error handling and safe fallbacks for template rendering to prevent UI failures.
  • Chores

    • Upgraded Angular and aligned dependencies; simplified the build with prebuild template steps and removed custom webpack tooling.

@uldisrudzitis uldisrudzitis self-assigned this Oct 5, 2025
@uldisrudzitis uldisrudzitis added Enhancement dependencies Pull requests that update a dependency file labels Oct 5, 2025
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Oct 5, 2025

Walkthrough

Switches the editor build to Angular DevKit v18, removes custom webpack/twig-loader, adds prebuild scripts to copy and bundle backend .twig files into a generated TWIG_TEMPLATES map, and introduces a runtime TwigTemplateRenderService used across many renderers. Also modernizes NGXS selectors, combineLatest usage, HttpClient provisioning, and various import paths.

Changes

Cohort / File(s) Summary
Build & tooling
editor/angular.json, editor/package.json, editor/tsconfig.json
Move to @angular-devkit/build-angular:application; upgrade Angular/tooling to v18; add twig dep; remove webpack/twig-loader/event-hooks-webpack-plugin/webpack dev deps; widen node engine constraints; enable esModuleInterop.
Webpack removal → prebuild scripts
editor/extra-webpack.config.js (removed), editor/copy-twig-templates.js, editor/bundle-twig-templates.js
Delete custom webpack hooks; add scripts to copy backend .twig files into src/templates and to bundle them into a generated TS map.
Bundled templates & runtime engine
editor/src/app/render/twig-templates.ts, editor/src/app/render/twig-template-render.service.ts
Add auto-generated TWIG_TEMPLATES map and a TwigTemplateRenderService that loads, compiles, caches, and renders templates via the global Twig runtime.
Renderer services migrated to Twig service
Multiple editor/src/app/.../render/*.ts, editor/src/app/sites/**, editor/src/app/shop/**
Replace static/in-file Twig imports and direct Template(...) calls with injected twigTemplateRenderService.render(templateKey, viewData) across many render services; add try/catch logging and safe empty-string fallbacks; inject service via constructors.
NGXS selector metadata & signatures
Many editor/src/app/*/*.state.ts
Update @Selector metadata to include owning state slices (e.g., [State, AppState.getSite]), remove unused first parameters in several selector signatures, and adjust selector wiring.
State selection & dispatch patterns
Multiple components/services under editor/src/app/* (app, login, preview, sites, user, etc.)
Replace class-based @Select(State)/selectSnapshot usage with inline selectors (state => state.app/user/sites/...), adapt post-dispatch flows to selectOnce/select(...).pipe(take(1)), and switch many dispatches to dispatch(new Action()) instances.
RxJS modernization
Many components/services
Convert variadic/nested combineLatest(...) usages to the array form combineLatest([ ... ]) and adjust subscription destructuring accordingly.
HttpClient provisioning & AppModule
editor/src/app/app.module.ts
Replace HttpClientModule import with provideHttpClient(withInterceptorsFromDi()) and add bootstrap: [AppComponent].
Minor import/path fixes & misc
Scattered files (e.g., .../shop-regional-costs.component.ts, .../sites-shared.module.ts)
Adjust relative import paths and small refactors; add esModuleInterop to tsconfig.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant Component
  participant RenderSvc as SpecificRenderService
  participant TwigSvc as TwigTemplateRenderService
  participant Templates as TWIG_TEMPLATES
  note over Component,RenderSvc: Runtime Twig rendering replaces static Template imports
  User->>Component: Trigger render()
  Component->>RenderSvc: startRender(viewData)
  RenderSvc->>TwigSvc: render(templateKey, viewData)
  TwigSvc->>Templates: lookup(templateKey)
  alt template found
    TwigSvc->>TwigSvc: compile & cache (Twig.twig)
    TwigSvc-->>RenderSvc: html string
    RenderSvc-->>Component: html string
    Component-->>User: Display HTML
  else missing/failed
    TwigSvc-->>RenderSvc: throw / error
    RenderSvc-->>Component: '' (logged)
    Component-->>User: Fallback display
  end
Loading
sequenceDiagram
  autonumber
  participant NPM as npm run build
  participant Pre as prebuild
  participant Copy as copy-twig-templates.js
  participant Bundle as bundle-twig-templates.js
  participant FS as FileSystem
  participant App as Angular build
  note over NPM,Bundle: New prebuild pipeline replaces webpack twig-loader step
  NPM->>Pre: prebuild
  Pre->>Copy: copy backend .twig -> src/templates
  Copy->>FS: read/write
  Pre->>Bundle: scan src/templates -> emit src/app/render/twig-templates.ts
  Bundle->>FS: write generated TS
  NPM->>App: build with @angular-devkit builder
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Possibly related PRs

  • Angular upgrade to v12 #563 — Changes angular.json, extra-webpack.config.js and package.json; closely related to webpack removal and build migration in this PR.
  • Angular upgrade to v14 #569 — Modifies editor angular.json and builder config; overlaps with build-system adjustments here.
  • Angular upgrade to v17 #573 — Edits angular.json and package.json for Angular/tooling upgrades; related to version and build-schema changes.

Poem

I hopped through templates, bundled them tight,
I swapped webpack thorns for a runtime light.
Twig cached in my burrow, rendering with care,
Errors logged gently — empty string to spare.
Hop, render, display — a rabbit's delight. 🥕

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title “Angular upgrade to v18” clearly states the main goal of this changeset, which is bumping the Angular framework to version 18. It is concise, free of noise, and specific enough for a teammate to understand the primary change without listing every detail. This aligns well with the scope of the PR, given the extensive Angular package and configuration updates present.
✨ 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 enh-angular-upgrade-to-v18

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.

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

Caution

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

⚠️ Outside diff range comments (2)
editor/src/app/shop/settings/shop-settings.state.ts (1)

59-78: Restore the unused state argument to keep selector parameters aligned.

In NGXS, selector handlers still receive the local state slice as the first argument, even when you supply dependencies. By dropping the _ parameter, currentSiteSettings now receives the entire ShopSettingsModel object, so the subsequent .find(...) call will throw (currentSiteSettings.find is not a function). Please reintroduce the leading state parameter (e.g. _state) so the dependency output continues to land in the second slot, or adjust the decorator accordingly.

-  static getCurrentWeightUnit(currentSiteSettings) {
+  static getCurrentWeightUnit(
+    _state: ShopSettingsModel,
+    currentSiteSettings
+  ) {
...
-  static getCurrentCurrency(currentSiteSettings) {
+  static getCurrentCurrency(
+    _state: ShopSettingsModel,
+    currentSiteSettings
+  ) {
editor/src/app/sites/template-settings/site-templates.state.ts (1)

35-44: Fix the selector dependency ordering

Including SiteTemplatesState in the dependency list shifts the argument order so templateSlug now receives the entire state object. The selector then looks up state['[object Object]'], always returning undefined, which breaks every site-template lookup.

-  @Selector([SiteTemplatesState, SiteSettingsState.getCurrentSiteTemplate])
+  @Selector([SiteSettingsState.getCurrentSiteTemplate])
   static getCurrentTemplate(
     state: SiteTemplatesStateModel,
     templateSlug: string
   ) {
🧹 Nitpick comments (6)
editor/src/app/sites/sections/sections-state/site-sections.state.ts (1)

76-76: Add explicit parameter types for consistency.

The parameters state and site lack explicit type annotations, which is inconsistent with the getCurrentSiteSections method (lines 64-66) that was updated with explicit types.

Apply this diff to add explicit types:

-  static getCurrentSiteShopSections(state, site): SiteSectionStateModel[] {
+  static getCurrentSiteShopSections(
+    state: SiteSectionStateModel[],
+    site: string
+  ): SiteSectionStateModel[] {
editor/src/app/shop/products/shop-products.component.ts (1)

54-66: LGTM! combineLatest array form is required for RxJS 7 compatibility.

The update to combineLatest([...]) array syntax is correct for RxJS 7.8.2 (the deprecated multi-argument signature was removed). The explicit type array provides good type safety for the map callback.

Optional improvement: Consider replacing the any type for products (line 65) with a specific interface type for better type safety. However, this appears to be pre-existing technical debt rather than something introduced in this PR.

editor/src/app/sites/site.component.ts (2)

134-143: Consider simplifying the nested switchMap.

The nested switchMap structure is functionally correct but could be simplified by selecting the sites state earlier in the operator chain.

Consider this refactor:

-          switchMap(() =>
-            this.store
-              .select((state) => state.sites)
-              .pipe(
-                take(1),
-                map((sitesState: SiteStateModel[]) =>
-                  sitesState.find((site) => site.order === this.site.order)
-                )
-              )
-          )
+          switchMap(() => this.store.select((state) => state.sites)),
+          take(1),
+          map((sitesState: SiteStateModel[]) =>
+            sitesState.find((site) => site.order === this.site.order)
+          )

167-179: Consider simplifying the nested switchMap.

Similar to the rename flow, this nested switchMap structure could be simplified for better readability.

Consider this refactor:

-            switchMap(() =>
-              this.store
-                .select((state) => state.sites)
-                .pipe(
-                  take(1),
-                  map((sitesState: SiteStateModel[]) => {
-                    return sitesState.find(
-                      (site) => siteNames.indexOf(site.name) === -1
-                    );
-                  })
-                )
-            )
+            switchMap(() => this.store.select((state) => state.sites)),
+            take(1),
+            map((sitesState: SiteStateModel[]) =>
+              sitesState.find((site) => siteNames.indexOf(site.name) === -1)
+            )
editor/copy-twig-templates.js (1)

1-3: Update the outdated comment.

The comment on line 3 references "twig-loader," but according to the PR summary, the webpack twig-loader has been removed in favor of a runtime Twig rendering service. Consider updating the comment to reflect the current approach.

Apply this diff:

 // Angular uses the same twig templates from backend
 // This copy twig templates from backend to frontend
-// so that they can be used in the Angular app via twig-loader
+// so that they can be bundled and used in the Angular app via TwigTemplateRenderService
editor/src/app/sites/sections/entries/galleries/gallery-column-render.service.ts (1)

105-113: Consider error propagation strategy for template rendering failures.

The try/catch blocks correctly handle rendering errors by logging and returning an empty string. However, silently returning empty strings might mask template issues in production.

Consider one of these approaches:

  1. Re-throw errors in development mode to catch issues early
  2. Return a minimal error placeholder in edit mode
  3. Add monitoring/alerting for rendering failures

Example for development-mode error propagation:

} catch (error) {
  console.error('Failed to render template:', error);
  if (!environment.production) {
    throw error; // Fail fast in development
  }
  return '';
}

Also applies to: 130-138

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 450c7c1 and 1f17283.

⛔ Files ignored due to path filters (1)
  • editor/package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (68)
  • editor/angular.json (2 hunks)
  • editor/bundle-twig-templates.js (1 hunks)
  • editor/copy-twig-templates.js (1 hunks)
  • editor/extra-webpack.config.js (0 hunks)
  • editor/package.json (2 hunks)
  • editor/src/app/app-state/app-state.service.ts (2 hunks)
  • editor/src/app/app.component.ts (4 hunks)
  • editor/src/app/app.module.ts (3 hunks)
  • editor/src/app/login/login.component.ts (1 hunks)
  • editor/src/app/preview/preview.component.ts (7 hunks)
  • editor/src/app/preview/preview.service.ts (10 hunks)
  • editor/src/app/preview/style.service.ts (1 hunks)
  • editor/src/app/profile-dropdown/profile-dropdown.component.ts (1 hunks)
  • editor/src/app/render/default-template-render.service.ts (4 hunks)
  • editor/src/app/render/mashup-template-render.service.ts (4 hunks)
  • editor/src/app/render/messy-template-render.service.ts (4 hunks)
  • editor/src/app/render/template-render.service.ts (5 hunks)
  • editor/src/app/render/twig-template-render.service.ts (1 hunks)
  • editor/src/app/render/twig-templates.ts (1 hunks)
  • editor/src/app/render/white-template-render.service.ts (4 hunks)
  • editor/src/app/shop/orders/shop-orders.state.ts (1 hunks)
  • editor/src/app/shop/products/shop-products.component.ts (2 hunks)
  • editor/src/app/shop/products/shop-products.state.ts (1 hunks)
  • editor/src/app/shop/regional-costs/shop-regional-costs.component.ts (1 hunks)
  • editor/src/app/shop/regional-costs/shop-regional-costs.state.ts (1 hunks)
  • editor/src/app/shop/settings/shop-settings.component.ts (1 hunks)
  • editor/src/app/shop/settings/shop-settings.state.ts (2 hunks)
  • editor/src/app/shop/shop-cart-render.service.ts (5 hunks)
  • editor/src/app/shop/shop-state.service.ts (1 hunks)
  • editor/src/app/shop/shop.state.ts (1 hunks)
  • editor/src/app/sites/sections/additional-footer-text-render.service.ts (3 hunks)
  • editor/src/app/sites/sections/additional-text-render.service.ts (3 hunks)
  • editor/src/app/sites/sections/background-gallery-render.service.ts (2 hunks)
  • editor/src/app/sites/sections/entries/entries-state/section-entries.state.ts (1 hunks)
  • editor/src/app/sites/sections/entries/galleries/gallery-column-render.service.ts (2 hunks)
  • editor/src/app/sites/sections/entries/galleries/gallery-link-render.service.ts (2 hunks)
  • editor/src/app/sites/sections/entries/galleries/gallery-pile-render.service.ts (2 hunks)
  • editor/src/app/sites/sections/entries/galleries/gallery-render.service.ts (1 hunks)
  • editor/src/app/sites/sections/entries/galleries/gallery-row-render.service.ts (3 hunks)
  • editor/src/app/sites/sections/entries/galleries/gallery-slideshow-render.service.ts (2 hunks)
  • editor/src/app/sites/sections/entries/mashup-entries-render.service.ts (2 hunks)
  • editor/src/app/sites/sections/entries/portfolio-thumbnails-render.service.ts (2 hunks)
  • editor/src/app/sites/sections/entries/section-entry-render.service.ts (4 hunks)
  • editor/src/app/sites/sections/grid-view-render.service.ts (2 hunks)
  • editor/src/app/sites/sections/section-footer-render.service.ts (2 hunks)
  • editor/src/app/sites/sections/section-head-render.service.ts (2 hunks)
  • editor/src/app/sites/sections/section.component.ts (3 hunks)
  • editor/src/app/sites/sections/sections-menu-render.service.ts (2 hunks)
  • editor/src/app/sites/sections/sections-state/site-sections.state.ts (2 hunks)
  • editor/src/app/sites/sections/site-sections.component.ts (3 hunks)
  • editor/src/app/sites/sections/tags/section-tags.state.ts (1 hunks)
  • editor/src/app/sites/settings/site-settings.component.ts (3 hunks)
  • editor/src/app/sites/settings/site-settings.state.ts (3 hunks)
  • editor/src/app/sites/shared/file-upload.service.ts (1 hunks)
  • editor/src/app/sites/shared/sites-shared.module.ts (1 hunks)
  • editor/src/app/sites/site.component.ts (2 hunks)
  • editor/src/app/sites/sites-banners-render.service.ts (2 hunks)
  • editor/src/app/sites/sites-header-render.service.ts (3 hunks)
  • editor/src/app/sites/sites-menu-render.service.ts (2 hunks)
  • editor/src/app/sites/sites-state/sites.state.ts (2 hunks)
  • editor/src/app/sites/sites.component.ts (1 hunks)
  • editor/src/app/sites/social-media-links-render.service.ts (2 hunks)
  • editor/src/app/sites/template-settings/site-template-settings.component.ts (2 hunks)
  • editor/src/app/sites/template-settings/site-template-settings.state.ts (2 hunks)
  • editor/src/app/sites/template-settings/site-templates.state.ts (3 hunks)
  • editor/src/app/themes/themes.component.ts (1 hunks)
  • editor/src/app/user/user.service.ts (1 hunks)
  • editor/tsconfig.json (1 hunks)
💤 Files with no reviewable changes (1)
  • editor/extra-webpack.config.js
🧰 Additional context used
🧬 Code graph analysis (28)
editor/src/app/themes/themes.component.ts (1)
editor/src/app/app-state/app-state.interface.ts (1)
  • AppStateModel (7-27)
editor/src/app/sites/sections/grid-view-render.service.ts (2)
editor/src/app/render/messy-template-render.service.ts (1)
  • Injectable (25-357)
editor/src/app/render/twig-template-render.service.ts (1)
  • Injectable (6-60)
editor/src/app/login/login.component.ts (1)
editor/src/app/app-state/app-state.interface.ts (1)
  • AppStateModel (7-27)
editor/src/app/sites/sites-state/sites.state.ts (5)
editor/src/app/sites/sections/sections-state/site-sections.actions.ts (1)
  • DeleteSiteSectionsAction (104-107)
editor/src/app/sites/settings/site-settings.actions.ts (1)
  • DeleteSiteSettingsAction (39-42)
editor/src/app/sites/template-settings/site-template-settings.actions.ts (1)
  • DeleteSiteTemplateSettingsAction (36-39)
editor/src/app/sites/sections/tags/section-tags.actions.ts (1)
  • DeleteSiteSectionsTagsAction (44-47)
editor/src/app/sites/sections/entries/entries-state/section-entries.actions.ts (1)
  • DeleteSiteSectionsEntriesAction (45-48)
editor/src/app/shop/shop-cart-render.service.ts (1)
editor/src/app/render/twig-template-render.service.ts (1)
  • Injectable (6-60)
editor/src/app/sites/sections/sections-menu-render.service.ts (4)
editor/src/app/render/default-template-render.service.ts (1)
  • Injectable (23-125)
editor/src/app/render/template-render.service.ts (1)
  • Injectable (34-390)
editor/src/app/shop/shop-cart-render.service.ts (1)
  • Injectable (6-721)
editor/src/app/sites/sites-header-render.service.ts (1)
  • Injectable (5-168)
editor/src/app/preview/preview.component.ts (3)
editor/src/app/user/user.state.ts (1)
  • isLoggedIn (49-51)
editor/src/app/user/user.actions.ts (1)
  • UserLogoutAction (12-15)
editor/src/app/app-state/app.actions.ts (1)
  • AppShowLoading (16-18)
editor/src/app/sites/sections/entries/portfolio-thumbnails-render.service.ts (2)
editor/src/app/render/template-render.service.ts (1)
  • Injectable (34-390)
editor/src/app/render/twig-template-render.service.ts (1)
  • Injectable (6-60)
editor/src/app/sites/template-settings/site-templates.state.ts (1)
editor/src/app/sites/template-settings/site-templates.interface.ts (1)
  • TemplateSiteModel (15-21)
editor/src/app/sites/sites-banners-render.service.ts (2)
editor/src/app/render/template-render.service.ts (1)
  • Injectable (34-390)
editor/src/app/render/twig-template-render.service.ts (1)
  • Injectable (6-60)
editor/src/app/sites/sections/background-gallery-render.service.ts (5)
editor/src/app/render/default-template-render.service.ts (1)
  • Injectable (23-125)
editor/src/app/render/template-render.service.ts (1)
  • Injectable (34-390)
editor/src/app/sites/sections/grid-view-render.service.ts (1)
  • Injectable (7-109)
editor/src/app/sites/sections/sections-menu-render.service.ts (1)
  • Injectable (7-349)
editor/src/app/sites/sites-header-render.service.ts (1)
  • Injectable (5-168)
editor/src/app/sites/sections/section-head-render.service.ts (2)
editor/src/app/render/template-render.service.ts (1)
  • Injectable (34-390)
editor/src/app/render/twig-template-render.service.ts (1)
  • Injectable (6-60)
editor/src/app/sites/site.component.ts (2)
editor/src/app/sites/sites-state/site-state.model.ts (1)
  • SiteStateModel (1-9)
editor/src/app/sites/sites-state/sites.actions.ts (1)
  • CloneSiteAction (22-25)
editor/src/app/sites/sections/section-footer-render.service.ts (2)
editor/src/app/render/template-render.service.ts (1)
  • Injectable (34-390)
editor/src/app/render/twig-template-render.service.ts (1)
  • Injectable (6-60)
editor/src/app/sites/sites.component.ts (2)
editor/src/app/sites/sites-state/site-state.model.ts (1)
  • SiteStateModel (1-9)
editor/src/app/sites/sites-state/sites.actions.ts (1)
  • CreateSiteAction (3-6)
editor/src/app/sites/sections/entries/mashup-entries-render.service.ts (2)
editor/src/app/render/template-render.service.ts (1)
  • Injectable (34-390)
editor/src/app/render/twig-template-render.service.ts (1)
  • Injectable (6-60)
editor/src/app/sites/template-settings/site-template-settings.state.ts (1)
editor/src/app/sites/template-settings/site-template-settings.interface.ts (1)
  • SitesTemplateSettingsStateModel (3-5)
editor/src/app/profile-dropdown/profile-dropdown.component.ts (1)
editor/src/app/user/user.actions.ts (1)
  • UserLogoutAction (12-15)
editor/src/app/sites/sections/sections-state/site-sections.state.ts (1)
editor/src/app/sites/sections/sections-state/site-sections-state.model.ts (1)
  • SiteSectionStateModel (1-37)
editor/src/app/sites/sections/entries/galleries/gallery-render.service.ts (2)
editor/src/app/render/twig-template-render.service.ts (1)
  • Injectable (6-60)
editor/src/app/sites/sections/entries/galleries/gallery-row-render.service.ts (1)
  • Injectable (5-188)
editor/src/app/sites/sections/entries/section-entry-render.service.ts (1)
editor/src/app/shared/helpers.ts (3)
  • toHtmlAttributes (271-285)
  • formatPrice (351-358)
  • toCartAttributes (360-365)
editor/src/app/render/twig-template-render.service.ts (3)
editor/src/app/render/default-template-render.service.ts (1)
  • Injectable (23-125)
editor/src/app/render/template-render.service.ts (1)
  • Injectable (34-390)
editor/src/app/render/twig-templates.ts (1)
  • TWIG_TEMPLATES (4-1500)
editor/src/app/sites/settings/site-settings.component.ts (2)
editor/src/app/user/user.state.model.ts (1)
  • UserStateModel (1-19)
editor/src/app/app-state/app-state.interface.ts (1)
  • AppStateModel (7-27)
editor/src/app/sites/sections/additional-text-render.service.ts (1)
_api_app/app/Sites/SocialMediaLinksRenderService.php (1)
  • SocialMediaLinksRenderService (5-53)
editor/src/app/app.component.ts (2)
editor/src/app/app-state/app.actions.ts (2)
  • AppHideOverlay (12-14)
  • AppShowOverlay (8-10)
editor/src/app/user/user.actions.ts (1)
  • UserLoginAction (1-10)
editor/src/app/sites/social-media-links-render.service.ts (4)
editor/src/app/render/twig-template-render.service.ts (1)
  • Injectable (6-60)
editor/src/app/sites/sections/additional-footer-text-render.service.ts (1)
  • Injectable (8-94)
editor/src/app/sites/sections/additional-text-render.service.ts (1)
  • Injectable (6-145)
editor/src/app/sites/sections/section-render.service.ts (1)
  • Injectable (6-141)
editor/src/app/sites/sites-menu-render.service.ts (1)
editor/src/app/render/twig-template-render.service.ts (1)
  • Injectable (6-60)
editor/src/app/sites/sections/site-sections.component.ts (1)
editor/src/app/sites/sections/sections-state/site-sections.actions.ts (1)
  • CreateSectionAction (4-7)
🔇 Additional comments (50)
editor/src/app/sites/shared/file-upload.service.ts (1)

24-27: LGTM! Modern RxJS syntax adopted.

The migration from variadic combineLatest(obsA, obsB) to array form combineLatest([obsA, obsB]) correctly aligns with RxJS 7.x best practices. The variadic form was deprecated in RxJS 6.5, and this change ensures forward compatibility while maintaining identical behavior.

editor/src/app/sites/sections/section.component.ts (2)

7-7: LGTM! Import paths correctly updated to relative paths.

The transition from absolute to relative import paths aligns with the broader project refactoring and correctly resolves to the intended modules.

Also applies to: 17-17


230-233: LGTM! Correctly updated combineLatest to array syntax for RxJS 7.x.

The refactor from combineLatest(obs1, obs2) to combineLatest([obs1, obs2]) is necessary for RxJS 7.x compatibility, as the multi-argument signature was deprecated. The functional behavior remains identical, and the downstream destructuring in the map operator continues to work correctly.

editor/src/app/sites/sections/sections-state/site-sections.state.ts (2)

63-73: LGTM! Selector decorator correctly updated.

The decorator change from @Selector([AppState.getSite]) to @Selector([SiteSectionsState, AppState.getSite]) correctly declares the state dependency, aligning with NGXS requirements. The explicit parameter typing also improves type safety.


75-75: LGTM! Decorator correctly updated.

The decorator change to include SiteSectionsState is correct and consistent with the NGXS selector pattern.

editor/src/app/shop/products/shop-products.component.ts (2)

1-4: LGTM! Import modernization aligns with Angular 18 and NGXS patterns.

The import changes correctly update to modern patterns:

  • Removed unused isDevMode and tap imports
  • Using Store.select() pattern instead of @Select decorator is a valid NGXS approach

12-14: LGTM! Import paths correctly updated to relative paths.

The change from absolute (src/app/...) to relative (../../...) import paths follows Angular best practices and resolves correctly to the target modules.

editor/src/app/sites/sections/tags/section-tags.state.ts (1)

40-43: Approve Selector Parameter Fix

The updated @Selector([SectionTagsState, AppState.getSite]) now correctly passes the state object then the site key. All existing getCurrentSiteTags calls (store.select/selectSnapshot) remain compatible and will return the expected tag arrays.

editor/src/app/shop/regional-costs/shop-regional-costs.component.ts (1)

6-6: LGTM!

The import path has been correctly updated to use a relative path.

editor/src/app/shop/regional-costs/shop-regional-costs.state.ts (1)

65-71: LGTM! Selector dependency update follows NGXS best practices.

Adding ShopRegionalCostsState as the first dependency ensures the selector properly recomputes when the state slice changes. This change is consistent with the broader selector refactoring pattern across shop state files.

editor/src/app/shop/orders/shop-orders.state.ts (1)

35-38: LGTM! Selector dependency update follows NGXS best practices.

Adding ShopOrdersState as the first dependency ensures proper reactivity and memoization. This change aligns with the selector refactoring pattern across the codebase.

editor/src/app/sites/sections/entries/entries-state/section-entries.state.ts (1)

57-61: LGTM! Selector dependency update follows NGXS best practices.

Adding SectionEntriesState as the first dependency ensures the selector recomputes when the state changes. This is consistent with the broader refactoring pattern in this PR.

editor/src/app/shop/products/shop-products.state.ts (1)

57-63: LGTM! Selector dependency update follows NGXS best practices.

Adding ShopProductsState as the first dependency ensures proper selector reactivity. This change completes the consistent selector refactoring pattern across all shop state files.

editor/src/app/shop/shop.state.ts (1)

70-70: Verify root state and selector usage

  • Ensure AppStateModel declares a top-level sites property.
  • Restore or replace the inline selector with the typed, memoized SitesState.getSites to preserve type safety and performance.
editor/src/app/sites/sites.component.ts (1)

49-49: Approve NGXS async createSite flow
CreateSiteAction handler returns an observable that updates state before completion; component selectOnce then correctly retrieves the new site.

editor/tsconfig.json (1)

7-7: LGTM!

The addition of esModuleInterop: true is appropriate for this upgrade, as it improves interoperability with CommonJS modules used in the new Twig rendering infrastructure.

editor/src/app/themes/themes.component.ts (1)

77-88: LGTM!

The migration to using a state projection (state => state.app) instead of selecting AppState directly is consistent with the broader refactoring pattern in this PR and aligns with NGXS best practices.

editor/src/app/shop/shop-state.service.ts (1)

42-45: LGTM!

The migration to the array form of combineLatest([...]) is correct for RxJS 7+ and aligns with the modern RxJS API conventions used throughout this PR.

editor/src/app/app-state/app-state.service.ts (2)

54-57: LGTM!

The migration to combineLatest([...]) array form is correct and consistent with RxJS 7+ conventions.


137-140: LGTM!

The migration to combineLatest([...]) array form is correct and consistent with RxJS 7+ conventions.

editor/src/app/sites/social-media-links-render.service.ts (2)

2-2: LGTM!

The integration of TwigTemplateRenderService is part of the broader architectural shift to centralized Twig template rendering in this PR.


36-44: LGTM!

The try/catch error handling with fallback to an empty string is a good practice and consistent with the pattern used across other render services in this PR.

editor/angular.json (5)

37-37: LGTM!

The migration to @angular-devkit/build-angular:application is the correct approach for Angular 18, replacing the deprecated custom webpack builder.


45-45: LGTM!

The migration of polyfills from a string to an array is required for the new application builder in Angular 18.


50-51: LGTM!

The addition of twig.min.js as a script and the browser entry point align with the new Twig rendering architecture introduced in this PR.


68-72: LGTM!

The addition of a development configuration with appropriate optimization and source map settings is a good practice for Angular 18 projects.


39-42: Re-run build to confirm empty browser path
The build failed (no editor directory due to ``). Please re-run with the repo cloned and verify ../engine/dist includes the browser output:

cd editor
npm run build
ls -la ../engine/dist/
editor/src/app/login/login.component.ts (1)

124-128: LGTM!

The selector change from AppState to (state) => state.app correctly accesses the app slice of the state and properly types the subscription callback. This aligns with the broader state access refactoring across the PR.

editor/src/app/sites/template-settings/site-template-settings.state.ts (2)

43-57: LGTM!

The selector properly declares dependencies on multiple states (SiteTemplateSettingsState, AppState.getSite, SiteSettingsState.getCurrentSiteTemplate) and correctly types the first parameter as SitesTemplateSettingsStateModel instead of the state class. This follows NGXS best practices for multi-state selectors.


59-73: LGTM!

The addition of optional chaining (currentSiteTemplateSettings?.find) improves null safety and prevents potential runtime errors when currentSiteTemplateSettings is undefined.

editor/src/app/sites/sections/entries/galleries/gallery-slideshow-render.service.ts (1)

159-192: LGTM with same verification as gallery-link-render.service.ts.

The changes correctly migrate to twigTemplateRenderService.render with proper error handling. Since this service also extends GalleryRenderService, the verification script in the gallery-link-render.service.ts review applies here as well.

editor/src/app/preview/preview.service.ts (4)

5-5: LGTM!

The addition of mergeMap import is correctly used at line 106 for the dispatch-then-select pattern in the siteSettings route.


106-157: LGTM!

The changes from direct property access (state.siteSettings) to bracket notation (state['siteSettings']) enable dynamic property access while maintaining type safety through explicit type annotations (line 107). This pattern is consistent with the broader state structure refactoring across the PR.


170-228: LGTM!

The bracket notation changes for state['siteSections'] are consistent with the refactoring pattern.


241-458: LGTM!

The bracket notation changes for state['sectionTags'] and state['sectionEntries'] are consistent with the refactoring pattern throughout the file.

editor/src/app/sites/sections/entries/galleries/gallery-row-render.service.ts (2)

3-3: LGTM!

The import path update for AppStateModel from '../../../../app-state/app-state.interface' aligns with the module structure.


153-186: LGTM with same verification as gallery-link-render.service.ts.

The changes correctly migrate to twigTemplateRenderService.render with proper error handling. Since this service also extends GalleryRenderService, the verification script in the gallery-link-render.service.ts review applies here as well.

editor/src/app/sites/sections/background-gallery-render.service.ts (3)

4-4: LGTM!

The import of TwigTemplateRenderService is correctly added to support the rendering migration.


12-13: LGTM!

The constructor properly injects TwigTemplateRenderService, which is then used in the render method.


150-158: LGTM!

The migration from direct Template invocation to twigTemplateRenderService.render with try/catch error handling is correctly implemented and consistent with the broader PR migration pattern.

editor/src/app/sites/sections/entries/galleries/gallery-link-render.service.ts (1)

121-154: Approve changesGalleryRenderService constructor publicly injects TwigTemplateRenderService, so this.twigTemplateRenderService.render is properly available.

editor/src/app/app.module.ts (2)

3-6: HTTP client migration to DI-based provisioning looks good.

The migration from HttpClientModule to provideHttpClient(withInterceptorsFromDi()) correctly adopts the Angular 18+ DI-based HTTP client pattern. The withInterceptorsFromDi() function ensures backward compatibility with existing class-based interceptors.

Also applies to: 89-89


56-56: No changes needed to bootstrap configuration. The project uses module-based bootstrap in main.ts (platformBrowserDynamic().bootstrapModule(AppModule)), so bootstrap: [AppComponent] in AppModule is expected.

editor/src/app/sites/sections/entries/galleries/gallery-render.service.ts (1)

9-9: LGTM! Proper DI setup for template rendering service.

The public visibility on twigTemplateRenderService correctly allows child render services (e.g., GalleryColumnRenderService, GalleryPileRenderService) to access the template rendering functionality.

editor/src/app/sites/sites-state/sites.state.ts (2)

63-63: LGTM! Selector dependency correction.

Adding SitesState to the selector dependencies ensures proper recomputation when the sites state changes. This aligns with NGXS best practices for selector memoization.


233-239: LGTM! Efficient action batching.

Dispatching the deletion actions as an array is the correct NGXS pattern for batching related state changes, ensuring they're processed together and reducing state change notifications.

editor/src/app/sites/sections/entries/portfolio-thumbnails-render.service.ts (1)

16-16: LGTM! Consistent TwigTemplateRenderService integration.

The migration to TwigTemplateRenderService follows the same pattern as other render services in this PR, with appropriate error handling and fallback behavior.

Also applies to: 104-112

editor/src/app/sites/sections/entries/galleries/gallery-pile-render.service.ts (1)

105-113: LGTM! Consistent template rendering migration.

The migration follows the same pattern as other gallery render services, maintaining the existing conditional logic while adopting the centralized TwigTemplateRenderService.

Also applies to: 130-138

editor/src/app/sites/sites-banners-render.service.ts (1)

9-9: LGTM! Consistent service migration pattern.

The TwigTemplateRenderService integration follows the established pattern across all render services in this PR, with appropriate constructor injection and error handling.

Also applies to: 100-108

editor/src/app/shop/settings/shop-settings.component.ts (1)

58-69: Approve changes Direct state projection of state.shopSettingsConfig matches the NGXS state definition in ShopSettingsConfigState.

Comment thread editor/copy-twig-templates.js Outdated
Comment thread editor/src/app/preview/style.service.ts Outdated

this.store
.select(ShopSettingsConfigState)
.select((state) => state.sitesettingsConfig)
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 | 🔴 Critical

🧩 Analysis chain

Verify the state property name.

The property name sitesettingsConfig (lowercase 's' in settings) appears unusual. Confirm that this matches the actual state property name. If the property is siteSettingsConfig (camelCase), this will cause a runtime error.

Run the following script to verify the correct property name:


🏁 Script executed:

#!/bin/bash
# Search for the state property definition to verify the correct name
rg -n "sitesettingsConfig|siteSettingsConfig" --type=ts -C3

Length of output: 4762


Fix incorrect state property name: In editor/src/app/preview/style.service.ts line 46, change state.sitesettingsConfig to state.siteSettingsConfig to match the registered state.

🤖 Prompt for AI Agents
In editor/src/app/preview/style.service.ts around line 46, the selector
references the wrong state property name; replace state.sitesettingsConfig with
state.siteSettingsConfig (camel-cased S) to match the registered state property
so the selector reads state.siteSettingsConfig.

Comment thread editor/src/app/render/default-template-render.service.ts
Comment on lines +347 to +350
const htmlOutput = this.twigTemplateRenderService.render(
'Sites/Sections/defaultTemplate',
viewData
);
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 | 🔴 Critical

Render the messy Twig template, not the default layout.

MessyTemplateRenderService should render Sites/Sections/messyTemplate. Pointing it at 'Sites/Sections/defaultTemplate' will load the wrong markup, breaking messy layout features (background gallery, floating banners, etc.). Please switch the template key back to the messy template.

-      const htmlOutput = this.twigTemplateRenderService.render(
-        'Sites/Sections/defaultTemplate',
+      const htmlOutput = this.twigTemplateRenderService.render(
+        'Sites/Sections/messyTemplate',
         viewData
       );
📝 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
const htmlOutput = this.twigTemplateRenderService.render(
'Sites/Sections/defaultTemplate',
viewData
);
const htmlOutput = this.twigTemplateRenderService.render(
'Sites/Sections/messyTemplate',
viewData
);
🤖 Prompt for AI Agents
In editor/src/app/render/messy-template-render.service.ts around lines 347 to
350, the service is mistakenly rendering 'Sites/Sections/defaultTemplate'
instead of the messy template; change the template key passed to
this.twigTemplateRenderService.render from 'Sites/Sections/defaultTemplate' to
'Sites/Sections/messyTemplate' so the messy layout (background gallery, floating
banners, etc.) is used, keeping the existing viewData argument unchanged.

Comment on lines +117 to +126
try {
const htmlOutput = this.twigTemplateRenderService.render(
'Sites/Sections/whiteTemplate',
viewData
);
this.replaceIframeContent(contentWindow, htmlOutput);
} catch (error) {
console.error('Failed to render template:', error);
return '';
}
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 | 🔴 Critical

Don't return a string from startRender()

TemplateRenderService.startRender has a void signature. Returning '' in the override widens it to string | void, which TypeScript treats as incompatible and fails the build. Just log the error, optionally clear the iframe, and exit without a value.

     } catch (error) {
       console.error('Failed to render template:', error);
-      return '';
+      this.replaceIframeContent(contentWindow, '');
+      return;
     }
📝 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
try {
const htmlOutput = this.twigTemplateRenderService.render(
'Sites/Sections/whiteTemplate',
viewData
);
this.replaceIframeContent(contentWindow, htmlOutput);
} catch (error) {
console.error('Failed to render template:', error);
return '';
}
try {
const htmlOutput = this.twigTemplateRenderService.render(
'Sites/Sections/whiteTemplate',
viewData
);
this.replaceIframeContent(contentWindow, htmlOutput);
} catch (error) {
console.error('Failed to render template:', error);
this.replaceIframeContent(contentWindow, '');
return;
}
🤖 Prompt for AI Agents
In editor/src/app/render/white-template-render.service.ts around lines 117 to
126, the catch block currently returns an empty string which widens the method
return type and breaks the build; remove the returned string and simply handle
the error by logging it and optionally clearing the iframe content (call
replaceIframeContent(contentWindow, '') or similar) before exiting the function
without a value so the method remains void-compatible.

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: 0

Caution

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

⚠️ Outside diff range comments (3)
editor/src/app/sites/site.component.ts (1)

122-146: Fix: prevent leak and wrong comparison after rename

  • Using this.site.name after dispatch likely compares against the new name, not the old; the filter may never pass.
  • filter + take(1) on queryParams can hang indefinitely if the condition is false, leaking a subscription.
  • Also, find(...) can return undefined; guard before navigating.

Apply this diff:

   updateField(field: string, value: string) {
     if (field === 'name') {
-      this.store
+      const prevName = this.site.name;
+      this.store
         .dispatch(new RenameSiteAction(this.site, value))
         .pipe(
-          switchMap((state) => {
-            return this.route.queryParams.pipe(
-              take(1),
-              filter((params) => params.site && params.site === this.site.name),
-              map(() => state)
-            );
-          }),
-          switchMap(() => this.store.select((state) => state.sites)),
-          take(1),
+          // Only update URL if the currently viewed site was the one renamed
+          filter(() => this.route.snapshot.queryParams.site === prevName),
+          switchMap(() =>
+            this.store.select((state) => state.sites).pipe(take(1))
+          ),
           map((sitesState: SiteStateModel[]) =>
             sitesState.find((site) => site.order === this.site.order)
           )
         )
-        .subscribe((renamedSite) => {
-          this.router.navigate([], {
+        .subscribe((renamedSite) => {
+          if (!renamedSite) {
+            return;
+          }
+          this.router.navigate([], {
             replaceUrl: true,
             queryParams: { site: renamedSite.name },
             queryParamsHandling: 'merge',
           });
         });
editor/src/app/sites/sections/sections-state/site-sections.state.ts (1)

75-84: Critical: Fix selector deps and null-guard @attributes; compose for memoization

Same dependency issue here. Also section['@attributes'].type will throw when @attributes is undefined. Compose from getCurrentSiteSections and guard access.

Apply:

-  @Selector([SiteSectionsState, AppState.getSite])
-  static getCurrentSiteShopSections(
-    state: SiteSectionStateModel[],
-    site: string
-  ): SiteSectionStateModel[] {
-    return SiteSectionsState.getCurrentSiteSections(state, site).filter(
-      (section) =>
-        section['@attributes'].type && section['@attributes'].type === 'shop'
-    );
-  }
+  @Selector([SiteSectionsState.getCurrentSiteSections])
+  static getCurrentSiteShopSections(
+    sections: SiteSectionStateModel[]
+  ): SiteSectionStateModel[] {
+    return sections.filter((section) => section['@attributes']?.type === 'shop');
+  }
editor/src/app/render/mashup-template-render.service.ts (1)

90-101: Fix return type to match possible null values.

getMainColumnAttributes returns nulls but declares string-only fields. This breaks under strictNullChecks.

Use nullable types:

-  getMainColumnAttributes(siteTemplateSettings): {
-    class: string;
-    'data-paddingtop': string;
-  } {
+  getMainColumnAttributes(siteTemplateSettings): {
+    class: string | null;
+    'data-paddingtop': string | null;
+  } {

Alternatively, omit keys when not set and mark them optional.

♻️ Duplicate comments (1)
editor/bundle-twig-templates.js (1)

41-44: Backslash escaping fix looks good.

Escaping backslashes before other characters prevents JS template literal escape interpretation and preserves Twig content. Thanks for addressing this.

🧹 Nitpick comments (7)
editor/src/app/preview/style.service.ts (2)

13-16: Remove unused import.

The ShopSettingsConfigState import is no longer used after switching to the projection function on line 46.

Apply this diff to remove the unused import:

 import {
   ShopSettingsConfigModel,
-  ShopSettingsConfigState,
 } from '../shop/settings/shop-settings-config.state';

45-50: Use typed selector for shopSettingsConfig or remove unused import

The state.shopSettingsConfig projection matches the registered state name but bypasses compile‐time checks; revert to the typed selector (ShopSettingsConfigState…) for type safety and consistency, or drop the now-unused ShopSettingsConfigState import.

editor/src/app/sites/site.component.ts (1)

152-166: Minor: use Set for membership check in clone flow

Replace array lookup with a Set for O(1) membership and simpler intent.

   cloneSite() {
     this.store
       .select((state) => state.sites)
       .pipe(
         take(1),
-        map((sites: SiteStateModel[]) => {
-          return sites.map((site) => site.name);
-        }),
-        switchMap((siteNames) =>
+        map((sites: SiteStateModel[]) => new Set(sites.map((s) => s.name))),
+        switchMap((siteNames) =>
           this.store.dispatch(new CloneSiteAction(this.site)).pipe(
-            switchMap(() => this.store.select((state) => state.sites)),
-            take(1),
-            map((sitesState: SiteStateModel[]) =>
-              sitesState.find((site) => siteNames.indexOf(site.name) === -1)
-            )
+            switchMap(() => this.store.select((state) => state.sites)),
+            take(1),
+            map((sitesState: SiteStateModel[]) =>
+              sitesState.find((site) => !siteNames.has(site.name))
+            )
           )
         )
       )
editor/src/app/render/twig-templates.ts (1)

1-2: Avoid embedding a timestamp to keep the generated file deterministic.

The "Generated at" line will change every run and create noisy diffs. Prefer stable output for VCS hygiene.

Adjust the generator (editor/bundle-twig-templates.js) to omit or gate the timestamp so this file doesn't churn on every build.

editor/bundle-twig-templates.js (1)

34-36: Make the generator reproducible and robust (no timestamp, sorted, ensure output dir).

  • Remove timestamp from header for deterministic diffs.
  • Sort templates by name to avoid churn from FS ordering.
  • Ensure output directory exists before write.

Apply this diff:

-const templates = findTwigFiles(TWIG_DIR);
+const templates = findTwigFiles(TWIG_DIR).sort((a, b) => a.name.localeCompare(b.name));
 
-const output = `// Auto-generated file - do not edit manually
-// Generated at: ${new Date().toISOString()}
+const output = `// Auto-generated file - do not edit manually
+
 export const TWIG_TEMPLATES: Record<string, string> = {
 ${templates
   .map(
     (t) =>
       `  '${t.name}': \`${t.content
         .replace(/\\/g, "\\\\")
         .replace(/`/g, "\\`")
         .replace(/\$/g, "\\$")}\``
   )
   .join(",\n")}
 };
 `;
 
-fs.writeFileSync(OUTPUT_FILE, output, "utf8");
+fs.mkdirSync(path.dirname(OUTPUT_FILE), { recursive: true });
+fs.writeFileSync(OUTPUT_FILE, output, "utf8");

Optionally, if you still want timestamps for logging, print them only to stdout.



Also applies to: 32-33, 50-50

</blockquote></details>
<details>
<summary>editor/src/app/render/mashup-template-render.service.ts (1)</summary><blockquote>

`166-174`: **Optional: clear iframe on render error.**

For a consistent fallback, clear the iframe when rendering fails.

```diff
   } catch (error) {
     console.error('Failed to render template:', error);
+    this.replaceIframeContent(contentWindow, '');
   }
editor/src/app/render/default-template-render.service.ts (1)

114-122: Approved—startRender correctly returns void. Optionally clear the iframe on error for consistency:

} catch (error) {
  console.error('Failed to render template:', error);
+ this.replaceIframeContent(contentWindow, '');
}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1f17283 and c2a2a26.

📒 Files selected for processing (10)
  • editor/bundle-twig-templates.js (1 hunks)
  • editor/copy-twig-templates.js (1 hunks)
  • editor/src/app/preview/style.service.ts (1 hunks)
  • editor/src/app/render/default-template-render.service.ts (4 hunks)
  • editor/src/app/render/mashup-template-render.service.ts (4 hunks)
  • editor/src/app/render/messy-template-render.service.ts (4 hunks)
  • editor/src/app/render/twig-templates.ts (1 hunks)
  • editor/src/app/render/white-template-render.service.ts (4 hunks)
  • editor/src/app/sites/sections/sections-state/site-sections.state.ts (2 hunks)
  • editor/src/app/sites/site.component.ts (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • editor/src/app/render/messy-template-render.service.ts
  • editor/copy-twig-templates.js
  • editor/src/app/render/white-template-render.service.ts
🧰 Additional context used
🧬 Code graph analysis (2)
editor/src/app/sites/site.component.ts (2)
editor/src/app/sites/sites-state/site-state.model.ts (1)
  • SiteStateModel (1-9)
editor/src/app/sites/sites-state/sites.actions.ts (1)
  • CloneSiteAction (22-25)
editor/src/app/sites/sections/sections-state/site-sections.state.ts (1)
editor/src/app/sites/sections/sections-state/site-sections-state.model.ts (1)
  • SiteSectionStateModel (1-37)
🔇 Additional comments (3)
editor/src/app/sites/sections/sections-state/site-sections.state.ts (2)

15-15: assignByPath export and import path verified
Helper assignByPath is exported from src/app/shared/helpers.ts and used consistently; no further action required.


63-73: No changes needed to @selector dependencies
In NGXS, specifying selectors overrides the implicit state, so you must include SiteSectionsState alongside AppState.getSite to receive both values.

Likely an incorrect or invalid review comment.

editor/bundle-twig-templates.js (1)

1-9: Sanity-check for lingering Twig usage
Manually ensure there are no twig-loader imports, .twig file references, new Template(...), or render(Template...) calls remaining in the codebase.

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: 2

🧹 Nitpick comments (2)
editor/copy-twig-templates.js (1)

7-9: Consider adding error handling for better diagnostics.

While build scripts typically allow errors to propagate, wrapping the cpx.copySync call in a try/catch block with a descriptive error message would improve debuggability when the source directory is missing or permission issues occur.

Apply this diff to add error handling:

+try {
   cpx.copySync("../_api_app/app/**/*.twig", "src/templates", {
     clean: true,
   });
-  console.log("✓ Twig files copied from backend to Angular app.");
+  console.log("✓ Twig files copied from backend to Angular app.");
+} catch (error) {
+  console.error("✗ Failed to copy Twig files:", error.message);
+  process.exit(1);
+}
editor/bundle-twig-templates.js (1)

10-30: Consider adding error handling for file system operations.

The script performs multiple file system operations (fs.readdirSync, fs.statSync, fs.readFileSync) without error handling. Adding try/catch blocks with descriptive error messages would help diagnose issues during the build process.

Apply this diff to add error handling:

 function findTwigFiles(dir, fileList = [], baseDir = dir) {
+  try {
     const files = fs.readdirSync(dir);
 
     files.forEach((file) => {
       const filePath = path.join(dir, file);
       const stat = fs.statSync(filePath);
 
       if (stat.isDirectory()) {
         findTwigFiles(filePath, fileList, baseDir);
       } else if (file.endsWith(".twig")) {
         const relativePath = path.relative(baseDir, filePath);
         const content = fs.readFileSync(filePath, "utf8");
         const templateName = relativePath
           .replace(/\.twig$/, "")
           .replace(/\\/g, "/");
         fileList.push({ name: templateName, content });
       }
     });
+  } catch (error) {
+    console.error(`Error processing directory ${dir}:`, error.message);
+    throw error;
+  }
 
   return fileList;
 }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c2a2a26 and 168c6a5.

📒 Files selected for processing (6)
  • editor/bundle-twig-templates.js (1 hunks)
  • editor/copy-twig-templates.js (1 hunks)
  • editor/src/app/preview/style.service.ts (2 hunks)
  • editor/src/app/render/mashup-template-render.service.ts (5 hunks)
  • editor/src/app/render/twig-templates.ts (1 hunks)
  • editor/src/app/sites/sections/sections-state/site-sections.state.ts (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • editor/src/app/preview/style.service.ts
🧰 Additional context used
🧬 Code graph analysis (1)
editor/src/app/sites/sections/sections-state/site-sections.state.ts (1)
editor/src/app/sites/sections/sections-state/site-sections-state.model.ts (1)
  • SiteSectionStateModel (1-37)
🔇 Additional comments (6)
editor/src/app/sites/sections/sections-state/site-sections.state.ts (3)

15-15: LGTM! Import path correctly updated to relative path.

The import path change from absolute to relative is correct for the file location and aligns with Angular best practices.


63-73: LGTM! Selector properly modernized with explicit state dependency.

The decorator now correctly includes SiteSectionsState as the first dependency, which is the proper NGXS pattern for selectors that need access to their own state. The added explicit typing improves type safety.


75-82: Excellent selector composition! Architectural improvement.

The refactored selector now properly composes from getCurrentSiteSections, eliminating logic duplication and improving maintainability. Key benefits:

  • DRY principle: Reuses site filtering logic instead of reimplementing it
  • Efficiency: Operates on pre-filtered sections rather than the entire state
  • Maintainability: Changes to getCurrentSiteSections automatically propagate
  • Clarity: Explicit dependency chain shows this selector builds on current site sections

The optional chaining for @attributes is good defensive programming.

editor/src/app/render/twig-templates.ts (1)

1-1499: LGTM! Auto-generated file.

This auto-generated file correctly exports the centralized Twig template registry consumed by TwigTemplateRenderService across the application.

editor/src/app/render/mashup-template-render.service.ts (2)

21-21: LGTM! Consistent with new Twig rendering architecture.

The addition of TwigTemplateRenderService dependency aligns with the project-wide migration to centralized Twig template rendering.

Also applies to: 46-46, 67-67


90-93: LGTM! More accurate return type.

The updated return type correctly reflects that class and data-paddingtop can be null, matching the implementation's actual behavior (lines 96, 100).

Comment on lines +40 to +44
` '${t.name}': \`${t.content
.replace(/\\/g, "\\\\")
.replace(/`/g, "\\`")
.replace(/\$/g, "\\$")}\``
)
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

Escape single quotes in template names to prevent syntax errors.

The template name t.name is embedded directly in single quotes without escaping. If a .twig file contains a single quote in its path (e.g., test's-template.twig), the generated TypeScript will have a syntax error.

Apply this diff to fix the issue:

       `  '${t.name}': \`${t.content
+        .replace(/'/g, "\\'")
         .replace(/\\/g, "\\\\")
         .replace(/`/g, "\\`")
         .replace(/\$/g, "\\$")}\``

Actually, better approach - use template name escaping:

-      `  '${t.name}': \`${t.content
+      `  '${t.name.replace(/'/g, "\\'")}': \`${t.content
         .replace(/\\/g, "\\\\")
         .replace(/`/g, "\\`")
         .replace(/\$/g, "\\$")}\``
📝 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
` '${t.name}': \`${t.content
.replace(/\\/g, "\\\\")
.replace(/`/g, "\\`")
.replace(/\$/g, "\\$")}\``
)
// …
` '${t.name.replace(/'/g, "\\'")}': \`${t.content
.replace(/\\/g, "\\\\")
.replace(/`/g, "\\`")
.replace(/\$/g, "\\$")}\``
// …
🤖 Prompt for AI Agents
In editor/bundle-twig-templates.js around lines 40-44, the template name t.name
is interpolated into single quotes without escaping which breaks generated
TypeScript when names contain a single quote; update the generation to escape
single quotes in t.name before embedding (e.g., replace all "'" with "\\'") so
the produced object key remains syntactically valid, or alternatively switch to
a safe quoting strategy such as using JSON.stringify for the key to ensure all
necessary escapes are handled.

Comment on lines +166 to +174
try {
const htmlOutput = this.twigTemplateRenderService.render(
'Sites/Sections/mashupTemplate',
viewData
);
this.replaceIframeContent(contentWindow, htmlOutput);
} catch (error) {
console.error('Failed to render template:', error);
}
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

Silent failure leaves user with blank page.

The error is caught and logged but the method doesn't throw, return an error, or update the UI. If template rendering fails, the user sees a blank iframe with only a console error. Consider either throwing the error to let callers handle it, or calling replaceIframeContent with an error message.

Apply this diff to improve error handling:

   try {
     const htmlOutput = this.twigTemplateRenderService.render(
       'Sites/Sections/mashupTemplate',
       viewData
     );
     this.replaceIframeContent(contentWindow, htmlOutput);
   } catch (error) {
     console.error('Failed to render template:', error);
+    // Display error message to user instead of leaving blank page
+    this.replaceIframeContent(
+      contentWindow,
+      '<div style="padding: 20px; color: red;">Failed to render template. Check console for details.</div>'
+    );
   }
📝 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
try {
const htmlOutput = this.twigTemplateRenderService.render(
'Sites/Sections/mashupTemplate',
viewData
);
this.replaceIframeContent(contentWindow, htmlOutput);
} catch (error) {
console.error('Failed to render template:', error);
}
try {
const htmlOutput = this.twigTemplateRenderService.render(
'Sites/Sections/mashupTemplate',
viewData
);
this.replaceIframeContent(contentWindow, htmlOutput);
} catch (error) {
console.error('Failed to render template:', error);
// Display error message to user instead of leaving blank page
this.replaceIframeContent(
contentWindow,
'<div style="padding: 20px; color: red;">Failed to render template. Check console for details.</div>'
);
}
🤖 Prompt for AI Agents
In editor/src/app/render/mashup-template-render.service.ts around lines 166 to
174, the catch block currently only logs the render error which leaves the
iframe blank; update the catch to call this.replaceIframeContent(contentWindow,
errorHtml) with a simple user-facing error HTML (message + optional details) so
the iframe shows an explanatory error, then rethrow the error (or return a
rejected Promise) so upstream callers can handle it; ensure the errorHtml is
safe/escaped and keep the console.error for diagnostics.

@uldisrudzitis uldisrudzitis merged commit 658c97f into master Oct 6, 2025
3 checks passed
@coderabbitai coderabbitai bot mentioned this pull request Oct 6, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Pull requests that update a dependency file Enhancement

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant