Skip to content

feat: Tenant Portal + admin de-scope + IA audit fixes (incl. F1–F6 behavior roadmap) - #186

Merged
HadiSaaibi merged 11 commits into
hadisaiibi-mouhannadfrom
feat/tenant-portal
Jul 21, 2026
Merged

feat: Tenant Portal + admin de-scope + IA audit fixes (incl. F1–F6 behavior roadmap)#186
HadiSaaibi merged 11 commits into
hadisaiibi-mouhannadfrom
feat/tenant-portal

Conversation

@MouhannadKhodor

Copy link
Copy Markdown
Collaborator

What this PR does

Everything on this team's branch since PR #174 (R1→I1) — the PropertyManager behavior roadmap (F1–F6) plus the new Tenant Portal and cross-role IA/audit fixes. All of it is already deployed and verified live on prorentallb.cloud (this PR just brings the history onto the integration branch for review/merge).

Stacked cleanly on top of the #174 squash (11 commits, zero conflicts). Zero DB migrations.

🏠 Tenant Portal + IA fixes (newest — the focus of this round)

  • Dedicated tenant portal at /[lang]/portal with its own light "Resident portal" chrome (not the admin shell). Tenant-only.
    • Home = lease history + apartments; Available units (display-only); Support / Requests (apartment-scoped MaintenanceRequest — the channel that actually reaches maintenance staff); Profile (identity + Keycloak account console).
  • Admin de-scoped: removed the availableUnits area/nav/route entirely and retired express-interest; removed a dead TenantDashboard branch from the admin router. → No tenant-only surface remains in any staff/admin panel; /dashboard/available-units 404s.
  • Admin-provisioned tenant logins: optional "Create portal login" on the create-renter form (email = username, permanent password; reuses createUserWithPassword, links Renter.renterUserId, does not consume a plan staff seat).
  • Routing isolation seam: ROLE_DASHBOARD.tenant → /portal; proxy.ts protects /portal and bounces tenant↔dashboard both ways; dashboard RSC guard as defense-in-depth.
  • IA1 audit correctness/contract fixes: finance activity feed now emits invoice/invoice_payment/expense (was Stripe subscription.*); billing tightened to ORG_ADMIN; supervisor.payments → none; billing page routed through the permission matrix; removed the dead-end category:'maintenance' support-ticket option.

🧰 F1–F6 behavior roadmap (previously deployed, first time on the integration branch)

F1 quick-win correctness · F2 maintenance/work-order workflow (+ the real supervisor-Tasks fix) · F3 recurring rent invoices + WO→tenant charge · F4 leasing correctness (date-overlap, expiry sweep, start-date policy) · F5 notifications & activity actor · F6 profile page + topbar-menu crash fix. Plus the PaginatedResponse items render fix.

Verification

  • api jest 338 pass / 4 skip · web vitest 30/30 · web check-types + next build clean · api build EXIT 0 · i18n parity 1181 == 1181.
  • Verified live on prorentallb.cloud: tenant login → /portal (lease history renders); admin sidebar has no "Available units"; /dashboard/available-units → 404; /health 200. Pre-deploy on a local restart (both EN/AR): tenant↔dashboard isolation both ways, anon-protected, and a tenant submitted a maintenance request end-to-end.

Notes for the reviewer

  • Already live: deployed via Docker Compose (images api b1f52c8898e6 / web ec95540229eb; rollback tag v1-pre-tp-20260721). Merging this PR just records the source history.
  • Not in this PR (pre-existing uncommitted infra on the working tree, consistent with prior sprints): the api-slim Dockerfile, apps/api/package.json, package-lock, a webhooks currentPeriodEnd tweak, and keycloak-theme/. The prod images were built from those locally. Happy to fold them into a separate infra PR if you'd like them tracked.
  • Deferred (agreed): IA2 — a "transition-only" AccessLevel for maintenance.tasks; maintenance rent/deposit visibility on the org-wide leases list.
  • One demo maintenance request ("Portal test — kitchen sink leak", demo.tenant / Unit G-01) exists on the prod DB from live verification — harmless demo data, deletable.

