-
Notifications
You must be signed in to change notification settings - Fork 4
Implement farm invitation system with pending workflow #470
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
27 commits
Select commit
Hold shift + click to select a range
ecd4d21
feat: Add the possibilty for users to accept or reject an invitation …
SvenVw 286c44a
fix: enable autocomplete for users not in the list
SvenVw 214d2e4
fix: replace regex check for email with validator package
SvenVw da9b386
refactor: implement feedback from code review
SvenVw 4a0ae7e
refactor: make invitations generic instead of specific for farms
SvenVw 70f85ae
docs: Add invitations to the page about Authorization
SvenVw 3530577
tests: increase coverage
SvenVw 8cb6751
refactor: improve invite email
SvenVw 93ca97a
feat: add spam prevention measures for invitations
SvenVw 0d953f1
fix: import
SvenVw b04deff
refactor: improve invitation on farms overview page
SvenVw a920171
fix: invite existing user by email
SvenVw 3b09093
refactor: improve invitation card
SvenVw e340d34
Merge branch 'development' into FDM460
SvenVw 4e6f924
refactor: use Font component for consistent fonts across email clients
SvenVw 8da7fc3
refactor: implement review feedback
SvenVw dfdaa85
test: fixes
SvenVw 3a88532
refactor: implement review feedback
SvenVw e5b7799
Merge branch 'development' into FDM460
SvenVw 56b3a8c
docs: explain expire parameter
SvenVw 636e07d
fix: remove email from error logs
SvenVw d1d841a
fix: initials fallback
SvenVw 729eb11
fix: move into transaction
SvenVw a398cec
refactor: move the pending invitation into a shared component
SvenVw bdb10c0
fix: handle inactive email recipients by revoking permissions
SvenVw baa1088
fix: Guard revokePrincipalFromFarm against email-only invite targets …
SvenVw c370904
refactor: improve text when no user is know and enable to send invita…
SvenVw File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| --- | ||
| "@svenvw/fdm-app": minor | ||
| --- | ||
|
|
||
| Add the possibility for users to accept or reject an invitation to a farm, instead of having it automatically. This makes it also possible to invite non-registered users to get access to a farm after signing up. | ||
|
|
||
| - Overview page shows pending farm invitations with accept/decline actions | ||
| - Invitation email sent when a user is invited to a farm | ||
| - Farm access settings page handles accept/decline invitation intents |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| --- | ||
| "@svenvw/fdm-core": minor | ||
| --- | ||
|
|
||
| Instead of directly granting roles, `grantRoleToFarm` now creates a pending invitation (7-day expiry) that must be accepted by the target principal. The invitation system has been refactored to be resource-agnostic, so any resource type (farm, field, etc.) can be shared via invitations. | ||
|
|
||
| **New generic functions (work for any resource):** | ||
| - `createInvitation` — creates a pending invitation for a resource | ||
| - `acceptInvitation` — accepts a pending invitation and grants the role | ||
| - `declineInvitation` — declines a pending invitation | ||
| - `listPendingInvitationsForPrincipal` — lists pending invitations for a principal across all resources | ||
| - `autoAcceptInvitationsForNewUser` — auto-accepts email-based invitations on email verification | ||
|
|
||
| **Farm-specific functions:** | ||
| - `listPendingInvitationsForFarm` — lists active invitations for a farm (requires share permission) | ||
| - `listPendingInvitationsForUser` — lists pending farm invitations for the current user, enriched with farm name and org name | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| --- | ||
| "@svenvw/fdm-docs": minor | ||
| --- | ||
|
|
||
| Add invitations to the page about Authorization |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
133 changes: 133 additions & 0 deletions
133
fdm-app/app/components/blocks/email/farm-invitation.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,133 @@ | ||
| import { | ||
| Body, | ||
| Button, | ||
| Container, | ||
| Font, | ||
| Head, | ||
| Heading, | ||
| Html, | ||
| Img, | ||
| Link, | ||
| Preview, | ||
| Section, | ||
| Text, | ||
| } from "@react-email/components" | ||
| import { Tailwind } from "@react-email/tailwind" | ||
|
|
||
| interface FarmInvitationEmailProps { | ||
| farmName: string | ||
| inviterName: string | ||
| targetEmail: string | ||
| role: string | ||
| appName: string | ||
| appBaseUrl: string | ||
| logoFileName?: string | ||
| /** If true, renders a "create account" CTA for unregistered users */ | ||
| isUnregistered?: boolean | ||
| } | ||
|
|
||
| const roleLabels: Record<string, string> = { | ||
| owner: "Eigenaar", | ||
| advisor: "Adviseur", | ||
| researcher: "Onderzoeker", | ||
| } | ||
|
|
||
| export const FarmInvitationEmail = ({ | ||
| farmName, | ||
| inviterName, | ||
| targetEmail, | ||
| role, | ||
| appName, | ||
| appBaseUrl, | ||
| logoFileName = "/fdm-high-resolution-logo-transparent.png", | ||
| isUnregistered = false, | ||
| }: FarmInvitationEmailProps) => { | ||
| const logoPath = `${appBaseUrl}${logoFileName}` | ||
| const roleLabel = roleLabels[role] ?? role | ||
|
|
||
| return ( | ||
| <Html lang="nl"> | ||
| <Head> | ||
| <Font | ||
| fontFamily="Inter" | ||
| fallbackFontFamily="sans-serif" | ||
| fontWeight={400} | ||
| fontStyle="normal" | ||
| /> | ||
| </Head> | ||
| <Preview> | ||
| {`${inviterName} heeft je uitgenodigd voor toegang tot bedrijf ${farmName} in ${appName}.`} | ||
| </Preview> | ||
| <Tailwind> | ||
| <Body className="bg-white my-auto mx-auto font-sans"> | ||
| <Container className="border border-solid border-[#eaeaea] rounded my-10 mx-auto p-5 w-116.25"> | ||
| <Section className="mt-7.5"> | ||
| <Img | ||
| src={logoPath} | ||
| width="150" | ||
| alt={`${appName} Logo`} | ||
| className="my-0 mx-auto" | ||
| /> | ||
| </Section> | ||
| <Heading className="text-black text-[24px] font-normal text-center p-0 my-7.5 mx-0"> | ||
| Uitnodiging voor <b>{farmName}</b> in {appName} | ||
| </Heading> | ||
| <Text className="text-black text-[14px] leading-6"> | ||
| Hallo {targetEmail}, | ||
| </Text> | ||
| <Text className="text-black text-[14px] leading-6"> | ||
| {inviterName} heeft je uitgenodigd om toegang te | ||
| krijgen tot het bedrijf <b>{farmName}</b> in{" "} | ||
| {appName} met de rol <b>{roleLabel}</b>. | ||
| </Text> | ||
| {isUnregistered ? ( | ||
| <> | ||
| <Text className="text-black text-[14px] leading-6"> | ||
| Maak een account aan om de uitnodiging te | ||
| accepteren. Na registratie wordt je toegang | ||
| automatisch verleend. | ||
| </Text> | ||
| <Section className="mt-8 mb-2 text-center"> | ||
| <Button | ||
| href={`${appBaseUrl}/signin`} | ||
| className="bg-[#0070f3] text-white border-solid border-[#0070f3] border-2 rounded mx-6 px-3 py-3 text-[14px] font-semibold no-underline" | ||
| > | ||
| Account aanmaken | ||
| </Button> | ||
| </Section> | ||
| </> | ||
| ) : ( | ||
| <> | ||
| <Text className="text-black text-[14px] leading-6"> | ||
| Log in en accepteer of weiger de | ||
| uitnodiging. | ||
| </Text> | ||
| <Section className="mt-8 mb-2 text-center"> | ||
| <Button | ||
| href={`${appBaseUrl}/farm`} | ||
| className="bg-[#0070f3] text-white border-solid border-[#0070f3] border-2 rounded mx-6 px-3 py-3 text-[14px] font-semibold no-underline" | ||
| > | ||
| Bekijk uitnodiging | ||
| </Button> | ||
| </Section> | ||
| <Section className="mt-4 mb-8 text-center"> | ||
| <Link href={`${appBaseUrl}/farm`}> | ||
| of open {appName} om je uitnodigingen te | ||
| bekijken. | ||
| </Link> | ||
| </Section> | ||
| </> | ||
| )} | ||
| <Text className="text-black text-[14px] leading-6"> | ||
| Als je deze uitnodiging niet wilt accepteren, kun je | ||
| deze e-mail negeren. | ||
| </Text> | ||
| <Text className="text-black text-[14px] leading-6 mt-8"> | ||
| Met vriendelijke groet, <br /> Het {appName} team | ||
| </Text> | ||
| </Container> | ||
| </Body> | ||
| </Tailwind> | ||
| </Html> | ||
| ) | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.