Skip to content

feat: certificate expiry warning and error alerts#121

Merged
emilwojtaszek merged 3 commits into
mainfrom
feat/certificate-expiry-alerts
Apr 5, 2026
Merged

feat: certificate expiry warning and error alerts#121
emilwojtaszek merged 3 commits into
mainfrom
feat/certificate-expiry-alerts

Conversation

@emilwojtaszek

@emilwojtaszek emilwojtaszek commented Apr 4, 2026

Copy link
Copy Markdown
Member

Summary

  • Show warning alert on invoices list and certificate settings pages when KSeF certificate expires within 7 days
  • Show error alert on both pages when certificate has already expired
  • Added Credentials.certificate_expiry_status/1 context function with typed return values
  • Extracted shared CertificateComponents module to keep alert markup DRY across pages

Test plan

  • Context unit tests: boundary cases at 0, 7, 8 days, expired, nil, no certificate (8 tests)
  • Certificate LiveView tests: expired, expiring soon, singular day, valid cert, no cert (5 tests)
  • Invoice list LiveView tests: expired, expiring soon, valid cert, mutual exclusivity with no-cert warning (4 tests)
  • mix format — clean
  • mix credo --strict — zero issues
  • All 99 tests pass

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Certificate expiry alerts added to certificate settings and invoice pages: show expired (days overdue), expiring soon (days remaining, correct singular/plural), or no banner when valid or absent.
    • Invoice status badges now consider duplicate status to suppress badges for confirmed duplicates.
  • Tests

    • Added comprehensive tests for expiry status computation and alert rendering across expired, expiring, valid, and missing certificate scenarios.

emilwojtaszek and others added 2 commits April 5, 2026 00:07
Confirmed duplicates are treated as discarded, so showing extraction
warnings is noise. Suppress the "incomplete" badge and "missing data"
alert when duplicate_status is :confirmed — on both list and detail views.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Show warning alert 7 days before certificate expiration on the invoices
list and certificate settings pages. Show error alert when certificate
has already expired. Extracted shared CertificateComponents module to
keep alert markup DRY across both pages.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Apr 4, 2026

Copy link
Copy Markdown

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 3dcfea13-2e3e-4019-890d-6b7604a0cebf

📥 Commits

Reviewing files that changed from the base of the PR and between 81441f9 and b19b3e4.

📒 Files selected for processing (1)
  • test/ksef_hub_web/live/certificate_live_test.exs

📝 Walkthrough

Walkthrough

Adds certificate expiry status computation and UI alerts: a new certificate_expiry_status/1 in Credentials, a CertificateComponents.cert_expiry_alert/1 component, LiveView integrations to surface alerts, invoice/extraction badge updates to consider duplicate status, and tests for logic and UI rendering.

Changes

Cohort / File(s) Summary
Certificate Expiry Logic
lib/ksef_hub/credentials.ex
Added @type expiry_status, @spec certificate_expiry_status/1, and implementation returning :no_certificate, :ok, {:expiring_soon, days}, or {:expired, days} based on certificate not_after and a 7‑day threshold.
Certificate UI Component
lib/ksef_hub_web/components/certificate_components.ex
New KsefHubWeb.CertificateComponents with cert_expiry_alert/1 rendering expired or expiring-soon banners, optional link_target, and empty output for other statuses.
LiveView integrations
lib/ksef_hub_web/live/certificate_live.ex, lib/ksef_hub_web/live/invoice_live/index.ex, lib/ksef_hub_web/live/invoice_live/show.ex
Added cert_expiry_status/cert_status assigns via Credentials.certificate_expiry_status/1, imported and rendered cert_expiry_alert, replaced has_certificate check with explicit status comparisons, and threaded link_target to alerts.
Invoice / Extraction badge
lib/ksef_hub_web/components/invoice_components.ex
Extended extraction_badge/1 with attr :duplicate_status and new clause to render nothing when duplicate_status: :confirmed; callers updated to pass duplicate_status.
Tests — unit & integration
test/ksef_hub/credentials_test.exs, test/ksef_hub_web/live/certificate_live_test.exs, test/ksef_hub_web/live/invoice_live/index_test.exs
Added unit tests for certificate_expiry_status/1 across edge cases and LiveView tests asserting expired/expiring banner rendering and absence when appropriate.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  Client->>LiveView: HTTP/WebSocket request (page render)
  LiveView->>Credentials: certificate_expiry_status(company_id)
  Credentials->>Repo/DB: get_certificate_for_company(company_id)
  Repo/DB-->>Credentials: certificate (with not_after) or nil
  Credentials-->>LiveView: expiry status (:ok/:no_certificate/:expiring_soon/:expired)
  LiveView->>CertificateComponents: render cert_expiry_alert(status, link_target)
  CertificateComponents-->>Client: HTML banner (or empty)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Possibly related PRs

Poem

🐰 I hopped through dates and not_after signs,

