docs: clarify email verification implementation details

- Added DB integration details for external email system
- Clarified share-id validation (404 on invalid)
- Enhanced subsequent matching to include tenantEmail
- Specified exact UI placement for email status indicators
- Fixed typo: EmailStatus.Verifies → EmailStatus.Verified

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
Knee Cola
2025-12-29 18:01:10 +01:00
parent 1cafe33386
commit a51476d82b

View File

@@ -8,16 +8,18 @@ To prevent missuse and ensure that the e-mail is correct, before an e-mail addre
This verification is done via a link sent to the tenant in a verification-request e-mail, which is sent to the tenant automatically when the landloard (app user) assigns this e-mail address to a BillingLocation. This verification is done via a link sent to the tenant in a verification-request e-mail, which is sent to the tenant automatically when the landloard (app user) assigns this e-mail address to a BillingLocation.
Sending of this verification-request e-mail is handled by a system separate from NextJS app in `web-app` workspace. Sending of this verification-request e-mail is handled by a system separate from NextJS app in `web-app` workspace. It detects newly assigned addresses from their status bein equal `EmailStatus.Unverified`. The two systems don't talk to each other at all - what's holding them together is the DB.
### Implementation details ### Implementation details
Verification link points to the NextJS app in `web-app` workspace at path `/email/verify/[share-id]` (share-id is calculated using `generateShareId` from `/home/kneecola/projects/evidencija-rezija/web-app/app/lib/shareChecksum.ts`). Verification link points to the NextJS app in `web-app` workspace at path `/email/verify/[share-id]` (share-id is calculated using `generateShareId` from `/home/kneecola/projects/evidencija-rezija/web-app/app/lib/shareChecksum.ts`).
The web page served at this path cerifies if the [share-id] is correct and if not it shows 404 page.
The web page served at this path contains an text explanation and "Verify e-mail" button. The web page served at this path contains an text explanation and "Verify e-mail" button.
The text includes the following information: The text includes the following information:
* what the web app is about - very short into * what the web app is about - very short into
* why the e-mail was sent = because the landloard of the property X configured the rent (`BillingLocation.rentDueNotification`) and/or utility bills (`BillingLocation.billFwdStrategy`) to be delivered to that e-mail address * why the e-mail was sent = because the landloard of the property `BillingLocation.name` configured the rent (`BillingLocation.rentDueNotification`) and/or utility bills (`BillingLocation.billFwdStrategy`) to be delivered to that e-mail address
* what will hapen if he/she clicks on the "Verify e-mail" button = they will be receiving rent due (`BillingLocation.rentDueNotification`) or utility bills due (`BillingLocation.billFwdStrategy`) notification or both - 2x a month - depending on the config set by the landloard * what will hapen if he/she clicks on the "Verify e-mail" button = they will be receiving rent due (`BillingLocation.rentDueNotification`) or utility bills due (`BillingLocation.billFwdStrategy`) notification or both - 2x a month - depending on the config set by the landloard
* opt-out infomation (they can ignore this e-mail, but can also opt-out at any moment) * opt-out infomation (they can ignore this e-mail, but can also opt-out at any moment)
@@ -25,36 +27,42 @@ If the user clicks the button "Verify e-mail" this triggers update of `BillingLo
Here's the expected stats flow: Here's the expected stats flow:
* landloard/app user assigns an an new address top `BillingLocation` -> `BillingLocation.tenantEmailStatus` is set to `EmailStatus.Unverified` * landloard/app user assigns an an new address to `BillingLocation` -> `BillingLocation.tenantEmailStatus` is set to `EmailStatus.Unverified`
* a verification si sent to the entered e-mail address -> `BillingLocation.tenantEmailStatus` is set to `EmailStatus.VerificationPending` * an automated system detects that a new address was set (as indicated by `EmailStatus.Unverified` status), it then sets verification-email -> `BillingLocation.tenantEmailStatus` is set to `EmailStatus.VerificationPending`
* tenant click the link from the verification-requets e-mail -> `BillingLocation.tenantEmailStatus` is set to `EmailStatus.Verified` * tenant click the link from the verification-requets e-mail -> `BillingLocation.tenantEmailStatus` is set to `EmailStatus.Verified`
**Note:** status is updated for the `BillingLocation` inidcated by locationID (decoded from `[share-id]` param), and all subsequent matching `BillingLocation` records (matched by comparing `BillingLocation.name` and `BillingLocation.userId`). Similar pattern is already implemented in `updateOrAddLocation` fn in `locationAction.ts` (`updateScope === "subsequent"`). **Note:** status is updated for the `BillingLocation` inidcated by locationID (decoded from `[share-id]` param), and all subsequent (later in time) matching `BillingLocation` records (matched by comparing `BillingLocation.name`, `BillingLocation.userId` and `BillingLocation.tenantEmail`). Similar pattern is already implemented in `updateOrAddLocation` fn in `locationAction.ts` (`updateScope === "subsequent"`).
## E-mail unsubscribe ## E-mail unsubscribe
Tenant can out-out from receiving e-mail notifications at any time via an `unsubscribe` link included at the end of every mail sent to the tenant. Tenant can out-out from receiving e-mail notifications at any time via an `unsubscribe` link included at the end of every mail sent to the tenant.
### Implementation details ### Implementation details
Verification link points to the NextJS app in `web-app` workspace at path `/email/unsubscribe/[share-id]` (share-id is calculated using `generateShareId` from `/home/kneecola/projects/evidencija-rezija/web-app/app/lib/shareChecksum.ts`). Verification link points to the NextJS app in `web-app` workspace at path `/email/unsubscribe/[share-id]` (share-id is calculated using `generateShareId` from `/home/kneecola/projects/evidencija-rezija/web-app/app/lib/shareChecksum.ts` ... search of examples of how this function is used).
The web page served at this path contains an text explanation and "Confirm unsubscribe" button. The web page served at this path contains an text explanation and "Confirm unsubscribe" button.
The text includes the following information: The text includes the following information:
* what the web app is about - very short into * what the web app is about - very short into
* why are they receiveing e-mails from this page = because their landlord for property X has configured the app to deliver rent due or utility bills due notification or both to that address * why are they receiveing e-mails from this page = because their landlord for property `BillingLocation.name` has configured the app to deliver rent due or utility bills due notification or both to that address
* what will hapen if they click on "Confirm unsubscribe" = they will no longer receive rent due / utility bull due reminders * what will hapen if they click on "Confirm unsubscribe" = they will no longer receive rent due / utility bull due reminders
E-mail address's verification status is tracked via `BillingLocation.tenantEmailStatus`, which is set to `EmailStatus.Unsubscribed`. E-mail address's verification status is tracked via `BillingLocation.tenantEmailStatus`, which is set to `EmailStatus.Unsubscribed`.
**Note:** status is updated for the `BillingLocation` inidcated by locationID (decoded from `[share-id]` param), and all subsequent matching `BillingLocation` records (matched by comparing `BillingLocation.name` and `BillingLocation.userId`). Similar pattern is already implemented in `updateOrAddLocation` fn in `locationAction.ts` (`updateScope === "subsequent"`). **Note:** status is updated for the `BillingLocation` inidcated by locationID (decoded from `[share-id]` param), and all subsequent (later in time) matching `BillingLocation` records (matched by comparing `BillingLocation.name`, `BillingLocation.userId` and `BillingLocation.tenantEmail`). Similar pattern is already implemented in `updateOrAddLocation` fn in `locationAction.ts` (`updateScope === "subsequent"`).
## E-mail status in `LocationCard.tsx` ## E-mail status in `LocationCard.tsx`
If the e-mail is not in `EmailStatus.Verifies` state for a given location, then this will be indicated in `LocationCard.tsx` as a sibling of `total-payed-label` block (`<div className="flex ml-1">`) If the e-mail is not in `EmailStatus.Verified` state for a given location, then this will be indicated in `LocationCard.tsx` as a sibling of `total-payed-label` block (`<div className="flex ml-1">`)
## E-mail status in `LocationEditForm.tsx` ## E-mail status in `LocationEditForm.tsx`
Current e-mail status will be indicated as a sibling of `<div id="tenantEmail-error">`. Current e-mail status will be indicated as a sibling of:
```
{/* Email status indicator should go here */}
<div id="tenantEmail-error" aria-live="polite" aria-atomic="true">
...
```
Use appropriate utf-8 icon for each status. Use appropriate utf-8 icon for each status.
# Logical Units of work # Logical Units of work