Skip to content

feat: rentals SaaS sprints R1→I1 + nav duplicate-translation fix - #174

Merged
HadiSaaibi merged 7 commits into
hadisaiibi-mouhannadfrom
mouhannad/rentals-r1-i1
Jul 20, 2026
Merged

feat: rentals SaaS sprints R1→I1 + nav duplicate-translation fix#174
HadiSaaibi merged 7 commits into
hadisaiibi-mouhannadfrom
mouhannad/rentals-r1-i1

Conversation

@MouhannadKhodor

Copy link
Copy Markdown
Collaborator

Lands the full post-v1 roadmap (all verified) plus one nav fix.

Scope (commit 6919d7f)

  • R1 Reports — read-only /reports summary + rent-roll + overdue (org_admin/finance only; reuses computeInvoiceSummary).
  • D1 Live role dashboards — org-admin/finance/staff dashboards fed from real data; shared KpiTile + dashboard-kpis helpers.
  • N1 Notifications — Redis/BullMQ queue + tenant support-tickets + best-effort email (NOTIFICATIONS_EMAIL flag, KC-resolved recipient, mailpit) + /dashboard/notifications history.
  • T1 Tenant self-serviceRenter.renterUserId seam links KC tenant user → Renter/Lease; /tenant/overview + open-request; structural isolation from sub.
  • O1 Prod Redis + hardening — dedicated prod Redis config, BullMQ dead-letter queue, terminus /health Redis indicator (1500ms ping race), atomic handleCheckoutCompleted ($transaction).
  • U1/U2 Rentals UX — top-level /dashboard/leases + nav (org-wide GET /leases), create-lease cascade dialog, clickable Buildings rows, repo-wide base-ui Select.Value raw-value fix, renter→new-lease deep-link, invoice/maintenance cross-links.
  • I1 i18n/RTL — 9 hardcoded-English pages made dict-driven; 9 new namespaces, key parity balanced; localized zod schemas.
  • Nav fixnav.billing → "Subscription"/"الاشتراك" to resolve the Arabic duplicate-label collision with Invoices ("الفواتير"); parity preserved.

Note on overlap

This branch also carries the 4 invoice/payment commits (9871eb1, 5eea50f, 9368d3c, 06bfa01) that are already under #139 (feat/rent-invoices), so the diff overlaps that PR until #139 merges into the base.

Verification

All sprints were verified during their respective sessions (jest/vitest green, Playwright authed E2E in EN+AR). No code was rebuilt in this commit — it is the accumulated, previously-verified working tree.

🤖 Generated with Claude Code

HadiSaaibi and others added 6 commits July 14, 2026 21:35
Invoice/InvoiceLineItem schema, computeInvoiceSummary deep module,
read-only InvoicesService/Controller, invoices permission area, and
the filtered list page. Write path lands in issues/013.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
InvoicesService.create/update/remove with wholesale line-item
replacement on update, LeasesService.remove guarded against
referenced invoices, and the New/Edit Invoice dialog (cascading
lease picker, repeatable line-item rows) plus delete confirmation.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
InvoicePayment schema + InvoicePaymentsService (create/findAll/
findOne/remove), building-scoped via the parent Invoice, returning
recomputed totalAmount/paidAmount/status on create/remove.
InvoicesService.remove now also rejects when a payment references
the invoice. Adds the Invoice detail page showing line items and
payment history together, with Record Payment and delete actions.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
CI format:check failed on 7 files added by the Invoices/Payments
PR (issues/012-014) that weren't run through prettier before commit.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Fixed the findings that were genuine bugs or matched real project
convention (AGENTS.md, existing service patterns):
- computeInvoiceSummary: compare paid/total in integer cents, not
  raw floats, so a fully-paid invoice can't get stuck partially_paid
  due to float rounding (e.g. 0.1 + 0.2).
- InvoicePaymentsService.summarizeInvoice: scope the Invoice lookup
  by orgId (defense-in-depth; both call sites already passed a
  pre-validated invoiceId, but this method returns entity data,
  unlike the id-only reference-count guards elsewhere).
- Invoice detail page: surface a payment-list fetch failure instead
  of silently rendering "No payments recorded yet."
- Invoices page: add aria-labels to the line-item category/
  description/amount controls; guard the row's onKeyDown so
  Enter/Space on the nested actions-menu trigger doesn't also
  navigate the row (keydown bubbles past the click stopPropagation
  guard).

