Skip to content

Dev - #92

Open
TatevikGr wants to merge 9 commits into
mainfrom
dev
Open

Dev#92
TatevikGr wants to merge 9 commits into
mainfrom
dev

Conversation

@TatevikGr

@TatevikGr TatevikGr commented Jul 29, 2026

Copy link
Copy Markdown
Collaborator

Summary by CodeRabbit

  • New Features

    • Added a dark mode that follows system preferences, remembers selections, and includes a light/dark toggle.
    • Extended dark-theme styling across dashboards, editors, tables, forms, modals, charts, and navigation.
    • Centralized API configuration for more consistent application connectivity.
    • Template saving now displays multiple field-specific validation errors with clear labels.
  • Bug Fixes

    • Removed the unsupported “requeued” message status and updated requeue handling.
    • Improved API configuration and page-loading reliability.
    • Updated test setup for more reliable request and environment handling.

Thanks for contributing to phpList!

* Refactor: env

* fix test

* app.api_base_url

* Remove api_base_url from controller render methods and add it as a global variable in twig configuration

* Remove api_token from controller render methods and update spa.html.twig to use app.session for auth_token

* Update API base URL configuration in templates and JavaScript

---------

Co-authored-by: Tatevik <tatevikg1@gmail.com>
@coderabbitai

coderabbitai Bot commented Jul 29, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@TatevikGr, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 22 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 6a9ad5eb-46bd-4e64-bc8a-eac0232f48ba

📥 Commits

Reviewing files that changed from the base of the PR and between 244be0d and dab6039.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (84)
  • assets/editor/EditorAssetPicker.vue
  • assets/styles/app.css
  • assets/vue/App.vue
  • assets/vue/api.js
  • assets/vue/components/base/BaseBadge.vue
  • assets/vue/components/base/BaseButton.vue
  • assets/vue/components/base/BaseCard.vue
  • assets/vue/components/base/BaseIcon.vue
  • assets/vue/components/base/BaseProgressBar.vue
  • assets/vue/components/base/CkEditorField.vue
  • assets/vue/components/bounces/BounceOverview.vue
  • assets/vue/components/bounces/BouncePer.vue
  • assets/vue/components/bounces/BounceRules.vue
  • assets/vue/components/bounces/BouncesActionsPanel.vue
  • assets/vue/components/campaigns/CampaignDirectory.vue
  • assets/vue/components/campaigns/ViewCampaignModal.vue
  • assets/vue/components/dashboard/CampaignsTable.vue
  • assets/vue/components/dashboard/KpiCard.vue
  • assets/vue/components/dashboard/PerformanceChartCard.vue
  • assets/vue/components/dashboard/QuickActionsCard.vue
  • assets/vue/components/dashboard/RecentCampaignsCard.vue
  • assets/vue/components/lists/AddSubscribersModal.vue
  • assets/vue/components/lists/CreateListModal.vue
  • assets/vue/components/lists/EditListModal.vue
  • assets/vue/components/lists/ListDirectory.vue
  • assets/vue/components/lists/ListSubscribersExportPanel.vue
  • assets/vue/components/public-pages/PublicPageEditor.vue
  • assets/vue/components/public-pages/PublicPagesDirectory.vue
  • assets/vue/components/settings/CreateAdminAttributeModal.vue
  • assets/vue/components/settings/CreateAdminModal.vue
  • assets/vue/components/settings/CreateSubscriberAttributeModal.vue
  • assets/vue/components/settings/EditAdminAttributeModal.vue
  • assets/vue/components/settings/EditAdminModal.vue
  • assets/vue/components/settings/EditSubscriberAttributeModal.vue
  • assets/vue/components/settings/SettingsActionsPanel.vue
  • assets/vue/components/settings/SettingsAdminAttributes.vue
  • assets/vue/components/settings/SettingsAdmins.vue
  • assets/vue/components/settings/SettingsConfigs.vue
  • assets/vue/components/settings/SettingsSubscriberAttributes.vue
  • assets/vue/components/sidebar/AppSidebar.vue
  • assets/vue/components/sidebar/SidebarLogo.vue
  • assets/vue/components/sidebar/SidebarNavItem.vue
  • assets/vue/components/sidebar/SidebarNavSection.vue
  • assets/vue/components/subscribers/ImportResult.vue
  • assets/vue/components/subscribers/SubscriberDirectory.vue
  • assets/vue/components/subscribers/SubscriberFilters.vue
  • assets/vue/components/subscribers/SubscriberModal.vue
  • assets/vue/components/subscribers/SubscriberTable.vue
  • assets/vue/components/templates/TemplateLibrary.vue
  • assets/vue/composables/useDarkMode.js
  • assets/vue/layouts/AdminLayout.vue
  • assets/vue/views/AnalyticsView.vue
  • assets/vue/views/CampaignEditView.vue
  • assets/vue/views/DashboardView.vue
  • assets/vue/views/ListSubscribersView.vue
  • assets/vue/views/TemplateEditView.vue
  • composer.json
  • config/packages/twig.yaml
  • config/services.yml
  • openapi.json
  • package.json
  • phpunit.xml.dist
  • src/Controller/AnalyticsController.php
  • src/Controller/BouncesController.php
  • src/Controller/CampaignsController.php
  • src/Controller/DashboardController.php
  • src/Controller/ListsController.php
  • src/Controller/PublicPagesController.php
  • src/Controller/SettingsController.php
  • src/Controller/SubscribersController.php
  • src/Controller/TemplatesController.php
  • templates/auth/login.html.twig
  • templates/base.html.twig
  • templates/spa.html.twig
  • tests/Integration/Controller/AnalyticsControllerTest.php
  • tests/Integration/Controller/BouncesControllerTest.php
  • tests/Integration/Controller/CampaignsControllerTest.php
  • tests/Integration/Controller/DashboardControllerTest.php
  • tests/Integration/Controller/ListsControllerTest.php
  • tests/Unit/assets/vue/components/campaigns/CampaignDirectory.spec.js
  • tests/Unit/assets/vue/composables/useDarkMode.spec.js
  • tests/Unit/assets/vue/layouts/AdminLayout.spec.js
  • tests/Unit/assets/vue/views/TemplateEditorView.spec.js
  • tests/bootstrap.php
