Skip to content

Templates#84

Merged
TatevikGr merged 5 commits intodevfrom
feat/templates
Apr 23, 2026
Merged

Templates#84
TatevikGr merged 5 commits intodevfrom
feat/templates

Conversation

@TatevikGr
Copy link
Copy Markdown
Contributor

@TatevikGr TatevikGr commented Apr 21, 2026

Summary by CodeRabbit

  • New Features

    • Templates section: listing, create and edit pages; responsive template library with add-from-default, preview, edit, and delete flows; full template editor with save, create, update, and derive-text utility.
  • UI Changes

    • Updated action buttons and icons across dashboard, lists, campaigns, subscribers and subscriber list views; renamed headings and adjusted navigation targets.
  • Chore

    • Internal dependency updated.

Summary

Provide a general description of the code changes in your pull request …
were there any bugs you had fixed? If so, mention them. If these bugs have open
GitHub issues, be sure to tag them here as well, to keep the conversation
linked together.

Unit test

Are your changes covered with unit tests, and do they not break anything?

You can run the existing unit tests using this command:

vendor/bin/phpunit tests/

Code style

Have you checked that you code is well-documented and follows the PSR-2 coding
style?

You can check for this using this command:

vendor/bin/phpcs --standard=PSR2 src/ tests/

Other Information

If there's anything else that's important and relevant to your pull
request, mention that information here. This could include benchmarks,
or other information.

If you are updating any of the CHANGELOG files or are asked to update the
CHANGELOG files by reviewers, please add the CHANGELOG entry at the top of the file.

Thanks for contributing to phpList!

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 21, 2026

Warning

Rate limit exceeded

@TatevikGr has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 42 minutes and 34 seconds before requesting another review.

Your organization is not enrolled in usage-based pricing. Contact your admin to enable usage-based pricing to continue reviews beyond the rate limit, or try again in 42 minutes and 34 seconds.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 5d3acf7e-25ce-450e-a03a-85d3d38d70a0

📥 Commits

Reviewing files that changed from the base of the PR and between 5763fbb and fc2c28b.

📒 Files selected for processing (3)
  • assets/vue/components/templates/TemplateLibrary.vue
  • assets/vue/views/TemplateEditView.vue
  • src/Controller/TemplatesController.php
📝 Walkthrough

Walkthrough

Adds a template management feature: new Vue Router routes for listing, creating, and editing templates; three frontend components (TemplatesView, TemplateLibrary, TemplateEditView) that load, create, update, and delete templates via the template API client; a Symfony TemplatesController that serves the SPA for the template routes and injects api context; a small package version bump for the REST API client; and several UI adjustments (icons, action button layouts) across components.

Sequence Diagram(s)

sequenceDiagram
    actor User
    participant Browser as Browser
    participant Router as Vue Router
    participant TemplatesView as TemplatesView
    participant Library as TemplateLibrary
    participant APIClient as Template API Client
    participant Backend as Backend API

    User->>Browser: Navigate to /templates
    Browser->>Router: route /templates
    Router->>TemplatesView: render
    TemplatesView->>Library: mount
    Library->>Library: isLoading = true
    Library->>APIClient: getTemplates(0,1000)
    APIClient->>Backend: GET /templates
    Backend-->>APIClient: template list
    APIClient-->>Library: return templates
    Library->>Library: sort & set templates
    Library->>TemplatesView: render cards
    TemplatesView-->>User: display template library
Loading
sequenceDiagram
    actor User
    participant Browser as Browser
    participant Router as Vue Router
    participant EditView as TemplateEditView
    participant APIClient as Template API Client
    participant Backend as Backend API

    User->>Browser: Navigate to /templates/:id/edit
    Browser->>Router: route /templates/:id/edit
    Router->>EditView: render
    EditView->>EditView: extract templateId, isLoading = true
    EditView->>APIClient: getTemplate(templateId)
    APIClient->>Backend: GET /templates/:id
    Backend-->>APIClient: template data
    APIClient-->>EditView: return template
    EditView->>EditView: populate form, isLoading = false
    User->>EditView: submit form
    EditView->>APIClient: updateTemplate(request, templateId)
    APIClient->>Backend: PUT /templates/:id
    Backend-->>APIClient: success
    APIClient-->>EditView: return result
    EditView->>User: show save success
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • ListsController #79 — Modifies router configuration and adds view imports; touches the same router file and route setup.
  • Feat: campaigns #81 — Adds a templateClient export in assets/vue/api.js, which the new TemplateLibrary and TemplateEditView depend on.
🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'Templates' is vague and generic, failing to convey the specific nature of changes across router setup, new views, components, controllers, and UI refinements. Consider a more descriptive title like 'Add template management feature with CRUD operations' to better summarize the changeset scope.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/templates

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

@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: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@assets/vue/components/templates/TemplateLibrary.vue`:
- Around line 38-43: The template rendering accesses
templateItem.images[0].mimetype directly which throws when images is an empty
array; update getTemplateImage(templateItem) (and any other places around the
same component block referenced at lines 101-105) to guard against missing/empty
images by first checking templateItem.images && templateItem.images.length > 0
(or using optional chaining) before accessing images[0], and return a safe
fallback (e.g., a placeholder image URL or null) when no preview image exists so
the library render won't break.
- Around line 6-15: The "New Template" button (and the similar inactive buttons
around the component) are interactive but have no click handlers—either wire
them to real handlers or disable them until implemented; to quickly fix, add the
disabled attribute and aria-disabled plus a visual disabled style to the "New
Template" <button> and the other inactive buttons (apply to the buttons at the
other ranges noted) or alternatively bind them to placeholder methods like
createTemplate/copyTemplate/deleteTemplate that emit a TODO event or open a "Not
implemented" toast; update class names to include a disabled state (e.g., remove
hover:bg-ext-wf3 and add opacity-50 cursor-not-allowed) so users get correct
affordance.

In `@assets/vue/views/TemplateEditView.vue`:
- Around line 177-189: The plain-text generator in populateTextFromContent
currently strips tags but leaves HTML entities (e.g., &amp;, &nbsp;) intact;
after the existing tag/style/script stripping and trimming (in
populateTextFromContent operating on form.value.content and setting
form.value.text) decode HTML entities by using the browser DOM decode approach
(e.g., create a temporary element, set its innerHTML to the intermediate string
and read .textContent) or an equivalent HTML-entity decoder, then assign the
decoded result to form.value.text so entities are converted to their correct
characters.

In `@src/Controller/TemplatesController.php`:
- Around line 15-22: In the index(Request $request) action (the controller
method named index for the #[Route('/', name: 'templates', ...)]), change the
hard-coded page label passed to the template: replace the 'page' => 'Campaigns'
entry with the correct templates page label (e.g. 'Templates' or the appropriate
i18n key) so the /templates route renders the correct SPA title.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: e71d3c1e-c314-4329-a42d-3cc1d562866d

📥 Commits

Reviewing files that changed from the base of the PR and between 1f9eb8a and 30e3413.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (6)
  • assets/router/index.js
  • assets/vue/components/templates/TemplateLibrary.vue
  • assets/vue/views/TemplateEditView.vue
  • assets/vue/views/TemplatesView.vue
  • package.json
  • src/Controller/TemplatesController.php

Comment thread assets/vue/components/templates/TemplateLibrary.vue
Comment thread assets/vue/components/templates/TemplateLibrary.vue
Comment thread assets/vue/views/TemplateEditView.vue
Comment thread src/Controller/TemplatesController.php
Copy link
Copy Markdown

@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)
assets/vue/components/campaigns/CampaignDirectory.vue (1)

110-119: Heads-up: "Requeue" button shows for any non-draft, non-active status.

Not introduced by this PR (this is styling-only), but while you're in here: the v-else on lines 110/250 means campaigns with statusKey === 'sent' or 'unknown' also get a Requeue button. On sent rows that sits next to "Copy to draft", which looks a bit odd with the new prominent icon+label styling. Might be worth gating it with v-else-if="campaign.statusKey === 'sent'" (or excluding 'unknown') depending on intended UX. Feel free to ignore if that's the intended behavior.

Also applies to: 250-259

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

In `@assets/vue/components/campaigns/CampaignDirectory.vue` around lines 110 -
119, The Requeue button is currently shown for any non-draft, non-active status
because it uses a broad v-else; update the template so the button is only
rendered for the intended statuses by replacing the ambiguous v-else with a
conditional check on campaign.statusKey (e.g., use v-else-if="campaign.statusKey
=== 'sent'" or v-else-if="campaign.statusKey !== 'unknown'" depending on desired
UX) wherever the button appears (the blocks that trigger
handleRequeue(campaign.id) and
:disabled="isActionLoading(campaign.id)"—including the other occurrence around
lines 250-259) so only the correct statuses render the Requeue button.
assets/vue/views/ListSubscribersView.vue (1)

76-84: start (play-triangle) icon on "Move Selected" feels off-theme.

A "play" icon on a button that moves subscribers between lists is a bit of a semantic mismatch — users typically read that glyph as start/run. Something like an arrow-right, swap, or move icon would communicate intent better. Not blocking, just a nitpick for icon vocabulary consistency across the app.

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

In `@assets/vue/views/ListSubscribersView.vue` around lines 76 - 84, The BaseIcon
used in the Move Selected button currently passes name="start" which shows a
play-triangle and is semantically misleading; change the icon to a more
appropriate glyph (e.g., "arrow-right", "swap" or "move") by updating the
BaseIcon name in the button that calls moveSelectedSubscribers so the visual
matches the action and app icon vocabulary. Locate the button component that
contains BaseIcon name="start" (the Move Selected button bound to
moveSelectedSubscribers and disabled by
hasSelection/selectedTargetListId/actionLoading) and replace the name prop with
the chosen icon name across the component/template.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@assets/vue/components/base/BaseIcon.vue`:
- Around line 95-97: The new SVG entries for the pause and start icons in
BaseIcon.vue are inconsistent: remove the unsupported rx attribute from the
start icon's <polygon> (or replace the polygon with a <path> if you want rounded
tips) and normalize stroke-width to match the registry (use stroke-width="2" for
both the pause and start entries); optionally add the same xmlns and
class="lucide …" attributes present on other icons for consistency. Ensure you
update the 'start' and 'pause' string values accordingly and keep
stroke-linejoin/stroke-linecap as-is if you rely on rounded joins.

