@eminuckan/spine 0.2.4 → 0.3.1
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.
- package/CHANGELOG.md +19 -0
- package/CODE_OF_CONDUCT.md +39 -0
- package/CONTRIBUTING.md +78 -0
- package/MAINTAINERS.md +43 -0
- package/README.md +170 -135
- package/ROADMAP.md +58 -0
- package/SECURITY.md +37 -0
- package/dist/access/evaluator.d.ts.map +1 -1
- package/dist/access/index.js +1 -1
- package/dist/access/types.d.ts +10 -2
- package/dist/access/types.d.ts.map +1 -1
- package/dist/{access-DlAqsVGQ.js → access-CGrsDMwu.js} +9 -7
- package/dist/access-CGrsDMwu.js.map +1 -0
- package/dist/api-client/api-config.server.d.ts +27 -0
- package/dist/api-client/api-config.server.d.ts.map +1 -1
- package/dist/api-client/error-codes.d.ts +6 -31
- package/dist/api-client/error-codes.d.ts.map +1 -1
- package/dist/api-client/index.js +2 -2
- package/dist/api-client/server.d.ts +1 -1
- package/dist/api-client/server.d.ts.map +1 -1
- package/dist/api-client/server.js +3 -3
- package/dist/{api-client-CZapXIF8.js → api-client-CgZUMukO.js} +9 -26
- package/dist/api-client-CgZUMukO.js.map +1 -0
- package/dist/auth/auth.server.d.ts.map +1 -1
- package/dist/auth/authorization-parameters.d.ts +5 -0
- package/dist/auth/authorization-parameters.d.ts.map +1 -0
- package/dist/auth/route-protection.server.d.ts +20 -4
- package/dist/auth/route-protection.server.d.ts.map +1 -1
- package/dist/auth/server.d.ts +1 -1
- package/dist/auth/server.d.ts.map +1 -1
- package/dist/auth/server.js +5 -5
- package/dist/auth/types.d.ts +24 -2
- package/dist/auth/types.d.ts.map +1 -1
- package/dist/{auth.server-B3ODGIX4.js → auth.server-D5oiz9aF.js} +62 -53
- package/dist/auth.server-D5oiz9aF.js.map +1 -0
- package/dist/{auth.server-wdKsjQsn.js → auth.server-lC94lmlx.js} +3 -3
- package/dist/identity/identity-context.server.d.ts +2 -0
- package/dist/identity/identity-context.server.d.ts.map +1 -1
- package/dist/identity/identity-store.d.ts +19 -2
- package/dist/identity/identity-store.d.ts.map +1 -1
- package/dist/identity/index.js +2 -2
- package/dist/identity/server.js +2 -2
- package/dist/identity/types.d.ts +3 -1
- package/dist/identity/types.d.ts.map +1 -1
- package/dist/{identity-yT2ijZF0.js → identity-C5un-xAs.js} +104 -60
- package/dist/identity-C5un-xAs.js.map +1 -0
- package/dist/index.js +8 -8
- package/dist/logging/index.js +1 -1
- package/dist/{logging-_h2mi3L9.js → logging-DviiAkLx.js} +2 -2
- package/dist/{logging-_h2mi3L9.js.map → logging-DviiAkLx.js.map} +1 -1
- package/dist/permissions/constants.d.ts +3 -0
- package/dist/permissions/constants.d.ts.map +1 -1
- package/dist/permissions/index.js +1 -1
- package/dist/permissions/permission-store.d.ts +1 -1
- package/dist/permissions/types.d.ts +1 -1
- package/dist/permissions/types.d.ts.map +1 -1
- package/dist/{permissions-OUK8Eql2.js → permissions-_n1hIG7Q.js} +6 -12
- package/dist/permissions-_n1hIG7Q.js.map +1 -0
- package/dist/query-client/types.d.ts +9 -9
- package/dist/query-client/types.d.ts.map +1 -1
- package/dist/query-client-Bh3TyB8W.js.map +1 -1
- package/dist/react-router/index.js +8 -8
- package/dist/react-router/server.js +14 -14
- package/dist/{redis-session-storage.server-D0-72FV-.js → redis-session-storage.server-v2VcpFsQ.js} +2 -2
- package/dist/{redis-session-storage.server-D0-72FV-.js.map → redis-session-storage.server-v2VcpFsQ.js.map} +1 -1
- package/dist/{server-B7JN86_0.js → server-B6PCxc-T.js} +31 -83
- package/dist/server-B6PCxc-T.js.map +1 -0
- package/dist/{server-DeBlxoaW.js → server-C96NMFBy.js} +17 -9
- package/dist/server-C96NMFBy.js.map +1 -0
- package/dist/{server-Ce9jQRdL.js → server-Ca-3DJf2.js} +44 -11
- package/dist/server-Ca-3DJf2.js.map +1 -0
- package/dist/{server-BmiuV28c.js → server-Dxsww9v9.js} +5 -4
- package/dist/server-Dxsww9v9.js.map +1 -0
- package/dist/server.js +14 -14
- package/dist/signalr/index.js +1 -1
- package/dist/signalr/types.d.ts +1 -1
- package/dist/{signalr-CSfCz1Ik.js → signalr-k-70VHKo.js} +3 -2
- package/dist/signalr-k-70VHKo.js.map +1 -0
- package/dist/tenant/index.js +2 -2
- package/dist/tenant/server.js +4 -4
- package/dist/tenant/tenant-context.d.ts +17 -1
- package/dist/tenant/tenant-context.d.ts.map +1 -1
- package/dist/tenant/tenant-cookie.server.d.ts.map +1 -1
- package/dist/tenant/tenant-store.d.ts +54 -9
- package/dist/tenant/tenant-store.d.ts.map +1 -1
- package/dist/tenant/tenant.server.d.ts +20 -1
- package/dist/tenant/tenant.server.d.ts.map +1 -1
- package/dist/tenant/types.d.ts +29 -9
- package/dist/tenant/types.d.ts.map +1 -1
- package/dist/tenant-DfLemb6n.js +384 -0
- package/dist/tenant-DfLemb6n.js.map +1 -0
- package/docs/adapters.md +87 -0
- package/docs/architecture.md +193 -0
- package/docs/backend-adaptation.md +249 -0
- package/docs/codex-for-oss-application.md +39 -0
- package/docs/codex-maintenance.md +47 -0
- package/docs/installation.md +213 -0
- package/docs/module-reference.md +207 -0
- package/docs/releasing.md +92 -0
- package/package.json +21 -3
- package/dist/access-DlAqsVGQ.js.map +0 -1
- package/dist/api-client-CZapXIF8.js.map +0 -1
- package/dist/auth.server-B3ODGIX4.js.map +0 -1
- package/dist/identity-yT2ijZF0.js.map +0 -1
- package/dist/permissions-OUK8Eql2.js.map +0 -1
- package/dist/server-B7JN86_0.js.map +0 -1
- package/dist/server-BmiuV28c.js.map +0 -1
- package/dist/server-Ce9jQRdL.js.map +0 -1
- package/dist/server-DeBlxoaW.js.map +0 -1
- package/dist/signalr-CSfCz1Ik.js.map +0 -1
- package/dist/tenant-BErqEENp.js +0 -256
- package/dist/tenant-BErqEENp.js.map +0 -1
package/dist/tenant-BErqEENp.js
DELETED
|
@@ -1,256 +0,0 @@
|
|
|
1
|
-
import { create } from "zustand";
|
|
2
|
-
import { createContext, useContext, useEffect } from "react";
|
|
3
|
-
import { jsx } from "react/jsx-runtime";
|
|
4
|
-
import { devtools } from "zustand/middleware";
|
|
5
|
-
|
|
6
|
-
//#region src/tenant/tenant-store.ts
|
|
7
|
-
/**
|
|
8
|
-
* Organization fetch endpoint (configurable)
|
|
9
|
-
*/
|
|
10
|
-
let organizationEndpoint = "/api/organization/get";
|
|
11
|
-
let tenantSwitchEndpoint = "/api/tenant/switch";
|
|
12
|
-
/**
|
|
13
|
-
* Configure tenant store endpoints
|
|
14
|
-
*/
|
|
15
|
-
function configureTenantEndpoints(config) {
|
|
16
|
-
if (config.organizationEndpoint) organizationEndpoint = config.organizationEndpoint;
|
|
17
|
-
if (config.tenantSwitchEndpoint) tenantSwitchEndpoint = config.tenantSwitchEndpoint;
|
|
18
|
-
}
|
|
19
|
-
const useTenantStore = create()(devtools((set, get) => ({
|
|
20
|
-
currentTenant: null,
|
|
21
|
-
availableTenants: [],
|
|
22
|
-
memberships: new Map(),
|
|
23
|
-
organizations: new Map(),
|
|
24
|
-
isLoading: false,
|
|
25
|
-
isSwitching: false,
|
|
26
|
-
setCurrentTenant: (tenantId) => set({ currentTenant: tenantId }),
|
|
27
|
-
setAvailableTenants: (tenants) => {
|
|
28
|
-
set({ availableTenants: tenants });
|
|
29
|
-
const { organizations, fetchOrganization } = get();
|
|
30
|
-
tenants.forEach((tenantId) => {
|
|
31
|
-
if (!organizations.has(tenantId)) fetchOrganization(tenantId);
|
|
32
|
-
});
|
|
33
|
-
},
|
|
34
|
-
setMemberships: (memberships) => {
|
|
35
|
-
const membershipMap = new Map();
|
|
36
|
-
memberships.forEach((membership) => {
|
|
37
|
-
membershipMap.set(membership.tenantId, membership);
|
|
38
|
-
});
|
|
39
|
-
set({ memberships: membershipMap });
|
|
40
|
-
},
|
|
41
|
-
setOrganization: (tenantId, data) => set((state) => ({ organizations: new Map(state.organizations).set(tenantId, data) })),
|
|
42
|
-
addOrganization: (tenantId, data) => {
|
|
43
|
-
set((state) => {
|
|
44
|
-
const newOrganizations = new Map(state.organizations);
|
|
45
|
-
newOrganizations.set(tenantId, data);
|
|
46
|
-
return { organizations: newOrganizations };
|
|
47
|
-
});
|
|
48
|
-
},
|
|
49
|
-
setLoading: (loading) => set({ isLoading: loading }),
|
|
50
|
-
setSwitching: (switching) => set({ isSwitching: switching }),
|
|
51
|
-
fetchOrganization: async (tenantId) => {
|
|
52
|
-
try {
|
|
53
|
-
const response = await fetch(`${organizationEndpoint}/${tenantId}`);
|
|
54
|
-
if (response.ok) {
|
|
55
|
-
const result = await response.json();
|
|
56
|
-
if (result.success && result.data) get().setOrganization(tenantId, result.data);
|
|
57
|
-
}
|
|
58
|
-
} catch (error) {
|
|
59
|
-
console.error("Failed to fetch organization data for tenant:", tenantId, error);
|
|
60
|
-
}
|
|
61
|
-
},
|
|
62
|
-
switchTenant: async (tenantId) => {
|
|
63
|
-
const { availableTenants } = get();
|
|
64
|
-
if (!availableTenants.includes(tenantId)) {
|
|
65
|
-
console.error("Tenant not available:", tenantId);
|
|
66
|
-
return;
|
|
67
|
-
}
|
|
68
|
-
set({ isSwitching: true });
|
|
69
|
-
try {
|
|
70
|
-
const response = await fetch(tenantSwitchEndpoint, {
|
|
71
|
-
method: "POST",
|
|
72
|
-
headers: { "Content-Type": "application/json" },
|
|
73
|
-
body: JSON.stringify({ tenantId })
|
|
74
|
-
});
|
|
75
|
-
if (response.ok) {
|
|
76
|
-
const result = await response.json();
|
|
77
|
-
if (result.success) {
|
|
78
|
-
set({ currentTenant: tenantId });
|
|
79
|
-
const { organizations, fetchOrganization } = get();
|
|
80
|
-
if (!organizations.has(tenantId)) await fetchOrganization(tenantId);
|
|
81
|
-
window.location.reload();
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
} catch (error) {
|
|
85
|
-
console.error("Failed to switch tenant:", error);
|
|
86
|
-
set({ isSwitching: false });
|
|
87
|
-
}
|
|
88
|
-
},
|
|
89
|
-
getMembership: (tenantId) => {
|
|
90
|
-
const { memberships } = get();
|
|
91
|
-
return memberships.get(tenantId) || null;
|
|
92
|
-
},
|
|
93
|
-
getCurrentMembership: () => {
|
|
94
|
-
const { currentTenant, memberships } = get();
|
|
95
|
-
if (!currentTenant) return null;
|
|
96
|
-
return memberships.get(currentTenant) || null;
|
|
97
|
-
},
|
|
98
|
-
getTenantName: (tenantId) => {
|
|
99
|
-
const { memberships, organizations } = get();
|
|
100
|
-
const membership = memberships.get(tenantId);
|
|
101
|
-
if (membership?.tenantName) return membership.tenantName;
|
|
102
|
-
if (membership?.organizationName) return membership.organizationName;
|
|
103
|
-
return organizations.get(tenantId)?.name || tenantId;
|
|
104
|
-
},
|
|
105
|
-
getOrganization: (tenantId) => {
|
|
106
|
-
const { organizations } = get();
|
|
107
|
-
return organizations.get(tenantId) || null;
|
|
108
|
-
},
|
|
109
|
-
getCurrentOrganization: () => {
|
|
110
|
-
const { currentTenant, organizations } = get();
|
|
111
|
-
if (!currentTenant) return null;
|
|
112
|
-
return organizations.get(currentTenant) || null;
|
|
113
|
-
}
|
|
114
|
-
}), { name: "TenantStore" }));
|
|
115
|
-
/**
|
|
116
|
-
* Initialize store with server data
|
|
117
|
-
*/
|
|
118
|
-
function initializeTenantStore(currentTenant, availableTenants, memberships) {
|
|
119
|
-
const store = useTenantStore.getState();
|
|
120
|
-
store.setCurrentTenant(currentTenant);
|
|
121
|
-
store.setAvailableTenants(availableTenants);
|
|
122
|
-
if (memberships) store.setMemberships(memberships);
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
//#endregion
|
|
126
|
-
//#region src/tenant/tenant-context.tsx
|
|
127
|
-
const TenantContext = createContext(null);
|
|
128
|
-
function TenantProvider({ children, initialTenant = null, initialTenants = [], initialMemberships = [], onTenantChange }) {
|
|
129
|
-
const currentTenant = useTenantStore((state) => state.currentTenant);
|
|
130
|
-
const availableTenants = useTenantStore((state) => state.availableTenants);
|
|
131
|
-
const memberships = useTenantStore((state) => state.memberships);
|
|
132
|
-
const organizations = useTenantStore((state) => state.organizations);
|
|
133
|
-
const isLoading = useTenantStore((state) => state.isLoading || state.isSwitching);
|
|
134
|
-
const currentOrganization = useTenantStore((state) => state.getCurrentOrganization());
|
|
135
|
-
const currentMembership = useTenantStore((state) => state.getCurrentMembership());
|
|
136
|
-
const setCurrentTenant = useTenantStore((state) => state.setCurrentTenant);
|
|
137
|
-
const setAvailableTenants = useTenantStore((state) => state.setAvailableTenants);
|
|
138
|
-
const setMemberships = useTenantStore((state) => state.setMemberships);
|
|
139
|
-
const switchTenantAction = useTenantStore((state) => state.switchTenant);
|
|
140
|
-
const fetchOrganization = useTenantStore((state) => state.fetchOrganization);
|
|
141
|
-
const getOrganization = useTenantStore((state) => state.getOrganization);
|
|
142
|
-
const getMembership = useTenantStore((state) => state.getMembership);
|
|
143
|
-
const getTenantName = useTenantStore((state) => state.getTenantName);
|
|
144
|
-
useEffect(() => {
|
|
145
|
-
if (initialTenant) setCurrentTenant(initialTenant);
|
|
146
|
-
if (initialTenants.length > 0) setAvailableTenants(initialTenants);
|
|
147
|
-
if (initialMemberships.length > 0) setMemberships(initialMemberships);
|
|
148
|
-
}, [
|
|
149
|
-
initialTenant,
|
|
150
|
-
initialTenants.join(","),
|
|
151
|
-
initialMemberships.map((membership) => membership.tenantId).join(","),
|
|
152
|
-
setCurrentTenant,
|
|
153
|
-
setAvailableTenants,
|
|
154
|
-
setMemberships
|
|
155
|
-
]);
|
|
156
|
-
useEffect(() => {
|
|
157
|
-
const getCookieTenant = () => {
|
|
158
|
-
const cookies = document.cookie.split(";");
|
|
159
|
-
const tenantCookie = cookies.find((cookie) => cookie.trim().startsWith("__active-org="));
|
|
160
|
-
if (tenantCookie) {
|
|
161
|
-
const value$1 = tenantCookie.split("=")[1];
|
|
162
|
-
return decodeURIComponent(value$1);
|
|
163
|
-
}
|
|
164
|
-
return null;
|
|
165
|
-
};
|
|
166
|
-
if (!currentTenant) {
|
|
167
|
-
const cookieTenant = getCookieTenant();
|
|
168
|
-
if (cookieTenant && availableTenants.includes(cookieTenant)) setCurrentTenant(cookieTenant);
|
|
169
|
-
}
|
|
170
|
-
}, [
|
|
171
|
-
currentTenant,
|
|
172
|
-
availableTenants,
|
|
173
|
-
setCurrentTenant
|
|
174
|
-
]);
|
|
175
|
-
const switchTenant = (tenantId) => {
|
|
176
|
-
switchTenantAction(tenantId).then(() => {
|
|
177
|
-
if (onTenantChange) onTenantChange();
|
|
178
|
-
});
|
|
179
|
-
};
|
|
180
|
-
const refreshTenants = () => {
|
|
181
|
-
console.log("Tenant refresh requested - should be handled by identity context");
|
|
182
|
-
};
|
|
183
|
-
const refreshOrganization = () => {
|
|
184
|
-
if (currentTenant) fetchOrganization(currentTenant);
|
|
185
|
-
};
|
|
186
|
-
useEffect(() => {
|
|
187
|
-
if (currentTenant && !currentOrganization) refreshOrganization();
|
|
188
|
-
}, [currentTenant, currentOrganization]);
|
|
189
|
-
const value = {
|
|
190
|
-
currentTenant,
|
|
191
|
-
availableTenants,
|
|
192
|
-
memberships,
|
|
193
|
-
currentOrganization,
|
|
194
|
-
currentMembership,
|
|
195
|
-
organizations,
|
|
196
|
-
isLoading,
|
|
197
|
-
switchTenant,
|
|
198
|
-
refreshTenants,
|
|
199
|
-
refreshOrganization,
|
|
200
|
-
getOrganization,
|
|
201
|
-
getMembership,
|
|
202
|
-
getTenantName
|
|
203
|
-
};
|
|
204
|
-
return /* @__PURE__ */ jsx(TenantContext.Provider, {
|
|
205
|
-
value,
|
|
206
|
-
children
|
|
207
|
-
});
|
|
208
|
-
}
|
|
209
|
-
/**
|
|
210
|
-
* Hook for using tenant context
|
|
211
|
-
*/
|
|
212
|
-
function useTenant() {
|
|
213
|
-
const context = useContext(TenantContext);
|
|
214
|
-
if (!context) throw new Error("useTenant must be used within a TenantProvider");
|
|
215
|
-
return context;
|
|
216
|
-
}
|
|
217
|
-
/**
|
|
218
|
-
* Hook for current tenant only
|
|
219
|
-
*/
|
|
220
|
-
function useCurrentTenant() {
|
|
221
|
-
const { currentTenant } = useTenant();
|
|
222
|
-
return currentTenant;
|
|
223
|
-
}
|
|
224
|
-
/**
|
|
225
|
-
* Hook for tenant switching
|
|
226
|
-
*/
|
|
227
|
-
function useTenantSwitcher() {
|
|
228
|
-
const { switchTenant, isLoading, availableTenants } = useTenant();
|
|
229
|
-
return {
|
|
230
|
-
switchTenant,
|
|
231
|
-
isLoading,
|
|
232
|
-
availableTenants
|
|
233
|
-
};
|
|
234
|
-
}
|
|
235
|
-
/**
|
|
236
|
-
* Hook for current organization
|
|
237
|
-
*/
|
|
238
|
-
function useCurrentOrganization() {
|
|
239
|
-
const { currentOrganization } = useTenant();
|
|
240
|
-
return currentOrganization;
|
|
241
|
-
}
|
|
242
|
-
/**
|
|
243
|
-
* Hook for organization operations
|
|
244
|
-
*/
|
|
245
|
-
function useOrganization() {
|
|
246
|
-
const { currentOrganization, refreshOrganization, isLoading } = useTenant();
|
|
247
|
-
return {
|
|
248
|
-
organization: currentOrganization,
|
|
249
|
-
refreshOrganization,
|
|
250
|
-
isLoading
|
|
251
|
-
};
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
//#endregion
|
|
255
|
-
export { TenantProvider, configureTenantEndpoints, initializeTenantStore, useCurrentOrganization, useCurrentTenant, useOrganization, useTenant, useTenantStore, useTenantSwitcher };
|
|
256
|
-
//# sourceMappingURL=tenant-BErqEENp.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"tenant-BErqEENp.js","names":["config: {\n organizationEndpoint?: string;\n tenantSwitchEndpoint?: string;\n}","currentTenant: string | null","availableTenants: string[]","memberships?: TenantMembership[]","value","tenantId: string","value: TenantContextType"],"sources":["../src/tenant/tenant-store.ts","../src/tenant/tenant-context.tsx"],"sourcesContent":["/**\n * Tenant Store (Zustand)\n * \n * Client-side state management for tenant and organization data.\n */\n\nimport { create } from 'zustand';\nimport { devtools } from 'zustand/middleware';\nimport type { OrganizationData, TenantMembership } from './types';\n\ninterface TenantState {\n // State\n currentTenant: string | null;\n availableTenants: string[];\n memberships: Map<string, TenantMembership>;\n organizations: Map<string, OrganizationData>;\n isLoading: boolean;\n isSwitching: boolean;\n\n // Actions\n setCurrentTenant: (tenantId: string | null) => void;\n setAvailableTenants: (tenants: string[]) => void;\n setMemberships: (memberships: TenantMembership[]) => void;\n setOrganization: (tenantId: string, data: OrganizationData) => void;\n addOrganization: (tenantId: string, data: OrganizationData) => void;\n setLoading: (loading: boolean) => void;\n setSwitching: (switching: boolean) => void;\n\n // Async actions\n fetchOrganization: (tenantId: string) => Promise<void>;\n switchTenant: (tenantId: string) => Promise<void>;\n\n // Getters\n getMembership: (tenantId: string) => TenantMembership | null;\n getCurrentMembership: () => TenantMembership | null;\n getTenantName: (tenantId: string) => string;\n getOrganization: (tenantId: string) => OrganizationData | null;\n getCurrentOrganization: () => OrganizationData | null;\n}\n\n/**\n * Organization fetch endpoint (configurable)\n */\nlet organizationEndpoint = '/api/organization/get';\nlet tenantSwitchEndpoint = '/api/tenant/switch';\n\n/**\n * Configure tenant store endpoints\n */\nexport function configureTenantEndpoints(config: {\n organizationEndpoint?: string;\n tenantSwitchEndpoint?: string;\n}) {\n if (config.organizationEndpoint) {\n organizationEndpoint = config.organizationEndpoint;\n }\n if (config.tenantSwitchEndpoint) {\n tenantSwitchEndpoint = config.tenantSwitchEndpoint;\n }\n}\n\nexport const useTenantStore = create<TenantState>()(\n devtools(\n (set, get) => ({\n // Initial state\n currentTenant: null,\n availableTenants: [],\n memberships: new Map(),\n organizations: new Map(),\n isLoading: false,\n isSwitching: false,\n\n // Setters\n setCurrentTenant: (tenantId) => set({ currentTenant: tenantId }),\n\n setAvailableTenants: (tenants) => {\n set({ availableTenants: tenants });\n\n // Fetch organization data for all tenants\n const { organizations, fetchOrganization } = get();\n tenants.forEach((tenantId) => {\n if (!organizations.has(tenantId)) {\n fetchOrganization(tenantId);\n }\n });\n },\n\n setMemberships: (memberships) => {\n const membershipMap = new Map<string, TenantMembership>();\n memberships.forEach((membership) => {\n membershipMap.set(membership.tenantId, membership);\n });\n set({ memberships: membershipMap });\n },\n\n setOrganization: (tenantId, data) =>\n set((state) => ({\n organizations: new Map(state.organizations).set(tenantId, data),\n })),\n\n addOrganization: (tenantId, data) => {\n set((state) => {\n const newOrganizations = new Map(state.organizations);\n newOrganizations.set(tenantId, data);\n return { organizations: newOrganizations };\n });\n },\n\n setLoading: (loading) => set({ isLoading: loading }),\n setSwitching: (switching) => set({ isSwitching: switching }),\n\n // Async actions\n fetchOrganization: async (tenantId) => {\n try {\n const response = await fetch(`${organizationEndpoint}/${tenantId}`);\n if (response.ok) {\n const result = await response.json();\n if (result.success && result.data) {\n get().setOrganization(tenantId, result.data);\n }\n }\n } catch (error) {\n console.error('Failed to fetch organization data for tenant:', tenantId, error);\n }\n },\n\n switchTenant: async (tenantId) => {\n const { availableTenants } = get();\n\n if (!availableTenants.includes(tenantId)) {\n console.error('Tenant not available:', tenantId);\n return;\n }\n\n set({ isSwitching: true });\n\n try {\n const response = await fetch(tenantSwitchEndpoint, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ tenantId }),\n });\n\n if (response.ok) {\n const result = await response.json();\n if (result.success) {\n set({ currentTenant: tenantId });\n\n // Fetch organization data if not already loaded\n const { organizations, fetchOrganization } = get();\n if (!organizations.has(tenantId)) {\n await fetchOrganization(tenantId);\n }\n\n // Reload the page to refresh all data with new tenant context\n window.location.reload();\n }\n }\n } catch (error) {\n console.error('Failed to switch tenant:', error);\n set({ isSwitching: false });\n }\n },\n\n // Getters\n getMembership: (tenantId) => {\n const { memberships } = get();\n return memberships.get(tenantId) || null;\n },\n\n getCurrentMembership: () => {\n const { currentTenant, memberships } = get();\n if (!currentTenant) return null;\n return memberships.get(currentTenant) || null;\n },\n\n getTenantName: (tenantId) => {\n const { memberships, organizations } = get();\n const membership = memberships.get(tenantId);\n if (membership?.tenantName) {\n return membership.tenantName;\n }\n if (membership?.organizationName) {\n return membership.organizationName;\n }\n return organizations.get(tenantId)?.name || tenantId;\n },\n\n getOrganization: (tenantId) => {\n const { organizations } = get();\n return organizations.get(tenantId) || null;\n },\n\n getCurrentOrganization: () => {\n const { currentTenant, organizations } = get();\n if (!currentTenant) return null;\n return organizations.get(currentTenant) || null;\n },\n }),\n { name: 'TenantStore' }\n )\n);\n\n/**\n * Initialize store with server data\n */\nexport function initializeTenantStore(\n currentTenant: string | null,\n availableTenants: string[],\n memberships?: TenantMembership[]\n) {\n const store = useTenantStore.getState();\n store.setCurrentTenant(currentTenant);\n store.setAvailableTenants(availableTenants);\n if (memberships) {\n store.setMemberships(memberships);\n }\n}\n","/**\n * Tenant Context Provider\n * \n * React context for tenant management.\n */\n\nimport { createContext, useContext, useEffect, type ReactNode } from 'react';\nimport { useTenantStore } from './tenant-store';\nimport type { OrganizationData, TenantContextType, TenantMembership } from './types';\n\nconst TenantContext = createContext<TenantContextType | null>(null);\n\ninterface TenantProviderProps {\n children: ReactNode;\n initialTenant?: string | null;\n initialTenants?: string[];\n initialMemberships?: TenantMembership[];\n onTenantChange?: () => void;\n}\n\nexport function TenantProvider({\n children,\n initialTenant = null,\n initialTenants = [],\n initialMemberships = [],\n onTenantChange,\n}: TenantProviderProps) {\n const currentTenant = useTenantStore((state) => state.currentTenant);\n const availableTenants = useTenantStore((state) => state.availableTenants);\n const memberships = useTenantStore((state) => state.memberships);\n const organizations = useTenantStore((state) => state.organizations);\n const isLoading = useTenantStore((state) => state.isLoading || state.isSwitching);\n const currentOrganization = useTenantStore((state) => state.getCurrentOrganization());\n const currentMembership = useTenantStore((state) => state.getCurrentMembership());\n\n const setCurrentTenant = useTenantStore((state) => state.setCurrentTenant);\n const setAvailableTenants = useTenantStore((state) => state.setAvailableTenants);\n const setMemberships = useTenantStore((state) => state.setMemberships);\n const switchTenantAction = useTenantStore((state) => state.switchTenant);\n const fetchOrganization = useTenantStore((state) => state.fetchOrganization);\n const getOrganization = useTenantStore((state) => state.getOrganization);\n const getMembership = useTenantStore((state) => state.getMembership);\n const getTenantName = useTenantStore((state) => state.getTenantName);\n\n // Initialize store with initial values\n useEffect(() => {\n if (initialTenant) {\n setCurrentTenant(initialTenant);\n }\n if (initialTenants.length > 0) {\n setAvailableTenants(initialTenants);\n }\n if (initialMemberships.length > 0) {\n setMemberships(initialMemberships);\n }\n }, [\n initialTenant,\n initialTenants.join(','),\n initialMemberships.map((membership) => membership.tenantId).join(','),\n setCurrentTenant,\n setAvailableTenants,\n setMemberships,\n ]);\n\n // Read tenant from cookie (client-side)\n useEffect(() => {\n const getCookieTenant = () => {\n const cookies = document.cookie.split(';');\n const tenantCookie = cookies.find((cookie) =>\n cookie.trim().startsWith('__active-org=')\n );\n\n if (tenantCookie) {\n const value = tenantCookie.split('=')[1];\n return decodeURIComponent(value);\n }\n\n return null;\n };\n\n if (!currentTenant) {\n const cookieTenant = getCookieTenant();\n if (cookieTenant && availableTenants.includes(cookieTenant)) {\n setCurrentTenant(cookieTenant);\n }\n }\n }, [currentTenant, availableTenants, setCurrentTenant]);\n\n // Switch tenant handler\n const switchTenant = (tenantId: string) => {\n switchTenantAction(tenantId).then(() => {\n if (onTenantChange) {\n onTenantChange();\n }\n });\n };\n\n // Refresh tenants (handled by identity context)\n const refreshTenants = () => {\n console.log('Tenant refresh requested - should be handled by identity context');\n };\n\n // Refresh organization\n const refreshOrganization = () => {\n if (currentTenant) {\n fetchOrganization(currentTenant);\n }\n };\n\n // Load organization data when tenant is set\n useEffect(() => {\n if (currentTenant && !currentOrganization) {\n refreshOrganization();\n }\n }, [currentTenant, currentOrganization]);\n\n const value: TenantContextType = {\n currentTenant,\n availableTenants,\n memberships,\n currentOrganization,\n currentMembership,\n organizations,\n isLoading,\n switchTenant,\n refreshTenants,\n refreshOrganization,\n getOrganization,\n getMembership,\n getTenantName,\n };\n\n return (\n <TenantContext.Provider value={value}>\n {children}\n </TenantContext.Provider>\n );\n}\n\n/**\n * Hook for using tenant context\n */\nexport function useTenant(): TenantContextType {\n const context = useContext(TenantContext);\n\n if (!context) {\n throw new Error('useTenant must be used within a TenantProvider');\n }\n\n return context;\n}\n\n/**\n * Hook for current tenant only\n */\nexport function useCurrentTenant(): string | null {\n const { currentTenant } = useTenant();\n return currentTenant;\n}\n\n/**\n * Hook for tenant switching\n */\nexport function useTenantSwitcher() {\n const { switchTenant, isLoading, availableTenants } = useTenant();\n return { switchTenant, isLoading, availableTenants };\n}\n\n/**\n * Hook for current organization\n */\nexport function useCurrentOrganization(): OrganizationData | null {\n const { currentOrganization } = useTenant();\n return currentOrganization;\n}\n\n/**\n * Hook for organization operations\n */\nexport function useOrganization() {\n const { currentOrganization, refreshOrganization, isLoading } = useTenant();\n\n return {\n organization: currentOrganization,\n refreshOrganization,\n isLoading,\n };\n}\n"],"mappings":"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"}
|