Skipped findings that contradict this repo's actual conventions
(verified against ExpensesService/expenses-page.tsx/AGENTS.md):
DatabaseService+Logger (no DatabaseService exists anywhere; every
service uses PrismaService per AGENTS.md), UUID+@@Schema primary
keys (all 17 existing models use cuid(), none declare @@Schema),
per-file contracts (packages/contracts/src is a single index.ts),
ZodValidationPipe on API controllers (backend uses class-validator
DTOs; Zod is frontend-only per AGENTS.md), importing wire types from
@repo/contracts in the browser (existing pages import from
@/types/api), ApiError-specific toast messages and design-token
status colors (existing pages use generic toasts and raw Tailwind
colors identically), and wrapping writes+timeline emits in a
transaction (no service in the codebase does this — verified in
ExpensesService). Also verified the flagged delete-guard "atomicity"
concerns in InvoicesService/LeasesService already match the
identical id-only reference-count pattern used in
VendorsService/WorkOrdersService.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Land the full post-v1 roadmap (all verified) in one working tree:

- R1 Reports: read-only /reports summary + rent-roll + overdue
  (org_admin/finance only; reuses computeInvoiceSummary).
- D1 Live role dashboards: org-admin/finance/staff dashboards fed from
  real data; shared KpiTile + dashboard-kpis helpers.
- N1 Notifications: Redis/BullMQ queue + tenant support-tickets +
  best-effort email step (NOTIFICATIONS_EMAIL flag, KC-resolved
  recipient, mailpit) + /dashboard/notifications history page.
- T1 Tenant self-service: Renter.renterUserId seam links KC tenant
  user -> Renter/Lease; /tenant/overview + open-request; structural
  isolation (all scope derived from sub).
- O1 Prod Redis + hardening: dedicated prod Redis config, BullMQ
  dead-letter queue, terminus /health Redis indicator (1500ms ping
  race), atomic handleCheckoutCompleted ($transaction).
- U1/U2 Rentals UX: top-level /dashboard/leases page + nav (org-wide
  GET /leases), create-lease cascade dialog, clickable Buildings rows,
  Notifications nav, repo-wide base-ui Select.Value raw-value fix,
  renter->new-lease deep-link, invoice/maintenance cross-links.
- I1 i18n/RTL: 9 hardcoded-English pages made dict-driven; 9 new
  namespaces, key parity balanced; localized zod schemas.
- Nav fix: nav.billing -> "Subscription"/"الاشتراك" to resolve Arabic
  duplicate label collision with Invoices ("الفواتير"); parity preserved.

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

coderabbitai Bot commented Jul 20, 2026

Copy link
Copy Markdown

Important

Review skipped

Too many files!

This PR contains 171 files, which is 71 over the limit of 100.

To get a review, narrow the scope:
• coderabbit review --type committed # exclude uncommitted changes
• coderabbit review --dir # limit to a subdirectory
• coderabbit review --base # compare against a closer base

Upgrade to a paid plan to raise the limit.

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 8efdd368-3347-4ef3-9e19-cdb924231377

📥 Commits

Reviewing files that changed from the base of the PR and between e462d2b and 6fdb750.