In `@assets/vue/views/TemplateEditView.vue`:
- Line 153: The component currently calls loadTemplate() only inside onMounted,
which leaves data stale when the route is reused; replace that pattern by
importing and using Vue's watch to observe isCreateMode and templateId (or add a
watch([isCreateMode, templateId], () => loadTemplate(), { immediate: true })) so
loadTemplate() runs on initial mount and whenever the route params/mode change;
update the import line to include watch from 'vue' and remove or stop relying
solely on onMounted for this behavior.

---

Nitpick comments:
In `@assets/vue/components/campaigns/CampaignDirectory.vue`:
- Around line 110-119: The Requeue button is currently shown for any non-draft,
non-active status because it uses a broad v-else; update the template so the
button is only rendered for the intended statuses by replacing the ambiguous
v-else with a conditional check on campaign.statusKey (e.g., use
v-else-if="campaign.statusKey === 'sent'" or v-else-if="campaign.statusKey !==
'unknown'" depending on desired UX) wherever the button appears (the blocks that
trigger handleRequeue(campaign.id) and
:disabled="isActionLoading(campaign.id)"—including the other occurrence around
lines 250-259) so only the correct statuses render the Requeue button.

In `@assets/vue/views/ListSubscribersView.vue`:
- Around line 76-84: The BaseIcon used in the Move Selected button currently
passes name="start" which shows a play-triangle and is semantically misleading;
change the icon to a more appropriate glyph (e.g., "arrow-right", "swap" or
"move") by updating the BaseIcon name in the button that calls
moveSelectedSubscribers so the visual matches the action and app icon
vocabulary. Locate the button component that contains BaseIcon name="start" (the
Move Selected button bound to moveSelectedSubscribers and disabled by
hasSelection/selectedTargetListId/actionLoading) and replace the name prop with
the chosen icon name across the component/template.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 69694bc3-9f6c-4dd6-b867-19f4be120c2e

📥 Commits