📝 Walkthrough

Walkthrough

The change centralizes SPA API configuration through application parameters and a dedicated DOM element. Controllers no longer pass API values as template props. PHPUnit uses dotenv-aware bootstrapping, and integration tests register requests with RequestStack. Template saves now support field-level API validation errors. The message status contract removes requeued, and campaign requeue requests use submitted. The Vue frontend adds persistent dark mode and dark-mode styling across application views and components.

Estimated code review effort: 4 (Complex) | ~45 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.67% 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 "Dev" is too vague to identify the pull request's main changes, which include dark mode and dependency updates. Replace "Dev" with a concise title that names the primary change, such as "Add dark mode and update frontend dependencies".
✅ 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.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch dev

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

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

⚠️ Outside diff range comments (1)
tests/Integration/Controller/CampaignsControllerTest.php (1)

41-45: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Use the configured API URL in this test.

The assertion at Lines 58-60 still hard-codes http://api.phplist.local/, unlike AnalyticsControllerTest.php and BouncesControllerTest.php. Read app.api_base_url from the container and interpolate it so the test remains valid when configuration differs in CI or another environment.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/Integration/Controller/CampaignsControllerTest.php` around lines 41 -
45, Update the test setup in CampaignsControllerTest to read app.api_base_url
from the container and use that configured value in the assertion currently
hard-coding http://api.phplist.local/. Match the configured-URL handling used by
AnalyticsControllerTest and BouncesControllerTest while preserving the existing
assertion behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@tests/Integration/Controller/CampaignsControllerTest.php`:
- Around line 41-45: Update the test setup in CampaignsControllerTest to read
app.api_base_url from the container and use that configured value in the
assertion currently hard-coding http://api.phplist.local/. Match the
configured-URL handling used by AnalyticsControllerTest and
BouncesControllerTest while preserving the existing assertion behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: cbb71002-bd7b-43f1-8ab4-16dc3b002fdc

📥 Commits

Reviewing files that changed from the base of the PR and between 244be0d and 0e57640.

📒 Files selected for processing (22)
  • assets/vue/api.js
  • composer.json
  • config/packages/twig.yaml
  • config/services.yml
  • phpunit.xml.dist
  • src/Controller/AnalyticsController.php
  • src/Controller/BouncesController.php
  • src/Controller/CampaignsController.php
  • src/Controller/DashboardController.php
  • src/Controller/ListsController.php
  • src/Controller/PublicPagesController.php
  • src/Controller/SettingsController.php
  • src/Controller/SubscribersController.php
  • src/Controller/TemplatesController.php
  • templates/base.html.twig
  • templates/spa.html.twig
  • tests/Integration/Controller/AnalyticsControllerTest.php
  • tests/Integration/Controller/BouncesControllerTest.php
  • tests/Integration/Controller/CampaignsControllerTest.php
  • tests/Integration/Controller/DashboardControllerTest.php
  • tests/Integration/Controller/ListsControllerTest.php
  • tests/bootstrap.php
💤 Files with no reviewable changes (9)
  • src/Controller/AnalyticsController.php
  • src/Controller/DashboardController.php
  • src/Controller/SettingsController.php
  • src/Controller/BouncesController.php
  • src/Controller/CampaignsController.php
  • src/Controller/ListsController.php
  • src/Controller/SubscribersController.php
  • src/Controller/TemplatesController.php
  • src/Controller/PublicPagesController.php

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

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

⚠️ Outside diff range comments (1)
openapi.json (1)

8221-8229: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Keep requeued in the status schema if requeuing is supported.

assets/vue/components/campaigns/CampaignDirectory.vue still calls campaignClient.updateCampaignStatus(campaignId, 'requeued'), but openapi.json no longer lists requeued in MessageMetadataRequest.status.enum. Add it back if the API accepts it, otherwise show another status or disable the Requeue action.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@openapi.json` around lines 8221 - 8229, Update MessageMetadataRequest.status
and the Requeue action in CampaignDirectory.vue so they remain consistent: if
updateCampaignStatus supports requeued, add "requeued" back to the schema enum;
otherwise replace or disable the action and use an accepted status.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@openapi.json`:
- Around line 8221-8229: Update MessageMetadataRequest.status and the Requeue
action in CampaignDirectory.vue so they remain consistent: if
updateCampaignStatus supports requeued, add "requeued" back to the schema enum;
otherwise replace or disable the action and use an accepted status.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 25b3d077-9f84-4192-a0f5-117fa6cbdf37

📥 Commits

Reviewing files that changed from the base of the PR and between 0e57640 and 20b89db.

📒 Files selected for processing (4)
  • assets/vue/views/TemplateEditView.vue
  • composer.json
  • openapi.json
  • tests/Unit/assets/vue/views/TemplateEditorView.spec.js
🚧 Files skipped from review as they are similar to previous changes (1)
  • composer.json

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@assets/vue/App.vue`:
- Around line 12-20: Update the App.vue startup flow around useDarkMode so
isDark is initialized before any child render, rather than waiting for
onMounted. Either have initDarkMode read
document.documentElement.classList.contains('dark') with a browser/SSR guard, or
call that initialization before mounting the AppSidebar/other children. Keep
AdminLayout.vue’s toggle state and aria-label in sync with the initial dark-mode
class on first render.