⛔ Files ignored due to path filters (8)
  • demo-admin-dashboard.png is excluded by !**/*.png
  • demo-reports.png is excluded by !**/*.png
  • demo-tenant-portal.png is excluded by !**/*.png
  • leases-page-admin.png is excluded by !**/*.png
  • n1-mailpit-support-ticket-email.png is excluded by !**/*.png
  • o1-demo-admin-dashboard.png is excluded by !**/*.png
  • o1-demo-reports.png is excluded by !**/*.png
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (171)
  • .dockerignore
  • .gitignore
  • AGENTS.md
  • apps/api/Dockerfile
  • apps/api/package.json
  • apps/api/scripts/seed-demo.js
  • apps/api/scripts/verify-n1.ts
  • apps/api/scripts/verify-o1-tx.js
  • apps/api/src/app.module.ts
  • apps/api/src/common/invoice-summary/compute-invoice-summary.spec.ts
  • apps/api/src/common/invoice-summary/compute-invoice-summary.ts
  • apps/api/src/config/env.validation.ts
  • apps/api/src/config/mail.config.ts
  • apps/api/src/config/redis.config.ts
  • apps/api/src/config/stripe.config.ts
  • apps/api/src/infrastructure/mail/mail.module.ts
  • apps/api/src/infrastructure/mail/mail.service.ts
  • apps/api/src/main.ts
  • apps/api/src/modules/billing/billing.service.ts
  • apps/api/src/modules/billing/plan-catalog.ts
  • apps/api/src/modules/health/health.controller.ts
  • apps/api/src/modules/health/health.module.ts
  • apps/api/src/modules/health/redis.health.spec.ts
  • apps/api/src/modules/health/redis.health.ts
  • apps/api/src/modules/invoice-payments/dto/create-invoice-payment.dto.ts
  • apps/api/src/modules/invoice-payments/invoice-payments.controller.ts
  • apps/api/src/modules/invoice-payments/invoice-payments.module.ts
  • apps/api/src/modules/invoice-payments/invoice-payments.service.spec.ts
  • apps/api/src/modules/invoice-payments/invoice-payments.service.ts
  • apps/api/src/modules/invoices/dto/create-invoice.dto.ts
  • apps/api/src/modules/invoices/dto/update-invoice.dto.ts
  • apps/api/src/modules/invoices/invoices.controller.ts
  • apps/api/src/modules/invoices/invoices.module.ts
  • apps/api/src/modules/invoices/invoices.service.spec.ts
  • apps/api/src/modules/invoices/invoices.service.ts
  • apps/api/src/modules/leases/leases-overview.controller.ts
  • apps/api/src/modules/leases/leases.module.ts
  • apps/api/src/modules/leases/leases.service.spec.ts
  • apps/api/src/modules/leases/leases.service.ts
  • apps/api/src/modules/notifications/n1-email-e2e.spec.ts
  • apps/api/src/modules/notifications/notification-email.service.spec.ts
  • apps/api/src/modules/notifications/notification-email.service.ts
  • apps/api/src/modules/notifications/notifications-dead-letter.service.spec.ts
  • apps/api/src/modules/notifications/notifications-dead-letter.service.ts
  • apps/api/src/modules/notifications/notifications.constants.ts
  • apps/api/src/modules/notifications/notifications.controller.ts
  • apps/api/src/modules/notifications/notifications.module.ts
  • apps/api/src/modules/notifications/notifications.processor.spec.ts
  • apps/api/src/modules/notifications/notifications.processor.ts
  • apps/api/src/modules/notifications/notifications.service.spec.ts
  • apps/api/src/modules/notifications/notifications.service.ts
  • apps/api/src/modules/notifications/o1-deadletter-e2e.spec.ts
  • apps/api/src/modules/renters/dto/create-renter.dto.ts
  • apps/api/src/modules/renters/renters.service.ts
  • apps/api/src/modules/reports/reports.controller.ts
  • apps/api/src/modules/reports/reports.module.ts
  • apps/api/src/modules/reports/reports.service.spec.ts
  • apps/api/src/modules/reports/reports.service.ts
  • apps/api/src/modules/support-tickets/dto/create-support-ticket.dto.ts
  • apps/api/src/modules/support-tickets/dto/update-support-ticket.dto.ts
  • apps/api/src/modules/support-tickets/support-tickets.controller.ts
  • apps/api/src/modules/support-tickets/support-tickets.module.ts
  • apps/api/src/modules/support-tickets/support-tickets.service.spec.ts
  • apps/api/src/modules/support-tickets/support-tickets.service.ts
  • apps/api/src/modules/tenant/dto/create-tenant-maintenance-request.dto.ts
  • apps/api/src/modules/tenant/tenant.controller.ts
  • apps/api/src/modules/tenant/tenant.module.ts
  • apps/api/src/modules/tenant/tenant.service.spec.ts
  • apps/api/src/modules/tenant/tenant.service.ts
  • apps/api/src/modules/webhooks/webhooks.service.spec.ts
  • apps/api/src/modules/webhooks/webhooks.service.ts
  • apps/web/Dockerfile
  • apps/web/next-env.d.ts
  • apps/web/package.json
  • apps/web/src/app/[lang]/(public)/_sections/hero-section.tsx
  • apps/web/src/app/[lang]/(public)/_sections/pricing-section.tsx
  • apps/web/src/app/[lang]/(public)/login/page.tsx
  • apps/web/src/app/[lang]/(public)/page.tsx
  • apps/web/src/app/[lang]/dashboard/buildings/[id]/floors/[floorId]/apartments/[apartmentId]/page.tsx
  • apps/web/src/app/[lang]/dashboard/buildings/[id]/floors/[floorId]/page.tsx
  • apps/web/src/app/[lang]/dashboard/buildings/[id]/page.tsx
  • apps/web/src/app/[lang]/dashboard/buildings/page.tsx
  • apps/web/src/app/[lang]/dashboard/expenses/page.tsx
  • apps/web/src/app/[lang]/dashboard/invoices/[id]/page.tsx
  • apps/web/src/app/[lang]/dashboard/invoices/page.tsx
  • apps/web/src/app/[lang]/dashboard/leases/page.tsx
  • apps/web/src/app/[lang]/dashboard/notifications/page.tsx
  • apps/web/src/app/[lang]/dashboard/page.tsx
  • apps/web/src/app/[lang]/dashboard/renters/[id]/page.tsx
  • apps/web/src/app/[lang]/dashboard/renters/page.tsx
  • apps/web/src/app/[lang]/dashboard/reports/page.tsx
  • apps/web/src/app/[lang]/dashboard/support/page.tsx
  • apps/web/src/app/[lang]/dashboard/tasks/[id]/page.tsx
  • apps/web/src/app/[lang]/dashboard/tasks/page.tsx
  • apps/web/src/app/[lang]/dashboard/users/page.tsx
  • apps/web/src/app/[lang]/dashboard/vendors/page.tsx
  • apps/web/src/app/[lang]/layout.tsx
  • apps/web/src/app/api/invoice-payments/[id]/route.ts
  • apps/web/src/app/api/invoice-payments/route.ts
  • apps/web/src/app/api/invoices/[id]/route.ts
  • apps/web/src/app/api/invoices/route.ts
  • apps/web/src/app/api/leases/route.ts
  • apps/web/src/app/api/notifications/[id]/read/route.ts
  • apps/web/src/app/api/notifications/read-all/route.ts
  • apps/web/src/app/api/notifications/route.ts
  • apps/web/src/app/api/notifications/unread-count/route.ts
  • apps/web/src/app/api/reports/overdue/route.ts
  • apps/web/src/app/api/reports/rent-roll/route.ts
  • apps/web/src/app/api/reports/summary/route.ts
  • apps/web/src/app/api/support-tickets/[id]/route.ts
  • apps/web/src/app/api/support-tickets/route.ts
  • apps/web/src/app/api/tenant/maintenance-requests/route.ts
  • apps/web/src/app/api/tenant/overview/route.ts
  • apps/web/src/app/globals.css
  • apps/web/src/auth/permissions.ts
  • apps/web/src/auth/roles.ts
  • apps/web/src/components/billing/billing-shell.tsx
  • apps/web/src/components/dashboard/apartment-detail-page.tsx
  • apps/web/src/components/dashboard/building-detail-page.tsx
  • apps/web/src/components/dashboard/buildings-page.tsx
  • apps/web/src/components/dashboard/expenses-page.tsx
  • apps/web/src/components/dashboard/finance-dashboard.tsx
  • apps/web/src/components/dashboard/floor-detail-page.tsx
  • apps/web/src/components/dashboard/invoice-detail-page.tsx
  • apps/web/src/components/dashboard/invoices-page.tsx
  • apps/web/src/components/dashboard/kpi-tile.tsx
  • apps/web/src/components/dashboard/leases-page.tsx
  • apps/web/src/components/dashboard/maintenance-request-detail-page.tsx
  • apps/web/src/components/dashboard/notifications-page.tsx
  • apps/web/src/components/dashboard/org-admin-dashboard.tsx
  • apps/web/src/components/dashboard/renter-detail-page.tsx
  • apps/web/src/components/dashboard/renters-page.tsx
  • apps/web/src/components/dashboard/reports-page.tsx
  • apps/web/src/components/dashboard/staff-dashboard.tsx
  • apps/web/src/components/dashboard/support-page.tsx
  • apps/web/src/components/dashboard/tasks-page.tsx
  • apps/web/src/components/dashboard/tenant-dashboard.tsx
  • apps/web/src/components/dashboard/users-page.tsx
  • apps/web/src/components/dashboard/vendors-page.tsx
  • apps/web/src/components/layout/dashboard-sidebar.tsx
  • apps/web/src/components/layout/dashboard-topbar.tsx
  • apps/web/src/components/layout/notifications-bell.tsx
  • apps/web/src/i18n/dictionaries/ar.json
  • apps/web/src/i18n/dictionaries/en.json
  • apps/web/src/lib/dashboard-kpis.ts
  • apps/web/src/lib/plans.ts
  • apps/web/src/lib/tenant-overview.ts
  • apps/web/src/store/api/base-api.ts
  • apps/web/src/store/api/endpoints/index.ts
  • apps/web/src/store/api/endpoints/invoice-payments.api.ts
  • apps/web/src/store/api/endpoints/invoices.api.ts
  • apps/web/src/store/api/endpoints/leases.api.ts
  • apps/web/src/store/api/endpoints/notifications.api.ts
  • apps/web/src/store/api/endpoints/reports.api.ts
  • apps/web/src/store/api/endpoints/support-tickets.api.ts
  • apps/web/src/store/api/endpoints/tenant.api.ts
  • apps/web/src/store/api/tag-types.ts
  • apps/web/src/store/persist-config.ts
  • apps/web/src/tests/unit/dashboard/kpis.test.ts
  • apps/web/src/tests/unit/tenant/tenant-overview.test.ts
  • apps/web/src/types/api.ts
  • docker-compose.prod.yml
  • packages/contracts/src/index.ts
  • packages/database/.env.example
  • packages/database/README.md
  • packages/database/prisma/migrations/20260714175807_add_invoice_model/migration.sql
  • packages/database/prisma/migrations/20260714201131_add_invoice_payment_model/migration.sql
  • packages/database/prisma/migrations/20260717120000_add_support_tickets_and_notifications/migration.sql
  • packages/database/prisma/migrations/20260717130000_add_renter_user_id/migration.sql
  • packages/database/prisma/schema.prisma
  • packages/database/prisma/seeders/index.ts

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • Review on demand using usage pricing
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch mouhannad/rentals-r1-i1

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.

…ompose deploy

Rebrand — remove all "Forward Mena" from every tracked file (reviewer surface):
- UI: app.name dict (en "Property Manager" / ar "إدارة العقارات"), login, hero
  (locale-aware), footer, <title>, Swagger title.
- Identifiers: package names property-manager-{fe,be} (+ lockfile), redux-persist
  key, X-Client header, code comments, .gitignore, AGENTS.md, DB README/.env.example.
- Email From → "Property Manager <notifications@prorentallb.cloud>".
- seed-demo: org "Property Manager Demo Co", demo emails @prorentallb.cloud;
  scripts derive paths from __dirname (no hardcoded Forward-Mena path).

Pricing — 3 real prices $29 / $79 / $199 (Starter/Growth/Pro), limits unchanged:
- Catalog (BE plan-catalog + FE plans fallback) prices updated.
- Per-plan Stripe price seam: STRIPE_PRICE_{STARTER,GROWTH,PRO} → stripe.config
  `prices` → billing.service resolves perPlanPrices[planKey] (falls back to
  catalog override, then env STRIPE_PRICE_ID). env.validation adds the optional keys.
- Pricing UI: pricing-section + billing-shell render ${plan.price} (was hardcoded $20).

Deploy (Docker Compose on the shared VPS, additive-only):
- apps/api/Dockerfile, apps/web/Dockerfile (multi-stage node:22-slim; explicit
  @repo/contracts→@repo/db build order; keep tsconfig-paths at runtime; web
  standalone with NEXT_PUBLIC build-arg + placeholder server env at build).
- .dockerignore, docker-compose.prod.yml (registry images, loopback-only publishes,
  api on app+external fm-postgres net, profile-gated migrate).

Verified live at https://prorentallb.cloud: public+authed EN/AR (no "Forward Mena",
distinct Subscription/Invoices nav), webhook signature-verified, Growth checkout uses
the $79 Stripe price.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@HadiSaaibi
HadiSaaibi self-requested a review July 20, 2026 15:33
@HadiSaaibi
HadiSaaibi merged commit 183b456 into hadisaiibi-mouhannad Jul 20, 2026
1 of 2 checks passed
@HadiSaaibi
HadiSaaibi deleted the mouhannad/rentals-r1-i1 branch July 20, 2026 17:03
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