From 237b1a4242d06b99b415efb722cb0918ee670f99 Mon Sep 17 00:00:00 2001 From: Jonathan Bourdon Date: Thu, 30 Apr 2026 15:46:06 -0400 Subject: [PATCH] docs: adds workspace-invites feature and tasks --- docs/FEATURES/workspace-invites.md | 66 +++++++++++++++++++ ...01-backend-invite-acceptance-foundation.md | 46 +++++++++++++ .../002-invite-email-delivery.md | 37 +++++++++++ .../003-frontend-invite-acceptance.md | 39 +++++++++++ .../004-invite-management-polish.md | 42 ++++++++++++ 5 files changed, 230 insertions(+) create mode 100644 docs/FEATURES/workspace-invites.md create mode 100644 docs/TASKS/workspace-invites/001-backend-invite-acceptance-foundation.md create mode 100644 docs/TASKS/workspace-invites/002-invite-email-delivery.md create mode 100644 docs/TASKS/workspace-invites/003-frontend-invite-acceptance.md create mode 100644 docs/TASKS/workspace-invites/004-invite-management-polish.md diff --git a/docs/FEATURES/workspace-invites.md b/docs/FEATURES/workspace-invites.md new file mode 100644 index 0000000..5b533c4 --- /dev/null +++ b/docs/FEATURES/workspace-invites.md @@ -0,0 +1,66 @@ +# Feature: Workspace Invites + +## Status + +Draft + +## Goal + +Allow workspace managers to invite teammates, clients, and providers into a workspace and allow invited people to accept access with the correct role and workspace scope. + +## User Stories + +- As a workspace manager, I want to invite a person by email and role so that they can access the right workspace. +- As an invited person, I want to accept an invite from a link so that I can join the workspace without administrator help. +- As an invited person without an account, I want to create my account as part of accepting the invite. +- As an invited person with an account, I want the accepted workspace to appear after sign-in. +- As a workspace manager, I want to see pending, accepted, cancelled, and expired invites so that I understand who has access or still needs follow-up. + +## Domain Rules + +- Workspace invites belong to exactly one workspace. +- Invite email matching should use normalized email addresses. +- Pending invite tokens must be single-use and should expire. +- Accepted invites must grant the invited role and a workspace scope claim for the invite workspace. +- Signed-in users may accept invites only when their account email matches the invite email. +- New users may create an account during invite acceptance, then receive the invited role and workspace scope. +- Accepted, cancelled, and expired invites must not be accepted again. +- Managers can create, list, cancel, and resend invites only for workspaces they can manage. +- Managers must not be able to create duplicate pending invites for the same normalized email in the same workspace. +- Invite acceptance must be auditable through stored status and timestamp changes. + +## Proposed Statuses + +- `Pending` +- `Accepted` +- `Cancelled` +- `Expired` + +## Backend Surface + +- `POST /api/workspaces/{workspaceId:guid}/invites` +- `GET /api/workspaces/{workspaceId:guid}/invites` +- `POST /api/workspace-invites/{inviteId:guid}/resend` +- `POST /api/workspace-invites/{inviteId:guid}/cancel` +- `GET /api/workspace-invites/accept/{token}` +- `POST /api/workspace-invites/accept` + +## Frontend Surface + +- Workspace settings members tab for invite creation and invite management. +- Public invite acceptance route. +- Authenticated invite acceptance route for signed-in users. +- Registration/sign-in handoff for invited users without a usable session. + +## Done When + +- [ ] Invite creation sends an email with an acceptance link. +- [ ] Acceptance link validates a pending, unexpired, single-use token. +- [ ] Signed-in users can accept matching-email invites. +- [ ] New users can register through the invite path. +- [ ] Accepted invites grant role and workspace scope. +- [ ] Accepted users see the workspace after token refresh or sign-in. +- [ ] Managers can cancel and resend pending invites. +- [ ] Invite statuses are represented without magic strings. +- [ ] Backend tests cover create, duplicate, accept, expired, cancelled, and email mismatch cases. +- [ ] OpenAPI and frontend API usage are updated after contract changes. diff --git a/docs/TASKS/workspace-invites/001-backend-invite-acceptance-foundation.md b/docs/TASKS/workspace-invites/001-backend-invite-acceptance-foundation.md new file mode 100644 index 0000000..be1f21a --- /dev/null +++ b/docs/TASKS/workspace-invites/001-backend-invite-acceptance-foundation.md @@ -0,0 +1,46 @@ +# Task: Backend Workspace Invite Foundation + +## Goal + +Implement the backend data model and endpoints needed to create, list, and accept workspace invites. + +## Feature Spec + +- `docs/FEATURES/workspace-invites.md` + +## Scope + +- Add a `WorkspaceInvite` persistence model with workspace id, normalized email, role, status, inviter, token data, timestamps, and expiration. +- Add invite statuses for `Pending`, `Accepted`, `Cancelled`, and `Expired` without magic strings. +- Add a manager-only endpoint to create workspace invites. +- Add a manager-only endpoint to list workspace invites. +- Prevent duplicate pending invites for the same normalized email in the same workspace. +- Add a public endpoint to resolve an invite token for display-safe invite details. +- Add an accept endpoint that validates token, status, expiration, and email match. +- On acceptance, grant the invited role and `KnownClaims.WorkspaceScope` claim to the user. +- Mark the invite as accepted in the same transaction as access grants. +- Add backend tests for create, list, pending, accepted, expired, cancelled, duplicate, and email mismatch paths. + +## Constraints + +- Keep backend code under `backend/src/Socialize.Api`. +- Keep workspace feature code under `Modules/Workspaces`. +- Do not expose raw token values in manager invite lists. +- Frontend invite screens are covered by task 003 and task 004. + +## Likely Files + +- `backend/src/Socialize.Api/Modules/Workspaces/Data/WorkspaceInvite.cs` +- `backend/src/Socialize.Api/Modules/Workspaces/Data/WorkspaceInviteStatuses.cs` +- `backend/src/Socialize.Api/Modules/Workspaces/Data/WorkspaceModelConfiguration.cs` +- `backend/src/Socialize.Api/Modules/Workspaces/Handlers/CreateWorkspaceInvite.cs` +- `backend/src/Socialize.Api/Modules/Workspaces/Handlers/GetWorkspaceInvites.cs` +- `backend/src/Socialize.Api/Modules/Workspaces/Handlers/*Accept*Invite*.cs` +- `backend/tests/Socialize.Tests/` + +## Validation + +```bash +dotnet build backend/Socialize.slnx +dotnet test backend/Socialize.slnx +``` diff --git a/docs/TASKS/workspace-invites/002-invite-email-delivery.md b/docs/TASKS/workspace-invites/002-invite-email-delivery.md new file mode 100644 index 0000000..72440d8 --- /dev/null +++ b/docs/TASKS/workspace-invites/002-invite-email-delivery.md @@ -0,0 +1,37 @@ +# Task: Invite Email Delivery + +## Goal + +Send invited users an acceptance link when a workspace invite is created or resent. + +## Feature Spec + +- `docs/FEATURES/workspace-invites.md` + +## Scope + +- Generate acceptance URLs from configured website options. +- Send an invite email after successful invite creation. +- Add a manager-only resend endpoint for pending, unexpired invites. +- Avoid sending email if invite creation fails. +- Do not include sensitive token values in logs. + +## Constraints + +- Use the repository email infrastructure. +- Do not introduce a new email provider. +- Keep email copy concise and product-specific. + +## Likely Files + +- `backend/src/Socialize.Api/Modules/Workspaces/Handlers/CreateWorkspaceInvite.cs` +- `backend/src/Socialize.Api/Modules/Workspaces/Handlers/ResendWorkspaceInvite.cs` +- `backend/src/Socialize.Api/Infrastructure/Emailer/` +- `backend/src/Socialize.Api/Infrastructure/Configuration/WebsiteOptions.cs` + +## Validation + +```bash +dotnet build backend/Socialize.slnx +dotnet test backend/Socialize.slnx +``` diff --git a/docs/TASKS/workspace-invites/003-frontend-invite-acceptance.md b/docs/TASKS/workspace-invites/003-frontend-invite-acceptance.md new file mode 100644 index 0000000..d9709df --- /dev/null +++ b/docs/TASKS/workspace-invites/003-frontend-invite-acceptance.md @@ -0,0 +1,39 @@ +# Task: Frontend Invite Acceptance + +## Goal + +Build the invite acceptance route and connect it to registration or sign-in. + +## Feature Spec + +- `docs/FEATURES/workspace-invites.md` + +## Scope + +- Add a public route for invite acceptance links. +- Load display-safe invite details from the token. +- If the user is signed in with the invited email, allow direct acceptance. +- If the user is signed in with a different email, show a clear mismatch state. +- If the user is signed out, route them to sign in or register and resume acceptance afterward. +- Refresh the current user profile after acceptance so the new workspace appears. + +## Constraints + +- Frontend runtime config must flow through `frontend/src/config.js`. +- Feature-owned code belongs under `frontend/src/features/workspaces`. +- Do not add a marketing-style landing page for invite acceptance. + +## Likely Files + +- `frontend/src/router/router.js` +- `frontend/src/features/workspaces/` +- `frontend/src/features/workspaces/stores/workspaceStore.js` +- `frontend/src/locales/en.json` +- `frontend/src/locales/fr.json` + +## Validation + +```bash +cd frontend +npm run build +``` diff --git a/docs/TASKS/workspace-invites/004-invite-management-polish.md b/docs/TASKS/workspace-invites/004-invite-management-polish.md new file mode 100644 index 0000000..3eb9b24 --- /dev/null +++ b/docs/TASKS/workspace-invites/004-invite-management-polish.md @@ -0,0 +1,42 @@ +# Task: Invite Management Polish + +## Goal + +Make workspace invite management complete for managers after acceptance exists. + +## Feature Spec + +- `docs/FEATURES/workspace-invites.md` + +## Scope + +- Show invite statuses in workspace settings. +- Add manager actions to cancel and resend pending invites. +- Hide or disable actions for accepted, cancelled, and expired invites. +- Decide whether the default list shows all invites or only active pending invites. +- Ensure accepted users appear in the active members list after acceptance. +- Update OpenAPI and frontend API usage after backend contract changes. + +## Constraints + +- Keep workspace settings within repository layout conventions. +- Avoid broad member-management refactors. + +## Likely Files + +- `backend/src/Socialize.Api/Modules/Workspaces/Handlers/GetWorkspaceInvites.cs` +- `backend/src/Socialize.Api/Modules/Workspaces/Handlers/CancelWorkspaceInvite.cs` +- `frontend/src/features/workspaces/views/WorkspaceSettingsView.vue` +- `frontend/src/features/workspaces/stores/workspaceStore.js` +- `shared/openapi/openapi.json` +- `frontend/src/api/schema.d.ts` + +## Validation + +```bash +dotnet build backend/Socialize.slnx +dotnet test backend/Socialize.slnx +./scripts/update-openapi.sh +cd frontend +npm run build +```