In `@assets/vue/components/base/BaseIcon.vue`:
- Around line 115-117: Update the SVG registry entries for warning, template,
public, list, and copy in BaseIcon so their child SVG color classes inherit the
wrapper’s selected and hover colors, including the matching dark variants;
remove or replace fixed text-slate color classes that override wrapper styling
while preserving each icon’s existing state behavior.

In `@assets/vue/components/settings/SettingsConfigs.vue`:
- Line 46: Update the non-editable branch of the :class binding in
SettingsConfigs.vue to replace dark:text-slate-500 with a lighter dark-mode text
token, while preserving the existing read-only background and other classes.

In `@assets/vue/components/sidebar/SidebarNavSection.vue`:
- Line 3: Replace the insufficient dark-mode slate-500 text token with slate-400
or another tested higher-contrast token at
assets/vue/components/sidebar/SidebarNavSection.vue lines 3-3,
assets/vue/components/bounces/BounceRules.vue lines 43-43,
assets/vue/views/CampaignEditView.vue lines 125-125, and
assets/vue/views/CampaignEditView.vue lines 260-262; update the corresponding
section label, list-order value, “No template” option, and test-recipient helper
text while preserving their existing light-mode styling and behavior.

In `@assets/vue/views/AnalyticsView.vue`:
- Around line 35-49: Update the secondary analytics count labels in
AnalyticsView, including the campaign count and corresponding labels near the
referenced sections, from dark:text-slate-500 to dark:text-slate-400 for
improved contrast. Do not change the chart x-axis and y-axis dark-mode color
handling.
🪄 Autofix

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 Plus