Reviewing files that changed from the base of the PR and between 30e3413 and 5763fbb.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (13)
  • assets/router/index.js
  • assets/vue/components/base/BaseIcon.vue
  • assets/vue/components/campaigns/CampaignDirectory.vue
  • assets/vue/components/dashboard/QuickActionsCard.vue
  • assets/vue/components/lists/ListDirectory.vue
  • assets/vue/components/subscribers/SubscriberDirectory.vue
  • assets/vue/components/subscribers/SubscriberTable.vue
  • assets/vue/components/templates/TemplateLibrary.vue
  • assets/vue/views/ListSubscribersView.vue
  • assets/vue/views/TemplateEditView.vue
  • package.json
  • src/Controller/TemplatesController.php
  • templates/auth/login.html.twig
✅ Files skipped from review due to trivial changes (4)
  • package.json
  • templates/auth/login.html.twig
  • assets/vue/components/subscribers/SubscriberDirectory.vue
  • assets/vue/components/dashboard/QuickActionsCard.vue
🚧 Files skipped from review as they are similar to previous changes (3)
  • assets/router/index.js
  • src/Controller/TemplatesController.php
  • assets/vue/components/templates/TemplateLibrary.vue

Comment on lines +95 to +97
pause: `<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><rect x="7" y="5" width="3" height="14" rx="1"></rect><rect x="14" y="5" width="3" height="14" rx="1"></rect></svg>`,

start: `<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polygon points="6 4 20 12 6 20 6 4" rx="1"></polygon></svg>`,
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Minor SVG quirks in the new pause/start icons.

A couple of small polish items on the new icons:

  • The start polygon has rx="1", but <polygon> doesn't support rx/ry in SVG — it's silently ignored, so the corners won't actually be rounded. If you want rounded tips, either use a <path> with rounded joins (your stroke-linejoin="round" already helps) or just drop the attribute.
  • stroke-width is inconsistent across the two new icons (1.8 for pause, 2.5 for start), and different from the 2 used by every other icon in this registry. Visually they'll look a bit heavier/lighter than their siblings when rendered side-by-side (e.g., in CampaignDirectory.vue where both appear as w-3.5 h-3.5).
🎨 Suggested tweak
-  pause: `<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"><rect x="7" y="5" width="3" height="14" rx="1"></rect><rect x="14" y="5" width="3" height="14" rx="1"></rect></svg>`,
+  pause: `<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="7" y="5" width="3" height="14" rx="1"></rect><rect x="14" y="5" width="3" height="14" rx="1"></rect></svg>`,

-  start: `<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><polygon points="6 4 20 12 6 20 6 4" rx="1"></polygon></svg>`,
+  start: `<svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><polygon points="6 4 20 12 6 20 6 4"></polygon></svg>`,

Also noticed the other icons include xmlns and an inline class="lucide …" — not a bug, but worth mirroring if you care about registry consistency.

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

In `@assets/vue/components/base/BaseIcon.vue` around lines 95 - 97, The new SVG
entries for the pause and start icons in BaseIcon.vue are inconsistent: remove
the unsupported rx attribute from the start icon's <polygon> (or replace the
polygon with a <path> if you want rounded tips) and normalize stroke-width to
match the registry (use stroke-width="2" for both the pause and start entries);
optionally add the same xmlns and class="lucide …" attributes present on other
icons for consistency. Ensure you update the 'start' and 'pause' string values
accordingly and keep stroke-linejoin/stroke-linecap as-is if you rely on rounded
joins.

Comment thread assets/vue/views/TemplateEditView.vue Outdated
@TatevikGr TatevikGr merged commit 5c27f5b into dev Apr 23, 2026
3 checks passed
@TatevikGr TatevikGr deleted the feat/templates branch April 23, 2026 08:15
TatevikGr added a commit that referenced this pull request Apr 23, 2026
    New Features
        Templates section: listing, create and edit pages; responsive template library with add-from-default, preview, edit, and delete flows; full template editor with save, create, update, and derive-text utility.

    UI Changes
        Updated action buttons and icons across dashboard, lists, campaigns, subscribers and subscriber list views; renamed headings and adjusted navigation targets.

    Chore
        Internal dependency updated.

---------

Co-authored-by: Tatevik <tatevikg1@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants