Skip to content

feat: add opt-in warranty expiration notifications#1586

Open
elemated wants to merge 3 commits into
sysadminsmedia:mainfrom
elemated:feat/warranty-expiration-notifications
Open

feat: add opt-in warranty expiration notifications#1586
elemated wants to merge 3 commits into
sysadminsmedia:mainfrom
elemated:feat/warranty-expiration-notifications

Conversation

@elemated

@elemated elemated commented Jun 26, 2026

Copy link
Copy Markdown

Adds a checkbox to an item's warranty details which allows the user to opt in to notifications 30 days before warranty expiration. Uses the same Shoutrrr flow as the existing maintenance reminders.

'Warranty Details' section

Before After

EDIT: Based on CodeRabbit review, I also gated the visibility of the checkbox based on 1) valid date, 2) NOT lifetime warranty. Since the notification couldn't fire in these instances anyway, CodeRabbit suggested updating the UI to match. Video of behavior below.

checkbox.hide.mov

Notifier settings

Before After

Test notification

Screenshot 2026-06-26 at 8 13 30 AM

What type of PR is this?

  • feature

What this PR does / why we need it:

Warranty expiration dates are stored in Homebox but seem to be mostly unused, requiring the user to dig into each item's details to find out when the warranty expires. This PR gives user the option to receive a notification 30 days before warranty expiration. This allows them to file a claim, extend coverage, or replace the item before the window ends.

backend

  • backend/internal/data/ent/schema/entity.go - added the new boolean which tracks if user wants warranty expiration notifications for that entity
  • backend/internal/data/migrations/...add_warranty_notification.sql - added new column for notify_warranty_expiration
  • backend/internal/data/repo/repo_entities.go - added the field to the data layer and added a query to find items whose warranty expires on a given day
  • backend/internal/core/services/service_background.go - extended the existing daily SendNotifiersToday to also gather warranty items and append the warranty expiration details to the message

frontend

  • frontend/pages/item/[id]/index/edit.vue - added the checkbox to the warranty section
  • frontend/locales/en.json - added the checkbox label and updated the Notifiers subtitle to mention warranty expirations (see images above)

Testing

To test this, I built it as a docker image and ran it on my live server.

Notes

I think there is room to extend the feature to allow the user to determine how many days before expiry they want a notification. But wanted to keep the scope of these changes as small as possible, and 30 days felt like a good starting point.

Code written with the help of Claude Opus.

@coderabbitai

coderabbitai Bot commented Jun 26, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

This PR adds opt-in warranty-expiration notifications. It adds the entity flag and schema column, repository lookup for warranties expiring on a target day, daily notifier output that includes warranty items, and frontend/docs updates for the new setting and message format.

Changes

Warranty expiration notifications

Layer / File(s) Summary
Storage and contracts
backend/internal/data/migrations/*, backend/internal/data/repo/repo_entities.go, frontend/lib/api/types/data-contracts.ts
Adds notify_warranty_expiration to the entities schema, repository update/mapping paths, and generated client entity types.
Warranty lookup
backend/internal/data/repo/repo_entities.go, backend/internal/data/repo/repo_entities_test.go
Adds GetWarrantyExpiringOn and a test covering the exact-date, opt-in, non-lifetime filter.
Notifier body
backend/internal/core/services/service_background.go
Computes the warranty target day, loads expiring warranty items per group, updates the empty-work check/logging, and appends warranty sections to the notifier body when present.
UI and docs
frontend/pages/item/[id]/index/edit.vue, frontend/locales/en.json, docs/src/content/docs/en/user-guide/notifiers.mdx
Adds the warranty-expiration checkbox label, updates notifier text, and documents warranty expiration reminders and message format.

Sequence Diagram(s)

The daily notifier now looks up expiring warranties before composing the combined message body.

sequenceDiagram
  participant SendNotifiersToday
  participant EntityRepository
  participant entities_table
  SendNotifiersToday->>SendNotifiersToday: compute warrantyTarget
  SendNotifiersToday->>EntityRepository: GetWarrantyExpiringOn(warrantyTarget)
  EntityRepository->>entities_table: query WarrantyExpires and notify_warranty_expiration
  entities_table-->>EntityRepository: matching rows
  EntityRepository-->>SendNotifiersToday: warrantyItems
  SendNotifiersToday->>SendNotifiersToday: append maintenance and warranty sections
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

Thirty days ahead, the reminders bloom,
Maintenance and warranties share one room.
A checkbox clicks, a query takes flight,
Then docs and labels glow soft and bright.
Homebox hums a punctual tune.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and accurately summarizes the main change: opt-in warranty expiration notifications.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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.
Description check ✅ Passed The PR description is mostly complete and covers type, rationale, testing, and notes, but it omits the required issue references section.
✨ Finishing Touches
✨ Simplify code
  • Create PR with simplified code

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
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (2)
backend/internal/core/services/service_background.go (1)

52-63: 🩺 Stability & Availability | 🔵 Trivial | 💤 Low value

Optional: a single group's lookup failure aborts notifications for all remaining groups.

If GetWarrantyExpiringOn (or the existing GetScheduled) returns an error for one group, the early return err skips every group that hasn't been processed yet in this run. Since this is a daily best-effort batch, consider logging and continue-ing to the next group so one bad group doesn't suppress everyone's notifications. This mirrors the pre-existing maintenance behavior, so it's a judgment call.

🤖 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 `@backend/internal/core/services/service_background.go` around lines 52 - 63,
The group lookup in service_background.go stops the entire notification run when
GetWarrantyExpiringOn (and the existing GetScheduled call) returns an error for
one group. Update the loop in the background notification job to treat these as
best-effort failures by logging the error with the group context (for example
group.ID and group.Name) and then continuing to the next group instead of
returning, so one bad group does not block remaining notifications.
backend/internal/data/migrations/postgres/20260513000000_add_warranty_notification.sql (1)

1-3: 📐 Maintainability & Code Quality | 🔵 Trivial

Consider adding a -- +goose Down section for reversibility.

The migration lacks a -- +goose Down block. While a review of the migrations directory shows that roughly 75% of recent migrations in this repository omit the Down section, including the rollback script for simple transactions like ADD COLUMN is a robust safety measure. It ensures goose down works correctly if a rollback is needed, avoiding manual intervention.

For this specific change, a simple reverse statement suffices:

-- +goose Down
ALTER TABLE entities DROP COLUMN notify_warranty_expiration;

Security Note: Ensure that any default values or sensitive data added in migrations are handled securely and do not inadvertently expose sensitive information in logs or revert to insecure states during rollbacks.

🤖 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
`@backend/internal/data/migrations/postgres/20260513000000_add_warranty_notification.sql`
around lines 1 - 3, The migration currently only defines the Goose Up path and
is missing a reversible Goose Down block. Update the migration by adding a `--
+goose Down` section in `20260513000000_add_warranty_notification.sql` that
cleanly reverses the `ALTER TABLE entities ADD COLUMN
notify_warranty_expiration` change, so `goose down` can rollback the schema
using the same migration file.
🤖 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 `@frontend/pages/item/`[id]/index/edit.vue:
- Around line 280-284: The notifyWarrantyExpiration checkbox is currently
available even when the item can never produce a warranty reminder. Update the
edit form logic in the item edit component so this toggle is hidden or disabled
whenever lifetimeWarranty is enabled or warrantyExpires is missing, and make
sure notifyWarrantyExpiration is cleared when those conditions become true. Use
the existing form field definitions around the checkbox and the related item
state handlers in the edit view to keep the behavior consistent.

---

Nitpick comments:
In `@backend/internal/core/services/service_background.go`:
- Around line 52-63: The group lookup in service_background.go stops the entire
notification run when GetWarrantyExpiringOn (and the existing GetScheduled call)
returns an error for one group. Update the loop in the background notification
job to treat these as best-effort failures by logging the error with the group
context (for example group.ID and group.Name) and then continuing to the next
group instead of returning, so one bad group does not block remaining
notifications.

In
`@backend/internal/data/migrations/postgres/20260513000000_add_warranty_notification.sql`:
- Around line 1-3: The migration currently only defines the Goose Up path and is
missing a reversible Goose Down block. Update the migration by adding a `--
+goose Down` section in `20260513000000_add_warranty_notification.sql` that
cleanly reverses the `ALTER TABLE entities ADD COLUMN
notify_warranty_expiration` change, so `goose down` can rollback the schema
using the same migration file.
🪄 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: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 97ee3dce-806d-4a0f-826b-40f0f080bdce

📥 Commits

Reviewing files that changed from the base of the PR and between 1701e60 and 8c3c91b.

⛔ Files ignored due to path filters (18)
  • backend/app/api/static/docs/docs.go is excluded by !backend/app/api/static/docs/**
  • backend/app/api/static/docs/openapi-3.json is excluded by !backend/app/api/static/docs/**
  • backend/app/api/static/docs/openapi-3.yaml is excluded by !backend/app/api/static/docs/**
  • backend/app/api/static/docs/swagger.json is excluded by !backend/app/api/static/docs/**
  • backend/app/api/static/docs/swagger.yaml is excluded by !backend/app/api/static/docs/**
  • backend/internal/data/ent/entity.go is excluded by !backend/internal/data/ent/**
  • backend/internal/data/ent/entity/entity.go is excluded by !backend/internal/data/ent/**
  • backend/internal/data/ent/entity/where.go is excluded by !backend/internal/data/ent/**
  • backend/internal/data/ent/entity_create.go is excluded by !backend/internal/data/ent/**
  • backend/internal/data/ent/entity_update.go is excluded by !backend/internal/data/ent/**
  • backend/internal/data/ent/migrate/schema.go is excluded by !backend/internal/data/ent/**
  • backend/internal/data/ent/mutation.go is excluded by !backend/internal/data/ent/**
  • backend/internal/data/ent/runtime.go is excluded by !backend/internal/data/ent/**
  • backend/internal/data/ent/schema/entity.go is excluded by !backend/internal/data/ent/**
  • docs/public/api/openapi-3.0.json is excluded by !docs/public/api/**
  • docs/public/api/openapi-3.0.yaml is excluded by !docs/public/api/**
  • docs/public/api/swagger-2.0.json is excluded by !docs/public/api/**
  • docs/public/api/swagger-2.0.yaml is excluded by !docs/public/api/**
📒 Files selected for processing (9)
  • backend/internal/core/services/service_background.go
  • backend/internal/data/migrations/postgres/20260513000000_add_warranty_notification.sql
  • backend/internal/data/migrations/sqlite3/20260513000000_add_warranty_notification.sql
  • backend/internal/data/repo/repo_entities.go
  • backend/internal/data/repo/repo_entities_test.go
  • docs/src/content/docs/en/user-guide/notifiers.mdx
  • frontend/lib/api/types/data-contracts.ts
  • frontend/locales/en.json
  • frontend/pages/item/[id]/index/edit.vue

Comment thread frontend/pages/item/[id]/index/edit.vue

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 1

🤖 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 `@frontend/pages/item/`[id]/index/edit.vue:
- Around line 291-295: The current watch on canNotifyWarrantyExpiration only
clears notifyWarrantyExpiration when the boolean flips, so stale invalid opt-ins
can survive hydration and still be sent by saveItem(). Update the logic in
edit.vue to also validate and clear item.value.notifyWarrantyExpiration whenever
the item is hydrated or changed, not just when canNotifyWarrantyExpiration
transitions to false. Use the existing watch(canNotifyWarrantyExpiration, ...)
alongside the item-loading/hydration path so the field is reset as soon as an
invalid warranty state is present.
🪄 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: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 07d16ec1-a0f9-492b-9a44-5e91162fbee3

📥 Commits

Reviewing files that changed from the base of the PR and between 8c3c91b and af8fea6.

📒 Files selected for processing (1)
  • frontend/pages/item/[id]/index/edit.vue

Comment thread frontend/pages/item/[id]/index/edit.vue Outdated
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