Run ID: 7e852151-d2d3-42e5-892b-0826cb3d105e

📥 Commits

Reviewing files that changed from the base of the PR and between 20b89db and b21bb3b.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (57)
  • assets/editor/EditorAssetPicker.vue
  • assets/styles/app.css
  • assets/vue/App.vue
  • assets/vue/components/base/BaseBadge.vue
  • assets/vue/components/base/BaseButton.vue
  • assets/vue/components/base/BaseCard.vue
  • assets/vue/components/base/BaseIcon.vue
  • assets/vue/components/base/BaseProgressBar.vue
  • assets/vue/components/base/CkEditorField.vue
  • assets/vue/components/bounces/BounceOverview.vue
  • assets/vue/components/bounces/BouncePer.vue
  • assets/vue/components/bounces/BounceRules.vue
  • assets/vue/components/bounces/BouncesActionsPanel.vue
  • assets/vue/components/campaigns/CampaignDirectory.vue
  • assets/vue/components/campaigns/ViewCampaignModal.vue
  • assets/vue/components/dashboard/CampaignsTable.vue
  • assets/vue/components/dashboard/KpiCard.vue
  • assets/vue/components/dashboard/PerformanceChartCard.vue
  • assets/vue/components/dashboard/QuickActionsCard.vue
  • assets/vue/components/dashboard/RecentCampaignsCard.vue
  • assets/vue/components/lists/AddSubscribersModal.vue
  • assets/vue/components/lists/CreateListModal.vue
  • assets/vue/components/lists/EditListModal.vue
  • assets/vue/components/lists/ListDirectory.vue
  • assets/vue/components/lists/ListSubscribersExportPanel.vue
  • assets/vue/components/public-pages/PublicPageEditor.vue
  • assets/vue/components/public-pages/PublicPagesDirectory.vue
  • assets/vue/components/settings/CreateAdminAttributeModal.vue
  • assets/vue/components/settings/CreateAdminModal.vue
  • assets/vue/components/settings/CreateSubscriberAttributeModal.vue
  • assets/vue/components/settings/EditAdminAttributeModal.vue
  • assets/vue/components/settings/EditAdminModal.vue
  • assets/vue/components/settings/EditSubscriberAttributeModal.vue
  • assets/vue/components/settings/SettingsActionsPanel.vue
  • assets/vue/components/settings/SettingsAdminAttributes.vue
  • assets/vue/components/settings/SettingsAdmins.vue
  • assets/vue/components/settings/SettingsConfigs.vue
  • assets/vue/components/settings/SettingsSubscriberAttributes.vue
  • assets/vue/components/sidebar/AppSidebar.vue
  • assets/vue/components/sidebar/SidebarNavItem.vue
  • assets/vue/components/sidebar/SidebarNavSection.vue
  • assets/vue/components/subscribers/ImportResult.vue
  • assets/vue/components/subscribers/SubscriberDirectory.vue
  • assets/vue/components/subscribers/SubscriberFilters.vue
  • assets/vue/components/subscribers/SubscriberModal.vue
  • assets/vue/components/subscribers/SubscriberTable.vue
  • assets/vue/components/templates/TemplateLibrary.vue
  • assets/vue/composables/useDarkMode.js
  • assets/vue/layouts/AdminLayout.vue
  • assets/vue/views/AnalyticsView.vue
  • assets/vue/views/CampaignEditView.vue
  • assets/vue/views/DashboardView.vue
  • assets/vue/views/ListSubscribersView.vue
  • assets/vue/views/TemplateEditView.vue
  • package.json
  • templates/base.html.twig
  • tests/Unit/assets/vue/components/campaigns/CampaignDirectory.spec.js

Comment thread assets/vue/App.vue
Comment on lines +115 to +117
classes.push("text-ext-wf3 dark:text-indigo-400");
} else {
classes.push("text-slate-400 group-hover:text-slate-600");
classes.push("text-slate-400 group-hover:text-slate-600 dark:group-hover:text-slate-300");

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Files:"
git ls-files | rg '(^|/)BaseIcon\.vue$|base/SvgRegistry|SvgRegistry|IconRegistry|icon.*registry' || true

echo
echo "BaseIcon outline/size:"
if [ -f assets/vue/components/base/BaseIcon.vue ]; then
  wc -l assets/vue/components/base/BaseIcon.vue
  ast-grep outline assets/vue/components/base/BaseIcon.vue --view expanded || true
fi

echo
echo "Context around lines 100-130:"
sed -n '1,180p' assets/vue/components/base/BaseIcon.vue

echo
echo "Search registry entries and text classes:"
rg -n "warning|template|public|list|copy|text-slate-500|wrapperClass|innerClass|svgClass|color|dark:text-indigo|text-ext-wf3|group-hover:text-slate-300" assets/vue/components/base -S || true

Repository: phpList/web-frontend

Length of output: 22641


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
from pathlib import Path
p = Path("assets/vue/components/base/BaseIcon.vue")
if p.exists():
    text = p.read_text()
    state_classes = [
        "text-ext-wf3",
        "dark:text-indigo-400",
        "group-hover:text-slate-600",
        "dark:group-hover:text-slate-300",
        "dark:text-slate-400"
    ]
    entries = ["warning", "template", "public", "list", "copy"]
    print("Has relevant state class in BaseIcon.vue:", any(c in text for c in state_classes))
    for entry in entries:
        idx = text.find(entry)
        print(f"\nINDEX {entry}: {idx}")
        if idx != -1:
            lo=max(0, idx-500); hi=min(len(text), idx+500)
            print(text[lo:hi])
else:
    print("file missing")
PY

Repository: phpList/web-frontend

Length of output: 4808


Make inner SVG icons inherit or inherit wrapper colors.

BaseIcon applies dark selected/hover colors on the wrapper <span>, but registries like warning, template, public, list, and copy set a child SVG’s own text-slate-500 and bypass the wrapper state colors. Add the matching dark state variants to those SVG entries, or use wrapper-only color variants in the registry.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@assets/vue/components/base/BaseIcon.vue` around lines 115 - 117, Update the
SVG registry entries for warning, template, public, list, and copy in BaseIcon
so their child SVG color classes inherit the wrapper’s selected and hover
colors, including the matching dark variants; remove or replace fixed text-slate
color classes that override wrapper styling while preserving each icon’s
existing state behavior.

Comment thread assets/vue/components/settings/SettingsConfigs.vue
Comment thread assets/vue/components/sidebar/SidebarNavSection.vue
Comment thread assets/vue/views/AnalyticsView.vue
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