counted days, crossed deadlines like vines.
If seven days near, I whisper "take care,"
if past — I thump loudly, "Replace it, prepare!"
New banners bloom — a rabbit's small cheer.

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ 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 'feat: certificate expiry warning and error alerts' directly and accurately summarizes the main changes in the pull request, which add certificate expiry warning and error alert functionality across multiple pages and components.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/certificate-expiry-alerts

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

@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.

🧹 Nitpick comments (2)
test/ksef_hub_web/live/certificate_live_test.exs (2)

469-482: Add element presence assertion before text check.

This test only verifies text content without confirming the banner element exists. If "1 day" appears elsewhere on the page, this could pass incorrectly. Consider adding an explicit element check for consistency with the other expiry tests.

♻️ Suggested improvement
      {:ok, view, _html} = live(conn, ~p"/c/#{company.id}/settings/certificates")
-      assert render(view) =~ "1 day"
+      assert has_element?(view, "[data-testid='certificate-expiring-banner']")
+      assert has_element?(view, "[data-testid='certificate-expiring-banner']", "1 day")
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/ksef_hub_web/live/certificate_live_test.exs` around lines 469 - 482, The
test "shows singular 'day' when 1 day left" only asserts the string via
render(view) but doesn't confirm the expiry banner element exists; update the
test to first assert the specific banner element is present (use the same
selector used by the other expiry tests, e.g. has_element?(view,
"<banner_selector>") or equivalent) against the view returned by live(conn,
~p"/c/#{company.id}/settings/certificates") and then assert render(view) =~ "1
day" to ensure the text is coming from the expected UI element.

446-449: Consider using has_element?/3 for text assertions.

Per coding guidelines, prefer has_element? over raw HTML matching with render(view) =~. The third argument to has_element?/3 accepts a text filter:

♻️ Suggested refactor using has_element?/3
      {:ok, view, _html} = live(conn, ~p"/c/#{company.id}/settings/certificates")
      assert has_element?(view, "[data-testid='certificate-expired-banner']")
-      assert render(view) =~ "Certificate expired"
-      assert render(view) =~ "no longer working"
+      assert has_element?(view, "[data-testid='certificate-expired-banner']", "Certificate expired")
+      assert has_element?(view, "[data-testid='certificate-expired-banner']", "no longer working")

This applies similarly to lines 465-466 for the expiring soon test.

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

In `@test/ksef_hub_web/live/certificate_live_test.exs` around lines 446 - 449,
Replace raw HTML text assertions that use render(view) =~ with has_element?/3
checks: for the expired banner use assertions like has_element?(view,
"[data-testid='certificate-expired-banner']", "Certificate expired") and
has_element?(view, "[data-testid='certificate-expired-banner']", "no longer
working") instead of render(view) =~ "..."; do the same for the expiring-soon
test (lines around 465-466) by checking the appropriate data-testid and expected
text via has_element?/3 using the same view variable.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@test/ksef_hub_web/live/certificate_live_test.exs`:
- Around line 469-482: The test "shows singular 'day' when 1 day left" only
asserts the string via render(view) but doesn't confirm the expiry banner
element exists; update the test to first assert the specific banner element is
present (use the same selector used by the other expiry tests, e.g.
has_element?(view, "<banner_selector>") or equivalent) against the view returned
by live(conn, ~p"/c/#{company.id}/settings/certificates") and then assert
render(view) =~ "1 day" to ensure the text is coming from the expected UI
element.
- Around line 446-449: Replace raw HTML text assertions that use render(view) =~
with has_element?/3 checks: for the expired banner use assertions like
has_element?(view, "[data-testid='certificate-expired-banner']", "Certificate
expired") and has_element?(view, "[data-testid='certificate-expired-banner']",
"no longer working") instead of render(view) =~ "..."; do the same for the
expiring-soon test (lines around 465-466) by checking the appropriate
data-testid and expected text via has_element?/3 using the same view variable.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 54cc1295-0caf-4591-ba40-b0e2dd7c59b3

📥 Commits

Reviewing files that changed from the base of the PR and between bd826af and 81441f9.

📒 Files selected for processing (9)
  • lib/ksef_hub/credentials.ex
  • lib/ksef_hub_web/components/certificate_components.ex
  • lib/ksef_hub_web/components/invoice_components.ex
  • lib/ksef_hub_web/live/certificate_live.ex
  • lib/ksef_hub_web/live/invoice_live/index.ex
  • lib/ksef_hub_web/live/invoice_live/show.ex
  • test/ksef_hub/credentials_test.exs
  • test/ksef_hub_web/live/certificate_live_test.exs
  • test/ksef_hub_web/live/invoice_live/index_test.exs

Replace loose render(view) =~ checks with has_element?/3 scoped to
the specific data-testid banner selectors, ensuring text assertions
target the correct UI element.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@emilwojtaszek emilwojtaszek merged commit 0cc8491 into main Apr 5, 2026
0 of 2 checks passed
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.

1 participant