@authhero/react-admin 0.23.0 → 0.25.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,23 @@
1
1
  # @authhero/react-admin
2
2
 
3
+ ## 0.25.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 7bf78f7: Add deploy buttons for react-admin
8
+
9
+ ## 0.24.0
10
+
11
+ ### Minor Changes
12
+
13
+ - 9d6cfb8: Wrap adapters as part of the multi-tenant package
14
+
15
+ ### Patch Changes
16
+
17
+ - Updated dependencies [9d6cfb8]
18
+ - @authhero/adapter-interfaces@0.122.0
19
+ - @authhero/widget@0.7.1
20
+
3
21
  ## 0.23.0
4
22
 
5
23
  ### Minor Changes
package/README.md CHANGED
@@ -1,5 +1,10 @@
1
1
  # react-admin
2
2
 
3
+ [![Deploy with Vercel](https://vercel.com/button)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fauthhero%2Fauthhero&env=VITE_AUTH0_DOMAIN,VITE_AUTH0_CLIENT_ID,VITE_AUTH0_API_URL,VITE_SINGLE_DOMAIN_MODE&envDescription=Configure%20your%20AuthHero%20connection.%20Set%20VITE_SINGLE_DOMAIN_MODE%20to%20%22true%22%20to%20skip%20domain%20selector.&envLink=https%3A%2F%2Fgithub.com%2Fauthhero%2Fauthhero%2Fblob%2Fmain%2Fapps%2Freact-admin%2FREADME.md&project-name=authhero-admin&repository-name=authhero-admin&root-directory=apps%2Freact-admin&build-command=pnpm%20run%20build&output-directory=dist&install-command=corepack%20enable%20%26%26%20corepack%20prepare%20pnpm%4010.11.0%20--activate%20%26%26%20pnpm%20install%20--no-frozen-lockfile)
4
+ [![Deploy to Cloudflare Pages](https://deploy.workers.cloudflare.com/button)](https://deploy.workers.cloudflare.com/?url=https://github.com/authhero/authhero)
5
+
6
+ > **Cloudflare Pages Setup:** Select "Pages" during setup, set root directory to `apps/react-admin`, build command to `pnpm run build`, and output directory to `dist`. Add environment variables `VITE_AUTH0_DOMAIN`, `VITE_AUTH0_CLIENT_ID`, `VITE_AUTH0_API_URL`, and `VITE_SINGLE_DOMAIN_MODE`.
7
+
3
8
  ## Installation
4
9
 
5
10
  Install the application dependencies by running:
@@ -26,6 +31,9 @@ VITE_AUTH0_API_URL=https://auth2.sesamy.com
26
31
  VITE_AUTH0_DOMAIN=localhost:3000
27
32
  VITE_AUTH0_CLIENT_ID=auth-admin
28
33
  VITE_AUTH0_API_URL=https://localhost:3000
34
+
35
+ # Optional: Skip domain selector and use configured domain directly
36
+ VITE_SINGLE_DOMAIN_MODE=true
29
37
  ```
30
38
 
31
39
  See `.env.example` for more details.
@@ -33,7 +41,8 @@ See `.env.example` for more details.
33
41
  **Notes:**
34
42
 
35
43
  - If `VITE_AUTH0_DOMAIN` is set, it will be automatically added to the domain list
36
- - Users can still add additional domains through the UI
44
+ - Users can still add additional domains through the UI (unless `VITE_SINGLE_DOMAIN_MODE=true`)
45
+ - Set `VITE_SINGLE_DOMAIN_MODE=true` to skip the domain selector entirely
37
46
 
38
47
  ## Development
39
48
 
@@ -55,6 +64,12 @@ npm run build
55
64
  yarn build
56
65
  ```
57
66
 
67
+ ## Deployment
68
+
69
+ The React Admin application can be deployed to Vercel. See the [Vercel deployment guide](../docs/deployment-targets/vercel.md) for detailed instructions.
70
+
71
+ **Important:** When deploying to Vercel, you must set the environment variable `ENABLE_EXPERIMENTAL_COREPACK=1` to avoid build errors with pnpm.
72
+
58
73
  ## DataProvider
59
74
 
60
75
  The included data provider use [FakeREST](https://github.com/marmelab/fakerest) to simulate a backend.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@authhero/react-admin",
3
- "version": "0.23.0",
3
+ "version": "0.25.0",
4
4
  "packageManager": "pnpm@10.20.0",
5
5
  "private": false,
6
6
  "repository": {
@@ -88,6 +88,65 @@ function createHeaders(tenantId?: string): Headers {
88
88
  return headers;
89
89
  }
90
90
 
91
+ // Helper for client-side paging, sorting, and search on pre-fetched data
92
+ interface ClientSideListParams {
93
+ data: any[];
94
+ page: number;
95
+ perPage: number;
96
+ sortField?: string;
97
+ sortOrder?: "ASC" | "DESC";
98
+ searchQuery?: string;
99
+ searchFields?: string[];
100
+ idKey?: string;
101
+ }
102
+
103
+ function clientSideListHandler({
104
+ data,
105
+ page,
106
+ perPage,
107
+ sortField,
108
+ sortOrder,
109
+ searchQuery,
110
+ searchFields = ["name"],
111
+ idKey = "id",
112
+ }: ClientSideListParams): { data: any[]; total: number } {
113
+ let filtered = data;
114
+
115
+ // Apply client-side search filter
116
+ if (searchQuery) {
117
+ const query = searchQuery.toLowerCase();
118
+ filtered = data.filter((item: any) =>
119
+ searchFields.some((field) =>
120
+ item[field]?.toString().toLowerCase().includes(query),
121
+ ),
122
+ );
123
+ }
124
+
125
+ // Apply client-side sorting
126
+ if (sortField) {
127
+ filtered = [...filtered].sort((a: any, b: any) => {
128
+ const aVal = a[sortField] || "";
129
+ const bVal = b[sortField] || "";
130
+ const comparison = String(aVal).localeCompare(String(bVal));
131
+ return sortOrder === "DESC" ? -comparison : comparison;
132
+ });
133
+ }
134
+
135
+ // Apply client-side pagination
136
+ const total = filtered.length;
137
+ const startIndex = (page - 1) * perPage;
138
+ const endIndex = startIndex + perPage;
139
+ const paged = filtered.slice(startIndex, endIndex);
140
+
141
+ return {
142
+ data: paged.map((item: any) => ({
143
+ id: item[idKey] || item.id,
144
+ ...item,
145
+ })),
146
+ total,
147
+ };
148
+ }
149
+
91
150
  // Helper to handle singleton resource fetching
92
151
  async function fetchSingleton(
93
152
  resource: string,
@@ -201,15 +260,7 @@ export default (
201
260
  resourceKey: "resource_servers",
202
261
  idKey: "id",
203
262
  },
204
- organizations: {
205
- fetch: (client) =>
206
- client.organizations.list({
207
- from: String((page - 1) * (perPage || 10)),
208
- take: perPage || 10,
209
- }),
210
- resourceKey: "organizations",
211
- idKey: "id",
212
- },
263
+ // Organizations handled separately with client-side paging/search
213
264
  // Logs removed from SDK handlers - using HTTP directly for full control
214
265
  rules: {
215
266
  fetch: (client) => client.rules.list(),
@@ -273,6 +324,26 @@ export default (
273
324
  );
274
325
  }
275
326
 
327
+ // Handle organizations with client-side paging and search (fetch 500, filter locally)
328
+ if (resource === "organizations" && !resourcePath.includes("/")) {
329
+ const result = await managementClient.organizations.list({
330
+ from: "0",
331
+ take: 500,
332
+ });
333
+ const { data: allOrgs } = normalizeSDKResponse(result, "organizations");
334
+
335
+ return clientSideListHandler({
336
+ data: allOrgs,
337
+ page,
338
+ perPage: perPage || 10,
339
+ sortField: field,
340
+ sortOrder: order,
341
+ searchQuery: params.filter?.q,
342
+ searchFields: ["name", "display_name"],
343
+ idKey: "id",
344
+ });
345
+ }
346
+
276
347
  // Handle logs with direct HTTP for full control over query params
277
348
  if (resource === "logs") {
278
349
  const headers = createHeaders(tenantId);
package/src/index.tsx CHANGED
@@ -11,9 +11,20 @@ import { getSelectedDomainFromStorage } from "./utils/domainUtils";
11
11
  const isLocalDevelopment = window.location.hostname.startsWith("local.");
12
12
  const LOCAL_DOMAIN = "localhost:3000";
13
13
 
14
+ // Check if single domain mode is enabled - skips the domain selector entirely
15
+ const isSingleDomainMode = import.meta.env.VITE_SINGLE_DOMAIN_MODE === "true";
16
+ const envDomain = import.meta.env.VITE_AUTH0_DOMAIN;
17
+
14
18
  function Root() {
19
+ // In single domain mode, always use the configured domain from env
20
+ const getInitialDomain = () => {
21
+ if (isLocalDevelopment) return LOCAL_DOMAIN;
22
+ if (isSingleDomainMode && envDomain) return envDomain;
23
+ return null;
24
+ };
25
+
15
26
  const [selectedDomain, setSelectedDomain] = useState<string | null>(
16
- isLocalDevelopment ? LOCAL_DOMAIN : null,
27
+ getInitialDomain(),
17
28
  );
18
29
  const currentPath = location.pathname;
19
30
  const isAuthCallback = currentPath === "/auth-callback";
@@ -24,10 +35,10 @@ function Root() {
24
35
  currentPath.startsWith("/tenants/create") ||
25
36
  currentPath === "/tenants/";
26
37
 
27
- // Load domain from cookies on component mount (skip for local development)
38
+ // Load domain from cookies on component mount (skip for local development and single domain mode)
28
39
  useEffect(() => {
29
- if (isLocalDevelopment) {
30
- // For local development, always use localhost:3000
40
+ if (isLocalDevelopment || isSingleDomainMode) {
41
+ // For local development or single domain mode, always use the configured domain
31
42
  return;
32
43
  }
33
44
  const savedDomain = getSelectedDomainFromStorage();
@@ -48,8 +59,8 @@ function Root() {
48
59
  }
49
60
 
50
61
  // Show domain selector on root path or if no domain is selected
51
- // Skip for local development - redirect to /tenants instead
52
- if (!isLocalDevelopment && (isRootPath || !selectedDomain)) {
62
+ // Skip for local development and single domain mode - redirect to /tenants instead
63
+ if (!isLocalDevelopment && !isSingleDomainMode && (isRootPath || !selectedDomain)) {
53
64
  return (
54
65
  <DomainSelector
55
66
  onDomainSelected={(domain) => setSelectedDomain(domain)}
@@ -58,6 +69,12 @@ function Root() {
58
69
  );
59
70
  }
60
71
 
72
+ // For single domain mode on root path, redirect to /tenants
73
+ if (isSingleDomainMode && isRootPath) {
74
+ window.location.href = "/tenants";
75
+ return null;
76
+ }
77
+
61
78
  // For local development on root path, redirect to /tenants
62
79
  if (isLocalDevelopment && isRootPath) {
63
80
  window.location.href = "/tenants";
package/vercel.json CHANGED
@@ -13,5 +13,5 @@
13
13
  "destination": "/index.html"
14
14
  }
15
15
  ],
16
- "installCommand": "pnpm install --no-frozen-lockfile"
16
+ "installCommand": "corepack enable && corepack prepare pnpm@10.11.0 --activate && pnpm install --no-frozen-lockfile"
17
17
  }