🤖 Generated with Claude Code

Mouhannad and others added 11 commits July 21, 2026 21:37
…to `items`

The shared PaginatedResponse<T> typed its array field as `data`, but the
backend list services (payments.service, timeline.service) return `items`.
The client `unwrap` transform keys on a top-level `data` property only to
peel the optional ApiEnvelope, so it passed the `{items,total,page,limit}`
object through untouched — and 5 FE reads of `?.data ?? []` then resolved
to `undefined ?? []`, rendering the Payments list and every "Recent
activity" feed empty on production.

Fix: flip the contract array field to `items`, update the 5 paginated reads
(payments-page, finance/staff/tenant dashboards, timeline-feed), and add BE
regression specs asserting the list envelope is keyed by `items`. The two
notification reads keep `.data` — NotificationListResponse is a distinct
shape with a real `data` field. Web-only (packages/contracts + apps/web).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… nav, sign-out reachability, lease date guard, task renter autofill, expense→WO label

Sprint F1 of the Property Manager behavior-fix roadmap (workflow-anchored fixes
from docs/propertymanager_bugs.txt). All six are contained, verified changes:

- F1.1 [Q34/35] Logout returns to the PUBLIC origin. federated-logout now builds
  post_logout_redirect_uri from x-forwarded-host/host + x-forwarded-proto instead
  of request.url (which leaked the Next container host behind nginx). KC client
  prorentallb-web now also has post.logout.redirect.uris="+" (whitelists the
  redirect) — set additively out-of-band.
- F1.2 [Q11/24] Nav "Payments" → "Subscription Payments" (en/ar) and moved beside
  Subscription in the sidebar; the page is subscription-only.
- F1.3 [Q17] Sidebar Sign-out always reachable: dashboard layout is now a fixed
  app-shell (h-screen/overflow-hidden), <main> scrolls, <nav> scrolls independently.
- F1.4 [Q8] Server guard: lease endDate must be strictly after startDate on
  create() and update() (assertValidDateRange) — the DTO only validated dates in
  isolation. Regression spec added (api leases 24/24).
- F1.5 [Q12] New maintenance-request autofills the renter from the chosen
  apartment's active lease.
- F1.6 [Q16a] Expense→work-order cell shows the #<id> label, not the raw cuid
  (full WorkOrder.number is F2.1).

i18n parity held (en==ar 1023). Verified: web check-types clean, web vitest 30/30,
api leases jest 24/24, gitnexus detect_changes footprint matches intent.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ers, supervisor deep-links, task filters

Sprint F2 of the Property Manager behavior roadmap (workflow W5).

