@meetreeve/module-team 0.0.0-stage → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,159 @@
1
+ import * as react from 'react';
2
+ import { ReactNode } from 'react';
3
+
4
+ /**
5
+ * Roles the org-members router knows. `billing_manager` is a legacy alias
6
+ * the backend still accepts; no surface offers it from the invite dialog.
7
+ * Rows may still carry a role this union doesn't name, so row fields are
8
+ * typed `string` and the panel renders unknown roles verbatim.
9
+ */
10
+ type TeamRole = "owner" | "admin" | "billing_manager" | "member" | "viewer";
11
+ /** A role the invite dialog and the role picker can hand out (never `owner`). */
12
+ type AssignableRole = Exclude<TeamRole, "owner" | "billing_manager">;
13
+ /** One live member of the org (`OrganizationMember` row). `GET /api/org/members`. */
14
+ interface TeamMember {
15
+ /** OrganizationMember.id — the id every member route takes. */
16
+ id: string;
17
+ user_id: string;
18
+ email: string | null;
19
+ role: string;
20
+ is_default: boolean;
21
+ /** ISO-8601. */
22
+ created_at: string;
23
+ }
24
+ /** One invitation record. `GET /api/org/invitations`. */
25
+ interface Invitation {
26
+ id: string;
27
+ email: string;
28
+ role: string;
29
+ invited_by_email: string | null;
30
+ /** pending | accepted | expired | revoked */
31
+ status: string;
32
+ /** ISO-8601. */
33
+ created_at: string;
34
+ /** ISO-8601. */
35
+ expires_at: string;
36
+ accepted_at: string | null;
37
+ /**
38
+ * Spaces the invitee joins on accept (DEV-15988 / reeve-services DEV-15559).
39
+ * The backend always sends it; optional so a backend that predates it
40
+ * still type-checks as "no pre-set spaces".
41
+ */
42
+ spaces?: InvitationSpace[];
43
+ }
44
+ /** A space role. */
45
+ type SpaceRole = "member" | "admin";
46
+ /** One pre-set space on an invitation (`GET /api/org/invitations` item). */
47
+ interface InvitationSpace {
48
+ id: string;
49
+ name: string;
50
+ role: SpaceRole;
51
+ }
52
+ /** One space an invite (or a pending invitation) should land the invitee in. */
53
+ interface InviteSpace {
54
+ space_id: string;
55
+ role: SpaceRole;
56
+ }
57
+ /**
58
+ * One of the org's spaces, as the invite dialog's Spaces picker lists it
59
+ * (reeve-frontend maps `GET /api/org/rbac/spaces` onto this). Every space is
60
+ * offered, the default one included: nobody joins a space automatically
61
+ * (DEV-16808). `can_manage: false` rows are shown disabled.
62
+ */
63
+ interface SpaceOption {
64
+ id: string;
65
+ name: string;
66
+ kind?: string;
67
+ is_default: boolean;
68
+ can_manage: boolean;
69
+ }
70
+ /** `POST /api/org/invite`. */
71
+ interface InviteMemberResponse {
72
+ message: string;
73
+ email: string;
74
+ role: string;
75
+ invitation_id?: string;
76
+ }
77
+ /**
78
+ * The data-access boundary the panel is built against. Every method takes
79
+ * the org it acts on: there is no ambient "current org" anywhere in this
80
+ * package, so a request can never be issued for an org other than the one
81
+ * the caller named (DEV-8592). `createTeamClient` is the reference
82
+ * implementation; a host may supply its own.
83
+ */
84
+ interface TeamClient {
85
+ listMembers(orgId: string): Promise<TeamMember[]>;
86
+ updateMemberRole(orgId: string, memberId: string, role: string): Promise<void>;
87
+ removeMember(orgId: string, memberId: string): Promise<void>;
88
+ /**
89
+ * `spaces` goes in the body whenever it is passed, `[]` included (a
90
+ * one-space org sends `[]`, DEV-16808); omitted keeps the pre-DEV-15988
91
+ * request.
92
+ */
93
+ inviteMember(orgId: string, email: string, role: string, spaces?: InviteSpace[]): Promise<InviteMemberResponse>;
94
+ listInvitations(orgId: string): Promise<Invitation[]>;
95
+ revokeInvitation(orgId: string, invitationId: string): Promise<void>;
96
+ resendInvitation(orgId: string, invitationId: string): Promise<void>;
97
+ }
98
+
99
+ type TeamNotice = {
100
+ kind: "success" | "error";
101
+ message: string;
102
+ };
103
+ /** What `renderInvitationActions` gets besides the invitation. */
104
+ interface InvitationActionHelpers {
105
+ /** Refetch members and invitations, as Resend does after it lands. */
106
+ reload: () => Promise<void>;
107
+ }
108
+ interface TeamMembersPanelProps {
109
+ /**
110
+ * Data access. `createTeamClient({ fetch })` from `@meetreeve/module-team/client`.
111
+ * Pass a stable instance (module scope or `useMemo`): a new client refetches.
112
+ */
113
+ client: TeamClient;
114
+ /**
115
+ * The org whose team this panel shows. Required: every request is issued
116
+ * for this org, and changing it resets the panel (DEV-8592).
117
+ */
118
+ orgId: string;
119
+ /**
120
+ * Roles the invite dialog AND the per-member role picker offer, in order.
121
+ * Default admin/member/viewer. The operator cockpit (DEV-2439) passes
122
+ * `["admin", "member"]`.
123
+ */
124
+ inviteRoles?: AssignableRole[];
125
+ /**
126
+ * The signed-in user's email: marks their row "you", and that row's role
127
+ * decides which role changes the picker offers (DEV-18190). Without it, or
128
+ * when no member matches, every role shows as a plain label.
129
+ */
130
+ currentUserEmail?: string | null;
131
+ /**
132
+ * Host-owned per-member controls (Studio's Budget / Capabilities dialogs).
133
+ * Rendered at the end of every member row, owners included; the host
134
+ * decides per member what to show.
135
+ */
136
+ renderMemberActions?: (member: TeamMember) => ReactNode;
137
+ /**
138
+ * Host-owned per-invitation controls, rendered next to Resend / Revoke on
139
+ * every pending invitation. Call `reload` after a change the row shows.
140
+ */
141
+ renderInvitationActions?: (invitation: Invitation, helpers: InvitationActionHelpers) => ReactNode;
142
+ /**
143
+ * The org's spaces for the invite dialog's Spaces picker. When omitted the
144
+ * dialog has no picker and invites go out exactly as before. Called each
145
+ * time the dialog opens; until it answers, the invite is held, since an org
146
+ * with several spaces refuses an invite that names none (422
147
+ * `spaces_required`).
148
+ */
149
+ loadSpaceOptions?: () => Promise<SpaceOption[]>;
150
+ /** Called with every success/error notice the panel shows inline. */
151
+ onNotice?: (notice: TeamNotice) => void;
152
+ }
153
+ /**
154
+ * Keyed on `orgId`: an org change remounts the body, which is what resets
155
+ * every piece of per-org state at once — no reset effect to forget.
156
+ */
157
+ declare function TeamMembersPanel(props: TeamMembersPanelProps): react.JSX.Element;
158
+
159
+ export { type AssignableRole, type Invitation, type InvitationActionHelpers, type InvitationSpace, type InviteMemberResponse, type InviteSpace, type SpaceOption, type SpaceRole, type TeamClient, type TeamMember, TeamMembersPanel, type TeamMembersPanelProps, type TeamNotice, type TeamRole };
@@ -0,0 +1,159 @@
1
+ import * as react from 'react';
2
+ import { ReactNode } from 'react';
3
+
4
+ /**
5
+ * Roles the org-members router knows. `billing_manager` is a legacy alias
6
+ * the backend still accepts; no surface offers it from the invite dialog.
7
+ * Rows may still carry a role this union doesn't name, so row fields are
8
+ * typed `string` and the panel renders unknown roles verbatim.
9
+ */
10
+ type TeamRole = "owner" | "admin" | "billing_manager" | "member" | "viewer";
11
+ /** A role the invite dialog and the role picker can hand out (never `owner`). */
12
+ type AssignableRole = Exclude<TeamRole, "owner" | "billing_manager">;
13
+ /** One live member of the org (`OrganizationMember` row). `GET /api/org/members`. */
14
+ interface TeamMember {
15
+ /** OrganizationMember.id — the id every member route takes. */
16
+ id: string;
17
+ user_id: string;
18
+ email: string | null;
19
+ role: string;
20
+ is_default: boolean;
21
+ /** ISO-8601. */
22
+ created_at: string;
23
+ }
24
+ /** One invitation record. `GET /api/org/invitations`. */
25
+ interface Invitation {
26
+ id: string;
27
+ email: string;
28
+ role: string;
29
+ invited_by_email: string | null;
30
+ /** pending | accepted | expired | revoked */
31
+ status: string;
32
+ /** ISO-8601. */
33
+ created_at: string;
34
+ /** ISO-8601. */
35
+ expires_at: string;
36
+ accepted_at: string | null;
37
+ /**
38
+ * Spaces the invitee joins on accept (DEV-15988 / reeve-services DEV-15559).
39
+ * The backend always sends it; optional so a backend that predates it
40
+ * still type-checks as "no pre-set spaces".
41
+ */
42
+ spaces?: InvitationSpace[];
43
+ }
44
+ /** A space role. */
45
+ type SpaceRole = "member" | "admin";
46
+ /** One pre-set space on an invitation (`GET /api/org/invitations` item). */
47
+ interface InvitationSpace {
48
+ id: string;
49
+ name: string;
50
+ role: SpaceRole;
51
+ }
52
+ /** One space an invite (or a pending invitation) should land the invitee in. */
53
+ interface InviteSpace {
54
+ space_id: string;
55
+ role: SpaceRole;
56
+ }
57
+ /**
58
+ * One of the org's spaces, as the invite dialog's Spaces picker lists it
59
+ * (reeve-frontend maps `GET /api/org/rbac/spaces` onto this). Every space is
60
+ * offered, the default one included: nobody joins a space automatically
61
+ * (DEV-16808). `can_manage: false` rows are shown disabled.
62
+ */
63
+ interface SpaceOption {
64
+ id: string;
65
+ name: string;
66
+ kind?: string;
67
+ is_default: boolean;
68
+ can_manage: boolean;
69
+ }
70
+ /** `POST /api/org/invite`. */
71
+ interface InviteMemberResponse {
72
+ message: string;
73
+ email: string;
74
+ role: string;
75
+ invitation_id?: string;
76
+ }
77
+ /**
78
+ * The data-access boundary the panel is built against. Every method takes
79
+ * the org it acts on: there is no ambient "current org" anywhere in this
80
+ * package, so a request can never be issued for an org other than the one
81
+ * the caller named (DEV-8592). `createTeamClient` is the reference
82
+ * implementation; a host may supply its own.
83
+ */
84
+ interface TeamClient {
85
+ listMembers(orgId: string): Promise<TeamMember[]>;
86
+ updateMemberRole(orgId: string, memberId: string, role: string): Promise<void>;
87
+ removeMember(orgId: string, memberId: string): Promise<void>;
88
+ /**
89
+ * `spaces` goes in the body whenever it is passed, `[]` included (a
90
+ * one-space org sends `[]`, DEV-16808); omitted keeps the pre-DEV-15988
91
+ * request.
92
+ */
93
+ inviteMember(orgId: string, email: string, role: string, spaces?: InviteSpace[]): Promise<InviteMemberResponse>;
94
+ listInvitations(orgId: string): Promise<Invitation[]>;
95
+ revokeInvitation(orgId: string, invitationId: string): Promise<void>;
96
+ resendInvitation(orgId: string, invitationId: string): Promise<void>;
97
+ }
98
+
99
+ type TeamNotice = {
100
+ kind: "success" | "error";
101
+ message: string;
102
+ };
103
+ /** What `renderInvitationActions` gets besides the invitation. */
104
+ interface InvitationActionHelpers {
105
+ /** Refetch members and invitations, as Resend does after it lands. */
106
+ reload: () => Promise<void>;
107
+ }
108
+ interface TeamMembersPanelProps {
109
+ /**
110
+ * Data access. `createTeamClient({ fetch })` from `@meetreeve/module-team/client`.
111
+ * Pass a stable instance (module scope or `useMemo`): a new client refetches.
112
+ */
113
+ client: TeamClient;
114
+ /**
115
+ * The org whose team this panel shows. Required: every request is issued
116
+ * for this org, and changing it resets the panel (DEV-8592).
117
+ */
118
+ orgId: string;
119
+ /**
120
+ * Roles the invite dialog AND the per-member role picker offer, in order.
121
+ * Default admin/member/viewer. The operator cockpit (DEV-2439) passes
122
+ * `["admin", "member"]`.
123
+ */
124
+ inviteRoles?: AssignableRole[];
125
+ /**
126
+ * The signed-in user's email: marks their row "you", and that row's role
127
+ * decides which role changes the picker offers (DEV-18190). Without it, or
128
+ * when no member matches, every role shows as a plain label.
129
+ */
130
+ currentUserEmail?: string | null;
131
+ /**
132
+ * Host-owned per-member controls (Studio's Budget / Capabilities dialogs).
133
+ * Rendered at the end of every member row, owners included; the host
134
+ * decides per member what to show.
135
+ */
136
+ renderMemberActions?: (member: TeamMember) => ReactNode;
137
+ /**
138
+ * Host-owned per-invitation controls, rendered next to Resend / Revoke on
139
+ * every pending invitation. Call `reload` after a change the row shows.
140
+ */
141
+ renderInvitationActions?: (invitation: Invitation, helpers: InvitationActionHelpers) => ReactNode;
142
+ /**
143
+ * The org's spaces for the invite dialog's Spaces picker. When omitted the
144
+ * dialog has no picker and invites go out exactly as before. Called each
145
+ * time the dialog opens; until it answers, the invite is held, since an org
146
+ * with several spaces refuses an invite that names none (422
147
+ * `spaces_required`).
148
+ */
149
+ loadSpaceOptions?: () => Promise<SpaceOption[]>;
150
+ /** Called with every success/error notice the panel shows inline. */
151
+ onNotice?: (notice: TeamNotice) => void;
152
+ }
153
+ /**
154
+ * Keyed on `orgId`: an org change remounts the body, which is what resets
155
+ * every piece of per-org state at once — no reset effect to forget.
156
+ */
157
+ declare function TeamMembersPanel(props: TeamMembersPanelProps): react.JSX.Element;
158
+
159
+ export { type AssignableRole, type Invitation, type InvitationActionHelpers, type InvitationSpace, type InviteMemberResponse, type InviteSpace, type SpaceOption, type SpaceRole, type TeamClient, type TeamMember, TeamMembersPanel, type TeamMembersPanelProps, type TeamNotice, type TeamRole };