@rebasepro/plugin-insights 0.2.4 → 0.2.5
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/dist/common/src/collections/default-collections.d.ts +5 -8
- package/dist/core/src/components/LoginView/LoginView.d.ts +9 -1
- package/dist/core/src/hooks/data/useCollectionFetch.d.ts +12 -1
- package/dist/index.es.js +215 -164
- package/dist/index.es.js.map +1 -1
- package/dist/index.umd.js +215 -164
- package/dist/index.umd.js.map +1 -1
- package/dist/plugin-insights/src/components/InsightWidget.d.ts +2 -2
- package/dist/plugin-insights/src/components/InsightWidgetSkeleton.d.ts +3 -1
- package/dist/plugin-insights/src/components/InsightsScorecardView.d.ts +3 -1
- package/dist/types/src/controllers/auth.d.ts +2 -2
- package/dist/types/src/controllers/client.d.ts +25 -40
- package/dist/types/src/controllers/data.d.ts +4 -0
- package/dist/types/src/controllers/data_driver.d.ts +5 -0
- package/dist/types/src/types/auth_adapter.d.ts +3 -56
- package/dist/types/src/types/backend.d.ts +2 -2
- package/dist/types/src/types/backend_hooks.d.ts +2 -17
- package/dist/types/src/types/properties.d.ts +7 -5
- package/dist/types/src/types/user_management_delegate.d.ts +16 -53
- package/dist/types/src/users/index.d.ts +0 -1
- package/dist/types/src/users/user.d.ts +0 -1
- package/package.json +4 -4
- package/src/components/InsightWidget.tsx +33 -4
- package/src/components/InsightWidgetSkeleton.tsx +7 -1
- package/src/components/InsightsScorecardView.tsx +4 -1
- package/dist/types/src/users/roles.d.ts +0 -14
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { AdminUser
|
|
1
|
+
import type { AdminUser } from "../controllers/client";
|
|
2
2
|
/**
|
|
3
3
|
* Context passed to every backend hook.
|
|
4
4
|
* Provides information about the request that triggered the hook.
|
|
@@ -64,19 +64,6 @@ export interface UserHooks {
|
|
|
64
64
|
*/
|
|
65
65
|
afterDelete?(userId: string, context: BackendHookContext): void | Promise<void>;
|
|
66
66
|
}
|
|
67
|
-
/**
|
|
68
|
-
* Hooks for intercepting Admin Role data at the API boundary.
|
|
69
|
-
* @group Backend Hooks
|
|
70
|
-
*/
|
|
71
|
-
export interface RoleHooks {
|
|
72
|
-
/**
|
|
73
|
-
* Transform a role record after it's read from the database,
|
|
74
|
-
* before it's returned to the client.
|
|
75
|
-
*
|
|
76
|
-
* Return the modified role, or `null` to filter it out entirely.
|
|
77
|
-
*/
|
|
78
|
-
afterRead?(role: AdminRole, context: BackendHookContext): AdminRole | null | Promise<AdminRole | null>;
|
|
79
|
-
}
|
|
80
67
|
/**
|
|
81
68
|
* Hooks for intercepting collection entity data at the REST API boundary.
|
|
82
69
|
*
|
|
@@ -146,7 +133,7 @@ export interface DataHooks {
|
|
|
146
133
|
* These hooks run server-side after database operations complete and before
|
|
147
134
|
* API responses are sent.
|
|
148
135
|
*
|
|
149
|
-
* - `users`
|
|
136
|
+
* - `users` — intercept admin user management endpoints
|
|
150
137
|
* - `data` — intercept ALL collection entity data flowing through the REST API
|
|
151
138
|
*
|
|
152
139
|
* `data` hooks complement per-collection `EntityCallbacks`. Entity callbacks
|
|
@@ -180,8 +167,6 @@ export interface DataHooks {
|
|
|
180
167
|
export interface BackendHooks {
|
|
181
168
|
/** Hooks for intercepting user management data */
|
|
182
169
|
users?: UserHooks;
|
|
183
|
-
/** Hooks for intercepting role management data */
|
|
184
|
-
roles?: RoleHooks;
|
|
185
170
|
/** Hooks for intercepting ALL collection entity data via the REST API */
|
|
186
171
|
data?: DataHooks;
|
|
187
172
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ComponentRef } from "./component_ref";
|
|
2
|
-
import type { EntityReference, EntityRelation, EntityValues, GeoPoint,
|
|
3
|
-
import type {
|
|
2
|
+
import type { Entity, EntityReference, EntityRelation, EntityValues, GeoPoint, Vector } from "./entities";
|
|
3
|
+
import type { JoinStep, OnAction, Relation } from "./relations";
|
|
4
4
|
import type { EntityCollection, FilterValues } from "./collections";
|
|
5
5
|
import type { ColorKey, ColorScheme } from "./chips";
|
|
6
6
|
import type { AuthController } from "../controllers/auth";
|
|
@@ -185,7 +185,7 @@ export interface StringProperty extends BaseProperty {
|
|
|
185
185
|
* Optional database column type. If not set, it defaults to `varchar` or `uuid` depending on `isId` configuration.
|
|
186
186
|
* Use `text` for strings with unbound length, `char` for fixed-length strings, or `varchar` for variable-length strings with a limit.
|
|
187
187
|
*/
|
|
188
|
-
columnType?: "varchar" | "text" | "char";
|
|
188
|
+
columnType?: "varchar" | "text" | "char" | "uuid";
|
|
189
189
|
/**
|
|
190
190
|
* Rules for validating this property
|
|
191
191
|
*/
|
|
@@ -541,9 +541,11 @@ export interface ArrayProperty extends BaseProperty {
|
|
|
541
541
|
ui?: ArrayUIConfig;
|
|
542
542
|
type: "array";
|
|
543
543
|
/**
|
|
544
|
-
* Optional database column type.
|
|
544
|
+
* Optional database column type. By default, maps to a native Postgres array
|
|
545
|
+
* (e.g. `text[]`, `integer[]`/`numeric[]`, `boolean[]`) if the element type
|
|
546
|
+
* is a primitive, otherwise defaults to `jsonb`.
|
|
545
547
|
*/
|
|
546
|
-
columnType?: "json" | "jsonb";
|
|
548
|
+
columnType?: "json" | "jsonb" | "text[]" | "integer[]" | "boolean[]" | "numeric[]";
|
|
547
549
|
/**
|
|
548
550
|
* The property of this array.
|
|
549
551
|
* You can specify any property (except another Array property)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { User } from "../users";
|
|
2
2
|
/**
|
|
3
3
|
* Result of creating a new user via admin flow.
|
|
4
4
|
* Contains the created user plus information about how credentials were delivered.
|
|
@@ -15,56 +15,46 @@ export interface UserCreationResult<USER extends User = User> {
|
|
|
15
15
|
temporaryPassword?: string;
|
|
16
16
|
}
|
|
17
17
|
/**
|
|
18
|
-
* Delegate to manage
|
|
19
|
-
*
|
|
20
|
-
*
|
|
18
|
+
* Delegate to manage auth-specific user operations.
|
|
19
|
+
*
|
|
20
|
+
* This interface allows the CMS to be agnostic of the underlying
|
|
21
|
+
* authentication provider or backend. User/role CRUD is now handled
|
|
22
|
+
* by the collection system; this delegate only exposes auth-specific
|
|
23
|
+
* operations (password hashing, invitations, bootstrap).
|
|
21
24
|
*
|
|
22
25
|
* @group Models
|
|
23
26
|
*/
|
|
24
27
|
export interface UserManagementDelegate<USER extends User = User> {
|
|
25
28
|
/**
|
|
26
|
-
* Are
|
|
29
|
+
* Are auth-related operations currently loading?
|
|
27
30
|
*/
|
|
28
31
|
loading: boolean;
|
|
29
32
|
/**
|
|
30
|
-
*
|
|
33
|
+
* In-memory list of users (used for client-side filtering fallback).
|
|
31
34
|
*/
|
|
32
|
-
users
|
|
35
|
+
users?: USER[];
|
|
33
36
|
/**
|
|
34
|
-
*
|
|
37
|
+
* Error from fetching the users list, if any.
|
|
35
38
|
*/
|
|
36
39
|
usersError?: Error;
|
|
37
40
|
/**
|
|
38
|
-
*
|
|
39
|
-
* user information when assigning ownership of an entity.
|
|
40
|
-
* @param uid
|
|
41
|
+
* Look up a single user by UID from the in-memory cache.
|
|
41
42
|
*/
|
|
42
|
-
getUser
|
|
43
|
+
getUser?: (uid: string) => USER | null;
|
|
43
44
|
/**
|
|
44
|
-
*
|
|
45
|
-
* When provided, the CMS will use this for the users table
|
|
46
|
-
* instead of loading all users into memory.
|
|
45
|
+
* Server-side user search with pagination.
|
|
47
46
|
*/
|
|
48
|
-
searchUsers?: (
|
|
47
|
+
searchUsers?: (params: {
|
|
49
48
|
search?: string;
|
|
50
49
|
limit?: number;
|
|
51
50
|
offset?: number;
|
|
52
|
-
orderBy?: string;
|
|
53
|
-
orderDir?: "asc" | "desc";
|
|
54
|
-
roleId?: string;
|
|
55
51
|
}) => Promise<{
|
|
56
52
|
users: USER[];
|
|
57
53
|
total: number;
|
|
58
54
|
}>;
|
|
59
|
-
/**
|
|
60
|
-
* Save a user (create or update)
|
|
61
|
-
* @param user
|
|
62
|
-
*/
|
|
63
|
-
saveUser?: (user: USER) => Promise<USER>;
|
|
64
55
|
/**
|
|
65
56
|
* Create a new user with invitation/password generation support.
|
|
66
57
|
* Returns additional info about how the credentials were delivered.
|
|
67
|
-
* Falls back to saveUser if not provided.
|
|
68
58
|
*/
|
|
69
59
|
createUser?: (user: USER) => Promise<UserCreationResult<USER>>;
|
|
70
60
|
/**
|
|
@@ -73,42 +63,15 @@ export interface UserManagementDelegate<USER extends User = User> {
|
|
|
73
63
|
* or a flag indicating an email invitation was sent.
|
|
74
64
|
*/
|
|
75
65
|
resetPassword?: (user: USER) => Promise<UserCreationResult<USER>>;
|
|
76
|
-
/**
|
|
77
|
-
* Delete a user
|
|
78
|
-
* @param user
|
|
79
|
-
*/
|
|
80
|
-
deleteUser?: (user: USER) => Promise<void>;
|
|
81
|
-
/**
|
|
82
|
-
* List of roles defined in the CMS.
|
|
83
|
-
*/
|
|
84
|
-
roles?: Role[];
|
|
85
|
-
/**
|
|
86
|
-
* Optional error if roles failed to load.
|
|
87
|
-
*/
|
|
88
|
-
rolesError?: Error;
|
|
89
|
-
/**
|
|
90
|
-
* Save a role (create or update)
|
|
91
|
-
* @param role
|
|
92
|
-
*/
|
|
93
|
-
saveRole?: (role: Role) => Promise<void>;
|
|
94
|
-
/**
|
|
95
|
-
* Delete a role
|
|
96
|
-
* @param role
|
|
97
|
-
*/
|
|
98
|
-
deleteRole?: (role: Role) => Promise<void>;
|
|
99
66
|
/**
|
|
100
67
|
* Is the currently logged in user an admin?
|
|
101
68
|
*/
|
|
102
69
|
isAdmin?: boolean;
|
|
103
|
-
/**
|
|
104
|
-
* If true, the UI will allow the user to create the default roles (admin, editor, viewer).
|
|
105
|
-
*/
|
|
106
|
-
allowDefaultRolesCreation?: boolean;
|
|
107
70
|
/**
|
|
108
71
|
* Optionally define roles for a given user. This is useful when the roles
|
|
109
72
|
* are coming from a separate provider than the one issuing the tokens.
|
|
110
73
|
*/
|
|
111
|
-
defineRolesFor?: (user: USER) => Promise<
|
|
74
|
+
defineRolesFor?: (user: USER) => Promise<string[] | undefined> | string[] | undefined;
|
|
112
75
|
/**
|
|
113
76
|
* Whether any admin users exist. Used by the bootstrap banner to decide
|
|
114
77
|
* whether to prompt. Populated via a lightweight check (e.g. `limit=1`
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rebasepro/plugin-insights",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.2.
|
|
4
|
+
"version": "0.2.5",
|
|
5
5
|
"main": "./dist/index.umd.js",
|
|
6
6
|
"module": "./dist/index.es.js",
|
|
7
7
|
"types": "./dist/index.d.ts",
|
|
@@ -16,9 +16,9 @@
|
|
|
16
16
|
"./package.json": "./package.json"
|
|
17
17
|
},
|
|
18
18
|
"dependencies": {
|
|
19
|
-
"@rebasepro/
|
|
20
|
-
"@rebasepro/
|
|
21
|
-
"@rebasepro/
|
|
19
|
+
"@rebasepro/types": "0.2.5",
|
|
20
|
+
"@rebasepro/ui": "0.2.5",
|
|
21
|
+
"@rebasepro/core": "0.2.5"
|
|
22
22
|
},
|
|
23
23
|
"peerDependencies": {
|
|
24
24
|
"react": ">=19.0.0",
|
|
@@ -1,14 +1,36 @@
|
|
|
1
1
|
import React from "react";
|
|
2
|
-
import type { InsightDefinition, DataRow } from "../types";
|
|
2
|
+
import type { InsightDefinition, DataRow, ScorecardConfig } from "../types";
|
|
3
3
|
import { useInsightsData } from "../engine/useInsightsData";
|
|
4
4
|
import { InsightsScorecardView } from "./InsightsScorecardView";
|
|
5
5
|
import { InsightWidgetSkeleton } from "./InsightWidgetSkeleton";
|
|
6
6
|
|
|
7
|
+
/**
|
|
8
|
+
* Compute a deterministic fixed height for a standard scorecard based
|
|
9
|
+
* on which optional elements the config declares. This eliminates
|
|
10
|
+
* layout shift between skeleton and loaded states.
|
|
11
|
+
*
|
|
12
|
+
* Breakdown (non-compact, non-small):
|
|
13
|
+
* py-4 padding: 16 + 16 = 32
|
|
14
|
+
* title row: 16.5 (text-xs leading-snug)
|
|
15
|
+
* mb-2 margin: 8
|
|
16
|
+
* value: 30 (text-2xl leading-tight)
|
|
17
|
+
* ---
|
|
18
|
+
* base: 86.5
|
|
19
|
+
* + dateRange: +16 (14px text + 2px mt-0.5)
|
|
20
|
+
* + comparison: +20 (16px text + 4px mt-1)
|
|
21
|
+
*/
|
|
22
|
+
function computeFixedHeight(config: ScorecardConfig): number {
|
|
23
|
+
let h = 86.5; // base: padding + title + mb-2 + value
|
|
24
|
+
if (config.dateRange) h += 16;
|
|
25
|
+
if (config.comparison) h += 20;
|
|
26
|
+
return Math.ceil(h);
|
|
27
|
+
}
|
|
28
|
+
|
|
7
29
|
/**
|
|
8
30
|
* Single insight widget orchestrator.
|
|
9
31
|
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
32
|
+
* Wraps skeleton and loaded states in a fixed-height container
|
|
33
|
+
* (computed from the scorecard config) to prevent layout shift.
|
|
12
34
|
*
|
|
13
35
|
* All theme-awareness is handled via Tailwind `dark:` classes.
|
|
14
36
|
*/
|
|
@@ -30,14 +52,19 @@ export function InsightWidget({
|
|
|
30
52
|
}) {
|
|
31
53
|
const { data, loading, error } = useInsightsData(definition, { path, collectionSlug, parentCollectionSlugs });
|
|
32
54
|
|
|
55
|
+
// For non-compact, non-embedded standard scorecards, use a fixed height
|
|
56
|
+
// derived from the config to prevent layout shift between skeleton → loaded.
|
|
57
|
+
const fixedHeight = (!compact && !embedded) ? computeFixedHeight(definition.scorecard) : undefined;
|
|
58
|
+
|
|
33
59
|
if (loading) {
|
|
34
|
-
return <InsightWidgetSkeleton config={definition.scorecard} compact={compact} embedded={embedded} />;
|
|
60
|
+
return <InsightWidgetSkeleton config={definition.scorecard} compact={compact} embedded={embedded} fixedHeight={fixedHeight} />;
|
|
35
61
|
}
|
|
36
62
|
|
|
37
63
|
if (error) {
|
|
38
64
|
return (
|
|
39
65
|
<div
|
|
40
66
|
className={`text-red-500/70 dark:text-red-400/70 text-[0.8125rem] ${embedded ? "px-5 py-4 h-full" : `rounded-lg bg-red-500/5 dark:bg-red-400/5 border border-red-500/10 dark:border-red-400/10 ${compact ? "px-3.5 py-3" : "px-5 py-4"}`}`}
|
|
67
|
+
style={fixedHeight ? { height: fixedHeight } : undefined}
|
|
41
68
|
>
|
|
42
69
|
<div className="font-semibold mb-1">{definition.title}</div>
|
|
43
70
|
<div>{error.message}</div>
|
|
@@ -49,6 +76,7 @@ export function InsightWidget({
|
|
|
49
76
|
return (
|
|
50
77
|
<div
|
|
51
78
|
className={`text-surface-400 dark:text-surface-500 text-[0.8125rem] ${embedded ? "px-5 py-4 h-full" : `rounded-lg bg-surface-100 dark:bg-surface-800 border border-surface-200 dark:border-surface-700 ${compact ? "px-3.5 py-3" : "px-5 py-4"}`}`}
|
|
79
|
+
style={fixedHeight ? { height: fixedHeight } : undefined}
|
|
52
80
|
>
|
|
53
81
|
{definition.title} — No data
|
|
54
82
|
</div>
|
|
@@ -62,6 +90,7 @@ export function InsightWidget({
|
|
|
62
90
|
title={definition.title}
|
|
63
91
|
compact={compact}
|
|
64
92
|
embedded={embedded}
|
|
93
|
+
fixedHeight={fixedHeight}
|
|
65
94
|
/>
|
|
66
95
|
);
|
|
67
96
|
}
|
|
@@ -21,12 +21,15 @@ export function InsightWidgetSkeleton({
|
|
|
21
21
|
config,
|
|
22
22
|
compact = false,
|
|
23
23
|
embedded = false,
|
|
24
|
+
fixedHeight,
|
|
24
25
|
}: {
|
|
25
26
|
/** Scorecard config — used to match optional elements (comparison, dateRange, icon). */
|
|
26
27
|
config: ScorecardConfig;
|
|
27
28
|
compact?: boolean;
|
|
28
29
|
/** When true, skip own border since the parent card provides it. */
|
|
29
30
|
embedded?: boolean;
|
|
31
|
+
/** Explicit height to prevent layout shift between skeleton → loaded. */
|
|
32
|
+
fixedHeight?: number;
|
|
30
33
|
}) {
|
|
31
34
|
const hasComparison = Boolean(config.comparison);
|
|
32
35
|
const hasIcon = Boolean(config.icon);
|
|
@@ -74,6 +77,7 @@ export function InsightWidgetSkeleton({
|
|
|
74
77
|
hasIcon={hasIcon}
|
|
75
78
|
hasDateRange={hasDateRange}
|
|
76
79
|
embedded={embedded}
|
|
80
|
+
fixedHeight={fixedHeight}
|
|
77
81
|
/>;
|
|
78
82
|
}
|
|
79
83
|
|
|
@@ -112,11 +116,13 @@ function StandardSkeleton({
|
|
|
112
116
|
hasIcon,
|
|
113
117
|
hasDateRange,
|
|
114
118
|
embedded,
|
|
119
|
+
fixedHeight,
|
|
115
120
|
}: {
|
|
116
121
|
hasComparison: boolean;
|
|
117
122
|
hasIcon: boolean;
|
|
118
123
|
hasDateRange: boolean;
|
|
119
124
|
embedded: boolean;
|
|
125
|
+
fixedHeight?: number;
|
|
120
126
|
}) {
|
|
121
127
|
const containerRef = useRef<HTMLDivElement>(null);
|
|
122
128
|
const [containerWidth, setContainerWidth] = useState<number | null>(null);
|
|
@@ -158,7 +164,7 @@ function StandardSkeleton({
|
|
|
158
164
|
<div
|
|
159
165
|
ref={containerRef}
|
|
160
166
|
className={cls("animate-pulse", baseClass)}
|
|
161
|
-
style={embedded ? undefined : { minHeight: isSmall ? 68 : 92 }}
|
|
167
|
+
style={embedded ? undefined : fixedHeight ? { height: fixedHeight } : { minHeight: isSmall ? 68 : 92 }}
|
|
162
168
|
>
|
|
163
169
|
{/* Title row — identical flex structure to InsightsScorecardView */}
|
|
164
170
|
<div className={`flex items-center justify-between ${isSmall ? "mb-1" : "mb-2"}`}>
|
|
@@ -39,6 +39,7 @@ export function InsightsScorecardView({
|
|
|
39
39
|
title,
|
|
40
40
|
compact = false,
|
|
41
41
|
embedded = false,
|
|
42
|
+
fixedHeight,
|
|
42
43
|
}: {
|
|
43
44
|
config: ScorecardConfig;
|
|
44
45
|
data: DataRow;
|
|
@@ -46,6 +47,8 @@ export function InsightsScorecardView({
|
|
|
46
47
|
compact?: boolean;
|
|
47
48
|
/** When true, skip own border/bg since the parent card provides them. */
|
|
48
49
|
embedded?: boolean;
|
|
50
|
+
/** Explicit height to prevent layout shift between skeleton → loaded. */
|
|
51
|
+
fixedHeight?: number;
|
|
49
52
|
}) {
|
|
50
53
|
const containerRef = useRef<HTMLDivElement>(null);
|
|
51
54
|
const [containerWidth, setContainerWidth] = useState<number | null>(null);
|
|
@@ -124,7 +127,7 @@ export function InsightsScorecardView({
|
|
|
124
127
|
: cls("rounded-lg flex flex-col min-w-0 bg-transparent border", defaultBorderMixin, isSmall ? "px-3.5 py-3" : "px-5 py-4");
|
|
125
128
|
|
|
126
129
|
return (
|
|
127
|
-
<div ref={containerRef} className={baseClass} style={embedded ? undefined : { minHeight: isSmall ? 68 : 92 }}>
|
|
130
|
+
<div ref={containerRef} className={baseClass} style={embedded ? undefined : fixedHeight ? { height: fixedHeight } : { minHeight: isSmall ? 68 : 92 }}>
|
|
128
131
|
{/* Title row */}
|
|
129
132
|
<div className={`flex items-center justify-between ${isSmall ? "mb-1" : "mb-2"}`}>
|
|
130
133
|
<div className="flex flex-col min-w-0">
|