F2.1 [Q16] Human-readable WorkOrder.number (decision D4: org-scoped sequential
  WO-000123). Migration adds WorkOrder.number (nullable→backfill per org by
  createdAt→NOT NULL + @@unique([orgId,number])). create() assigns the next
  number under a per-org pg advisory lock (unique index is the backstop).
  Shared formatWorkOrderNumber() in @repo/contracts; WorkOrderResponse gains
  number + numberLabel. Shown on the maintenance-request detail Work Orders
  table and — via a new ExpenseResponse.workOrderNumberLabel (BE joins
  WorkOrder.number) — on the expenses "Work order" column (replaces the F1.6
  #<id> stopgap). Backfills existing rows.

F2.2 [Q27] Maintenance "assigned to me". New GET /work-orders/assigned-to-me
  (WorkOrdersOverviewController + findAssignedToCaller, enriched with request/
  apartment/building + active-first ordering). Maintenance dashboard shows a
  "My work orders" section (assigned WOs at the top, deep-linked to the
  request). Maintenance-request detail highlights rows assigned to the caller
  ("Assigned to you" badge; callerSub decoded from the access-token JWT).

F2.3 [Q23] Supervisor dashboard deep-links. KPI tiles now link to filtered
  lists (?status=open|in_progress|resolved, ?priority=urgent) and "Needs
  attention" rows deep-link to /tasks/:id (were all generic /tasks). Tasks page
  reads those query params and gained status/priority filter controls.

F2.4 [Q12] Reverse renter→apartment autofill in the new-request dialog
  (renter with one active lease backfills building/floor/apartment), with a
  ping-pong guard on both directions.

i18n parity held (en==ar 1033). Verified: api build EXIT 0 + jest 277 pass/4
skip; web check-types clean, next build EXIT 0, vitest 30/30, eslint clean.

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

Root cause of the partner's Q23 ("clicking Tasks as supervisor bounces to the
dashboard"): proxy.ts (edge middleware) kept an INLINE hand-maintained copy of
the permission map that had drifted from auth/permissions.ts — its `tasks` entry
listed only org_admin + maintenance, omitting supervisor's `readonly`. So the
sidebar (which reads the real matrix) showed the Tasks link, but the edge
redirected supervisor off /dashboard/tasks -> /dashboard. Same class of bug
would recur on any future drift.

Fix: import PERMISSION_MATRIX + getAccess from auth/permissions.ts and gate
directly from it (renters->buildings alias + "unknown sub-area -> allow through"
fallback matching the previous semantics). permissions.ts is a plain object +
pure functions (type-only Role import) so it stays edge-runtime-safe. Divergence
between nav visibility and edge gating is now structurally impossible.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ant charge, reports/expense clarity

Sprint F3 of the Property Manager behavior roadmap (workflows W4/W7).

F3.1 [Q10] Recurring rent invoices (decision D1: monthly, anchored to the lease
  start day-of-month, generated within 7 days before the due date, idempotent
  per (lease, billing period), no proration). New recurring-invoices module:
  generateForLease (UTC-normalized, short-month clamp, 7-day lead window,
  active+start-date guard, idempotent via new Invoice.billingPeriod +
  @@unique([leaseId,billingPeriod]) with a P2002 race backstop) -> runForOrg/
  runAll; a BullMQ repeatable daily job (06:00) as the scheduler; and
  POST /recurring-invoices/run (org_admin) on-demand, surfaced as a
  "Generate rent invoices" button on the Invoices page.

F3.2 [Q13] Bill a tenant from a work order (decision D2: opt-in, default OFF).
  WorkOrder gains chargeToTenant + tenantChargeAmount + tenantChargedAt. On
  completion (create-as-completed or update-into-completed) with the flag + an
  amount + not-yet-charged, adds one invoice line item to the apartment's active
  lease (category other, due +30d) and stamps tenantChargedAt (idempotent; no
  active lease -> warn, no charge, no throw). FE: charge toggle + amount + badge.

F3.3 [Q14/39/40] Reports clarity — income = collected rent (cash basis), net =
  income - expenses (helper text/tooltips; no math change).

F3.4 [Q15] Expense != invoice clarification copy.

Two migrations (Invoice.billingPeriod; WorkOrder tenant-charge fields). i18n
parity en==ar 1046. Verified: api build EXIT 0 + jest 293 pass/4 skip (incl. 12
recurring + 5 WO-charge); web check-types clean, vitest 30/30, next build EXIT 0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…weep, start-date policy

Sprint F4 of the Property Manager behavior roadmap (workflow W3). No migration.

F4.1 [Q9] True date-overlap lease check. Replaced the old "block if ANY lease is
  effectively-active now" invariant (which made future/back-to-back leases
  impossible) with a real end-exclusive range-overlap check
  (newStart < existingEnd && existingStart < newEnd) against non-terminated
  leases on the apartment — in create() (active/draft) and update() (when dates/
  status change; self-excluded). Allows disjoint future/past + back-to-back;
  rejects real overlaps.

F4.2 [Q9] Apartment status auto-expiry. New apartment-status-sweep module: reverts
  `occupied` apartments with no effectively-active lease back to `vacant`
  (occupied-only filter leaves maintenance/unavailable alone). Daily BullMQ
  repeatable job (07:00) + POST /apartments/status-sweep (org_admin) on-demand
  trigger (BFF proxy added).

F4.3 [Q8, decision D3] Start-date policy. create() rejects a NEW active lease with
  a past start (400) unless CreateLeaseBody.recordExisting is set; FE adds a
  "Record an existing lease" checkbox that unlocks back-dating (and sets the flag)
  + a start-date `min=today` on both lease-create forms.

