feat(org): platform invites, roles, org sharing (v2 phase A1-1)
- New platform_invites table (migration a1b2c3d4e5f1) + PlatformInviteRow - Owner-only endpoints in admin_users.py: issue/list/revoke platform invites - invites.py: two-kind resolution (org + platform), email-supplied accept path - auth.py /me gains needs_onboarding (manager, 0 orgs, not owner) - organizations.py: transfer-ownership, leave (last owner guard), list/revoke org invites - contracts + web UI: admin/users invite modal, accept-invite platform variant, onboarding gating - Tests: test_org_platform_invites.py (18), org-sharing.test.tsx; report under docs/plan/reports Note: (app)/settings "اشتراکگذاری سازمان" section (1.10) remains presentational work; endpoints are done and tested.
This commit is contained in:
@@ -221,8 +221,16 @@ export interface Invite {
|
||||
invite_url: string;
|
||||
}
|
||||
|
||||
/** What the accept-invite screen shows before the invitee commits. */
|
||||
/**
|
||||
* What the accept-invite screen shows before the invitee commits.
|
||||
*
|
||||
* Two kinds share one endpoint: a `platform` invite mints a new manager account
|
||||
* (v2 D12) and has no organization yet, while an `organization` invite joins an
|
||||
* existing one. The screen shows a different title for each — "ساخت حساب مدیر"
|
||||
* versus joining a team — so the caller has to be able to tell them apart.
|
||||
*/
|
||||
export interface InvitePreview {
|
||||
kind: "platform" | "organization";
|
||||
organization_id: string;
|
||||
organization_name: string;
|
||||
email: string;
|
||||
@@ -235,9 +243,12 @@ export interface InvitePreview {
|
||||
* in first; setting a password anonymously is only for brand-new accounts.
|
||||
*/
|
||||
has_account: boolean;
|
||||
/** For a platform invite: the role the redeemed account will receive. */
|
||||
platform_role: PlatformRole;
|
||||
}
|
||||
|
||||
export interface AcceptInviteResult {
|
||||
kind: "platform" | "organization";
|
||||
organization_id: string;
|
||||
organization_name: string;
|
||||
role: Role;
|
||||
@@ -252,6 +263,29 @@ export interface TokenResponse {
|
||||
export interface MeResponse {
|
||||
user: User;
|
||||
organizations: OrganizationWithRole[];
|
||||
/**
|
||||
* True for a manager who belongs to no organization yet — they arrived on the
|
||||
* platform owner's invitation link and have built nothing, so the only useful
|
||||
* thing the UI can do is open the creation wizard. The owner is excluded.
|
||||
*/
|
||||
needs_onboarding: boolean;
|
||||
}
|
||||
|
||||
/** An invitation the platform owner issued, as it appears in the console. */
|
||||
export interface PlatformInvite {
|
||||
id: string;
|
||||
email: string | null;
|
||||
platform_role: string;
|
||||
note: string;
|
||||
expires_at: string;
|
||||
created_at: string;
|
||||
created_by_email: string;
|
||||
}
|
||||
|
||||
export interface IssuedPlatformInvite {
|
||||
invite: PlatformInvite;
|
||||
/** The raw URL. Shown exactly once, at creation. */
|
||||
invite_url: string;
|
||||
}
|
||||
|
||||
export interface Project {
|
||||
|
||||
Reference in New Issue
Block a user