i18n parity en==ar 1052. Verified: api build EXIT 0 + jest 312 pass/4 skip (incl.
overlap/policy/sweep specs); web check-types clean, vitest 30/30, next build EXIT 0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…feed shows the actor

Sprint F5 of the Property Manager behavior roadmap (workflow W8). No migration.

F5.1 [Q20] Expand notification triggers (were: only support-ticket open/status).
  Added best-effort in-app notifications (alongside the existing timeline.emit,
  each skips silently when the recipient sub is null) for:
  - work_order.assigned → the assignee (WorkOrdersService.create + genuine
    reassignment in update; never notifies the actor about themself)
  - invoice.issued → the tenant (InvoicesService.create + the F3.1 recurring
    auto-generator) via Lease→Renter.renterUserId
  - invoice.payment_recorded → the tenant (InvoicePaymentsService.create)
  - lease.created → the tenant (LeasesService.create)
  Each module now imports NotificationsModule. Role-fanout triggers
  (payment-failed→finance, lease-expiring→admin, maintenance-created→supervisors)
  are left as documented // TODO F5.1 follow-up (need a shared org-members-by-role
  helper + a new expiry sweep).

F5.2 [Q18] Activity feed shows WHO. TimelineService.findForCaller now enriches
  each event with actorName by resolving distinct actorId subs via
  KeycloakAdminService.getUser (name→email→null), deduped per page + cached
  per-sub with a 5-min TTL; KC failure → null, never throws. Contract
  TimelineEvent.actorName added; the timeline feed renders "by {actor}" with a
  "System" fallback for actor-less/system events; dict wired into both feed
  call sites (+ localized the Activity page heading).

i18n parity en==ar 1054. Verified: api build EXIT 0 + full jest 332 pass/4 skip;
web check-types clean, vitest 30/30, next build EXIT 0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ble units" showcase

Sprint F6 (final) of the Property Manager behavior roadmap (workflows W10/W11).
No migration.

F6.1 [Q33] Profile page. New /{lang}/dashboard/profile (requireSession, any role)
  showing the signed-in identity (name, email, localized role, org name + status,
  reusing the existing role/status label maps) + a "Manage account" link to the
  Keycloak account console (password/2FA). A "Profile" item was added to the
  topbar user dropdown. Fixes the partner's "profile couldn't load" (there was no
  /profile route).

F6.2 [Q5] Renter apartment showcase (foundational slice; the full inquiry/
  application flow remains a documented v2 follow-up). New GET /available-units
  (org-scoped vacant apartments, all member roles incl. tenant, no
  building-access filter) → AvailableUnit[]. New "Available units" page + sidebar
  nav (permission area availableUnits: tenant full, others readonly). Per-unit
  "Express interest" reuses the existing support-ticket create flow (no new data
  model) to open a lead ticket referencing the unit.

i18n parity en==ar 1081. Verified: api build EXIT 0 + full jest 335 pass/4 skip
(incl. 3 available-units); web check-types clean, vitest 30/30, next build EXIT 0
(both new routes registered).

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

The topbar user-dropdown "Profile" item using base-ui `render={<Link/>}` threw
Base UI error #31 when the menu opened (crashed the page into the error
boundary). Switched it to a plain DropdownMenuItem with
`onClick={() => router.push(...)}` — the same reliable pattern the sibling
"Sign out" item uses — which navigates without the base-ui render-composition
edge case. Removed the now-unused next/link import.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…Group (real Q33 root cause)

The topbar avatar → user menu crashed the page into the error boundary the
moment it opened (Base UI error #31) — this is the partner's actual Q33
("clicking profile next to theme changes → this page couldn't load"; the
control next to the theme toggle IS this menu). Root cause: DropdownMenuLabel
maps to base-ui `Menu.GroupLabel`, which must live inside a `Menu.Group`; it was
rendered bare inside DropdownMenuContent, so base-ui threw on open. (The buildings
`⋯` menu, which has no label, opened fine — confirming the isolation.)

Fix: render the user-name header as a plain styled <div> instead of
DropdownMenuLabel (it's a display header, not a semantic group label); dropped
the now-unused import. The menu now opens and the Profile item works.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Tenant Portal (TP1-TP4) + IA1 audit fixes (docs/TENANT-PORTAL-ROADMAP.md).
- TP1: admin mints tenant login from create-renter form (email=username,
  permanent pwd; reuses createUserWithPassword, links renterUserId, no staff seat)
- TP2: new /[lang]/portal route group (own chrome) + routing isolation
  (roles.ts tenant->/portal, proxy.ts tenant<->dashboard both ways, dashboard RSC guard)
- TP3: portal content — lease history + apartments, available-units display-only,
  apartment-scoped Support/Requests, profile
- TP4: removed available-units from admin (area/nav/route) + retired express-interest;
  removed dead TenantDashboard admin branch
- IA1: finance timeline domain fix, billing->ORG_ADMIN, supervisor.payments->none,
  billing page->matrix, dead support-maintenance category removed
Zero migrations. i18n parity 1181. api jest 338/4skip, web vitest 30/30.

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

coderabbitai Bot commented Jul 21, 2026

Copy link
Copy Markdown

Important

Review skipped

Too many files!

This PR contains 104 files, which is 4 over the limit of 100.

To get a review, narrow the scope:
• coderabbit review --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.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 59d42dbe-d75a-4428-9c71-f019d97db562

📥 Commits

Reviewing files that changed from the base of the PR and between 183b456 and 3472741.

📒 Files selected for processing (105)
  • apps/api/src/app.module.ts
  • apps/api/src/modules/apartment-status-sweep/apartment-status-sweep-scheduler.service.ts
  • apps/api/src/modules/apartment-status-sweep/apartment-status-sweep.constants.ts
  • apps/api/src/modules/apartment-status-sweep/apartment-status-sweep.controller.ts
  • apps/api/src/modules/apartment-status-sweep/apartment-status-sweep.module.ts
  • apps/api/src/modules/apartment-status-sweep/apartment-status-sweep.processor.ts
  • apps/api/src/modules/apartment-status-sweep/apartment-status-sweep.service.spec.ts
  • apps/api/src/modules/apartment-status-sweep/apartment-status-sweep.service.ts
  • apps/api/src/modules/available-units/available-units.controller.ts
  • apps/api/src/modules/available-units/available-units.module.ts
  • apps/api/src/modules/available-units/available-units.service.spec.ts
  • apps/api/src/modules/available-units/available-units.service.ts
  • apps/api/src/modules/billing/billing.controller.ts
  • apps/api/src/modules/expenses/expenses.service.spec.ts
  • apps/api/src/modules/expenses/expenses.service.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/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/dto/create-lease.dto.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/payments/payments.controller.ts
  • apps/api/src/modules/payments/payments.service.spec.ts
  • apps/api/src/modules/recurring-invoices/recurring-invoices-scheduler.service.ts
  • apps/api/src/modules/recurring-invoices/recurring-invoices.constants.ts
  • apps/api/src/modules/recurring-invoices/recurring-invoices.controller.ts
  • apps/api/src/modules/recurring-invoices/recurring-invoices.module.ts
  • apps/api/src/modules/recurring-invoices/recurring-invoices.processor.ts
  • apps/api/src/modules/recurring-invoices/recurring-invoices.service.spec.ts
  • apps/api/src/modules/recurring-invoices/recurring-invoices.service.ts
  • apps/api/src/modules/renters/dto/create-renter.dto.ts
  • apps/api/src/modules/renters/renters.service.spec.ts
  • apps/api/src/modules/renters/renters.service.ts
  • apps/api/src/modules/tenant/tenant.service.spec.ts
  • apps/api/src/modules/tenant/tenant.service.ts
  • apps/api/src/modules/timeline/timeline.service.spec.ts
  • apps/api/src/modules/timeline/timeline.service.ts
  • apps/api/src/modules/work-orders/dto/create-work-order.dto.ts
  • apps/api/src/modules/work-orders/dto/update-work-order.dto.ts
  • apps/api/src/modules/work-orders/work-order-formatter.ts
  • apps/api/src/modules/work-orders/work-orders-overview.controller.ts
  • apps/api/src/modules/work-orders/work-orders.module.ts
  • apps/api/src/modules/work-orders/work-orders.service.spec.ts
  • apps/api/src/modules/work-orders/work-orders.service.ts
  • apps/web/src/app/[lang]/dashboard/billing/page.tsx
  • apps/web/src/app/[lang]/dashboard/layout.tsx
  • apps/web/src/app/[lang]/dashboard/page.tsx
  • apps/web/src/app/[lang]/dashboard/profile/page.tsx
  • apps/web/src/app/[lang]/dashboard/tasks/[id]/page.tsx
  • apps/web/src/app/[lang]/dashboard/timeline/page.tsx
  • apps/web/src/app/[lang]/portal/available-units/page.tsx
  • apps/web/src/app/[lang]/portal/layout.tsx
  • apps/web/src/app/[lang]/portal/page.tsx
  • apps/web/src/app/[lang]/portal/profile/page.tsx
  • apps/web/src/app/[lang]/portal/support/page.tsx
  • apps/web/src/app/api/apartments/status-sweep/route.ts
  • apps/web/src/app/api/auth/federated-logout/route.ts
  • apps/web/src/app/api/available-units/route.ts
  • apps/web/src/app/api/recurring-invoices/run/route.ts
  • apps/web/src/app/api/work-orders/assigned-to-me/route.ts
  • apps/web/src/auth/permissions.ts
  • apps/web/src/auth/roles.ts
  • apps/web/src/components/dashboard/apartment-detail-page.tsx
  • apps/web/src/components/dashboard/expenses-page.tsx
  • apps/web/src/components/dashboard/finance-dashboard.tsx
  • apps/web/src/components/dashboard/invoices-page.tsx
  • apps/web/src/components/dashboard/leases-page.tsx
  • apps/web/src/components/dashboard/maintenance-request-detail-page.tsx
  • apps/web/src/components/dashboard/org-admin-dashboard.tsx
  • apps/web/src/components/dashboard/payments-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/timeline-feed.tsx
  • apps/web/src/components/layout/dashboard-sidebar.tsx
  • apps/web/src/components/layout/dashboard-topbar.tsx
  • apps/web/src/components/portal/portal-available-units.tsx
  • apps/web/src/components/portal/portal-coming-soon.tsx
  • apps/web/src/components/portal/portal-dict.fallback.ts
  • apps/web/src/components/portal/portal-dict.ts
  • apps/web/src/components/portal/portal-home.tsx
  • apps/web/src/components/portal/portal-shell.tsx
  • apps/web/src/components/portal/portal-support.tsx
  • apps/web/src/components/ui/checkbox.tsx
  • apps/web/src/i18n/dictionaries/ar.json
  • apps/web/src/i18n/dictionaries/en.json
  • apps/web/src/proxy.ts
  • apps/web/src/store/api/endpoints/available-units.api.ts
  • apps/web/src/store/api/endpoints/index.ts
  • apps/web/src/store/api/endpoints/invoices.api.ts
  • apps/web/src/store/api/endpoints/work-orders.api.ts
  • apps/web/src/store/api/tag-types.ts
  • apps/web/src/tests/unit/tenant/tenant-overview.test.ts
  • packages/contracts/src/index.ts
  • packages/database/prisma/migrations/20260721010000_add_work_order_number/migration.sql
  • packages/database/prisma/migrations/20260721020000_add_invoice_billing_period/migration.sql
  • packages/database/prisma/migrations/20260721030000_add_work_order_tenant_charge/migration.sql
  • packages/database/prisma/schema.prisma

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 feat/tenant-portal

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.

@HadiSaaibi
HadiSaaibi merged commit 39474aa into hadisaiibi-mouhannad Jul 21, 2026
1 of 2 checks passed
@HadiSaaibi
HadiSaaibi deleted the feat/tenant-portal branch July 21, 2026 19:22
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