@nocobase/app-template-default 1.0.0-beta.83 → 1.0.0-beta.85
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/.agents/rules/build-and-deploy.md +1 -1
- package/.agents/rules/shell.md +4 -0
- package/AGENTS.md +8 -2
- package/README.MD +10 -2
- package/client/components/back-button.tsx +3 -5
- package/client/components/data-table/data-table.tsx +86 -32
- package/client/components/default-access-editor.tsx +13 -2
- package/client/components/feedback-form.tsx +474 -0
- package/client/components/lifecycle-diagram.tsx +10 -10
- package/client/components/list-feedback.tsx +174 -0
- package/client/components/permission-editor.tsx +419 -226
- package/client/extensions/nocobase-auth-forms/password-login-form.tsx +17 -6
- package/client/extensions/nocobase-auth-forms/password-registration-form.tsx +24 -7
- package/client/extensions/nocobase-auth-forms/password-reset-form.tsx +19 -7
- package/client/extensions/nocobase-auth-forms/password-reset-request-form.tsx +15 -7
- package/client/extensions/nocobase-device-approval/device-approval.tsx +7 -2
- package/client/extensions/nocobase-device-approval/locales/en-US.ts +16 -0
- package/client/extensions/nocobase-device-approval/locales/zh-CN.ts +7 -0
- package/client/extensions/nocobase-file-component-ui/components/file-preview-dialog.tsx +20 -17
- package/client/extensions/nocobase-workflow-management/README.md +3 -1
- package/client/extensions/nocobase-workflow-management/definition-list.tsx +19 -7
- package/client/extensions/nocobase-workflow-management/execution-list.tsx +61 -48
- package/client/hooks/use-list-data.ts +76 -0
- package/client/hooks/use-list-location.ts +23 -0
- package/client/hooks/use-list-tabs.ts +42 -0
- package/client/hooks/use-table-url-state.ts +143 -30
- package/client/locales/system/en-US.ts +24 -1
- package/client/locales/system/settings-en-US.ts +8 -10
- package/client/locales/system/settings-zh-CN.ts +8 -8
- package/client/locales/system/zh-CN.ts +22 -1
- package/client/pages/settings/default-access-form.tsx +44 -26
- package/client/pages/settings/default-access.tsx +100 -93
- package/client/pages/settings/hooks.tsx +4 -59
- package/client/pages/settings/page-permissions.ts +17 -0
- package/client/pages/settings/permissions-model.ts +54 -59
- package/client/pages/settings/role-form.tsx +98 -56
- package/client/pages/settings/roles.tsx +69 -95
- package/client/pages/settings/shared.tsx +43 -33
- package/client/pages/settings/user-form.tsx +51 -30
- package/client/pages/settings/users.tsx +105 -91
- package/client/routing/session-preparation.tsx +10 -3
- package/client/runtime.ts +0 -4
- package/config.example.yml +24 -0
- package/database/main/migrations/202611020002_create_mutation_receipts.ts +19 -0
- package/package.json +24 -21
- package/server/app.ts +0 -2
- package/server/config/auth.ts +2 -0
- package/server/config/concurrency.ts +3 -0
- package/server/config/index.ts +3 -0
- package/server/config/jobs.ts +1 -13
- package/server/config/notification.ts +1 -1
- package/server/config/queue.ts +1 -7
- package/server/plugins.ts +2 -0
- package/server/providers/index.ts +4 -1
- package/server/providers/submission.ts +61 -0
- package/tests/client/auth-i18n.test.tsx +9 -0
- package/tests/client/back-button.test.tsx +3 -3
- package/tests/client/data-table.test.tsx +9 -4
- package/tests/client/default-access.test.tsx +99 -5
- package/tests/client/form-feedback.test.tsx +589 -0
- package/tests/client/list-feedback.test.tsx +301 -0
- package/tests/client/list-recovery.test.tsx +373 -0
- package/tests/client/list-tabs.test.tsx +121 -0
- package/tests/client/manual-list-retry.test.tsx +190 -0
- package/tests/client/page-permission-navigation.test.ts +8 -0
- package/tests/client/permission-editor.test.tsx +191 -21
- package/tests/client/permission-filter-binding.test.tsx +1 -1
- package/tests/client/permission-workspace.test.ts +13 -0
- package/tests/client/role-return.test.tsx +2 -1
- package/tests/client/session-preparation.test.tsx +24 -1
- package/tests/client/settings-continuity.test.tsx +85 -5
- package/tests/client/settings-feedback.test.tsx +32 -15
- package/tests/client/settings-permissions.test.ts +35 -0
- package/tests/client/settings.test.tsx +2 -1
- package/tests/client/table-url-state.test.tsx +117 -3
- package/tests/client/user-drawer.test.tsx +2 -1
- package/tests/client/user-permissions.test.tsx +30 -5
- package/tests/database/mutation-receipts.test.ts +45 -0
- package/tests/fixtures/form-feedback/README.md +33 -0
- package/tests/fixtures/form-feedback/equipment-detail.tsx +11 -0
- package/tests/fixtures/form-feedback/equipment-layout.tsx +15 -0
- package/tests/fixtures/form-feedback/equipment-migration.ts +27 -0
- package/tests/fixtures/form-feedback/equipment-server.ts +197 -0
- package/tests/fixtures/form-feedback/equipment-validation.tsx +94 -0
- package/tests/fixtures/form-feedback/equipment.tsx +369 -0
- package/tests/fixtures/form-feedback/verify-inline.mjs +174 -0
- package/tests/fixtures/form-feedback/verify.mjs +373 -0
- package/tests/fixtures/permission-alignment.html +16 -0
- package/tests/fixtures/permission-alignment.tsx +124 -0
- package/tests/fixtures/submission/README.md +35 -0
- package/tests/fixtures/submission/browser.test.ts +478 -0
- package/tests/fixtures/submission/migration.ts +61 -0
- package/tests/fixtures/submission/page.tsx +138 -0
- package/tests/fixtures/submission/playwright.config.ts +11 -0
- package/tests/fixtures/submission/routes.ts +208 -0
- package/tests/helpers/settings-client.tsx +31 -0
- package/tests/playwright/permission-alignment.test.ts +109 -0
- package/tests/server/auth-email-config.test.ts +79 -0
- package/tests/server/concurrency.test.ts +61 -0
- package/tests/server/config.test.ts +34 -14
- package/tests/server/member-role.test.ts +7 -1
- package/tests/server/submission-settings.test.ts +241 -0
- package/client/route-overrides.ts +0 -9
- package/client/source-extensions.ts +0 -19
|
@@ -16,7 +16,7 @@ For a Docker source build, pass `--build-arg CDN_BASE_URL=https://cdn.example.co
|
|
|
16
16
|
|
|
17
17
|
## Packing the build for a deployment
|
|
18
18
|
|
|
19
|
-
`pnpm build --tar` writes `storage/exports/dist.tar.gz` after the build. The archive holds `dist/` as a directory next to `config.example.yml`, so extracting it produces exactly those
|
|
19
|
+
`pnpm build --tar` writes `storage/exports/dist.tar.gz` after the build. The archive holds `dist/` as a directory next to `config.example.yml`, with the `Dockerfile` and `Dockerfile.dockerignore`, so extracting it produces exactly those paths rather than scattering `server/` and `node_modules/` into whatever directory you unpacked in, and a server can build the image from it alone with `docker build --build-arg DIST=prebuilt .`.
|
|
20
20
|
|
|
21
21
|
`config.example.yml` travels with it because a deployment has to write a `config.yml` before it can start — `pnpm nocobase config init`, run inside `dist/`, does that from the example, in place, and `pnpm nocobase config check` there verifies it, database connection included, before the first start — and the example is the only statement of what may go in it. Directories of executable shims are left out: a `.bin` entry points at a path on the machine that installed it, and a dangling one makes `pnpm install` in the extracted tree report a corrupt store rather than repair it.
|
|
22
22
|
|
package/.agents/rules/shell.md
CHANGED
|
@@ -33,3 +33,7 @@ Use `Loading` for page or panel waits and `Spinner` (the shadcn-compatible adapt
|
|
|
33
33
|
The shared list of shell behaviors to keep when customizing is in [customizing the shell](../skills/nocobase-app-development/references/frontend/references/shell.md).
|
|
34
34
|
|
|
35
35
|
The signed-in region waits in `client/routing/session-preparation.tsx` before mounting `AppLayout`: the authentication provider first reconnects active realtime subscriptions as the current session and waits for the open notification, including permission invalidation. A ten-second timeout shows a localized retry/sign-out screen. Keep this boundary inside `RequiredAuthentication`; guest login must remain reachable without realtime. Readiness is only for initial session preparation. Subsequent permission notifications and reconnects still use normal fail-closed route unmounting, including portal and effect cleanup.
|
|
36
|
+
|
|
37
|
+
## List context
|
|
38
|
+
|
|
39
|
+
Keep the identity-keyed `SessionPreparation` boundary: mounted private list conditions and ancestor query memory must be discarded when the signed-in user changes. Public list state uses `useTableUrlState`; tabbed datasets use `useListTabs`, with complete owned parameter keys and permission-filtered tabs. Follow the synchronized [list navigation rules](../skills/nocobase-app-development/references/frontend/references/child-routes.md#list-tab-breadcrumbs-and-private-conditions). Default back links use an empty ancestor query when no source visit exists; explicit return destinations remain application-owned.
|
package/AGENTS.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
This is a NocoBase 3 application. Do not apply globally installed NocoBase 2 Skills. You are building the application itself — its pages, its API, its database tables. Everything under this directory is application-owned source code that you may edit directly.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Build application-specific features directly in the application by default. Use a plugin when the user requests one or when code reuse or modular organization calls for a separately registered, maintainable feature. Local plugins belong under `packages/`; the App builds and packages registered local plugins for deployment, so publishing is optional and only needed when distributing the plugin through a package registry. Follow [the project plugin development Skill](.agents/skills/nocobase-app-plugin-development/SKILL.md). Ordinary internal packages can share code without plugin registration; follow [internal packages](.agents/rules/workspace.md) when that is the boundary the application needs.
|
|
6
6
|
|
|
7
7
|
## Default template scope
|
|
8
8
|
|
|
@@ -28,6 +28,8 @@ For creating or maintaining a plugin in this App, read [the project plugin devel
|
|
|
28
28
|
|
|
29
29
|
`.agents/skills/nocobase-app-upgrade/` is a separate Skill for a separate job: merging a newer release of the template this application was generated from. Read it when the task is upgrading the template rather than building a feature — including when the user asks to upgrade NocoBase or its core version, because a newer template release is how this application moves to a newer NocoBase — and read it before touching anything — an upgrade done by copying the newest template over this application destroys the work that made it this application.
|
|
30
30
|
|
|
31
|
+
When upgrading from an older installed Skill, also read the target template package’s `AGENTS.md` and `README.MD` and fetch its declared `@nocobase/app-skills` package before editing. Follow the target’s upgrade requirements in the same upgrade, including application-owned lists and installed Registry copies. Confirm the target baseline only after conflicts are resolved and dependency, source, behavior and retained-data checks pass; see the [upgrade Skill](.agents/skills/nocobase-app-upgrade/SKILL.md).
|
|
32
|
+
|
|
31
33
|
`.agents/skills/nocobase-deployment/` covers moving this application from source to a production server: building for the target platform, what the archive does and does not carry, migrations and business data, production configuration, and what to verify afterwards. Read it before building for deployment, and when an application starts in production but does not work.
|
|
32
34
|
|
|
33
35
|
## Where things go
|
|
@@ -93,7 +95,7 @@ Put a package your **server** code (`server/`, `database/`, `cli/`) imports in `
|
|
|
93
95
|
|
|
94
96
|
### Plugins
|
|
95
97
|
|
|
96
|
-
**Read a plugin's Skill before building what it already does.** Building a permission system, a notification sender, or a job scheduler by hand when a registered plugin already provides one is the most expensive mistake available here. Register and update plugins with `pnpm nocobase plugin register` and `pnpm nocobase plugin update`; customize a plugin's page through its options
|
|
98
|
+
**Read a plugin's Skill before building what it already does.** Building a permission system, a notification sender, or a job scheduler by hand when a registered plugin already provides one is the most expensive mistake available here. Register and update plugins with `pnpm nocobase plugin register` and `pnpm nocobase plugin update`; customize a plugin's page through its options or an explicitly registered route component override, never by redeclaring its route. `.agents/skills/` is generated output; never edit a file there. See [plugins](.agents/skills/nocobase-app-development/references/plugins.md).
|
|
97
99
|
|
|
98
100
|
### Running the application
|
|
99
101
|
|
|
@@ -118,6 +120,10 @@ When the built-in mechanism genuinely cannot express what is being asked, changi
|
|
|
118
120
|
- Comment what you changed and why the built-in path did not fit. On upgrade, an agent reconciling the template's version needs to know whether your change is still needed.
|
|
119
121
|
- Record what the next agent needs to know in the matching `.agents/rules/` file, or a new one linked from the index below, so the guidance describes the application as it actually is without growing this file. If the framework's shared guidance is wrong for every application, update the source `@nocobase/app-skills` package instead of editing its synchronized copy.
|
|
120
122
|
|
|
123
|
+
## Standard submissions
|
|
124
|
+
|
|
125
|
+
Standard Repository writes require an operation key through the registered submission provider. Forms share `useSubmission` across all entries before asynchronous validation; custom effectful APIs explicitly join the receipt transaction. Follow [submission protection](.agents/skills/nocobase-app-development/references/submission.md) and [source upgrades](.agents/skills/nocobase-app-upgrade/references/submission-upgrade.md). Merge copied source and verify retained customizations on upgrade.
|
|
126
|
+
|
|
121
127
|
## Index
|
|
122
128
|
|
|
123
129
|
| Read | When |
|
package/README.MD
CHANGED
|
@@ -4,6 +4,12 @@ A full-stack NocoBase 3 application. The server runs on `@nocobase/app-server` a
|
|
|
4
4
|
|
|
5
5
|
You own everything in this directory. Add pages, API routes, database tables, and business logic directly here — this is your application's source code, not a framework you extend from the outside.
|
|
6
6
|
|
|
7
|
+
## Upgrade this application
|
|
8
|
+
|
|
9
|
+
Ask the application agent to upgrade NocoBase using the application-local [template upgrade Skill](.agents/skills/nocobase-app-upgrade/SKILL.md). This is the supported version-selection and three-way source reconciliation entry point. Before merging, read the selected target package’s `AGENTS.md`, this section and the upgrade Skill from its declared `@nocobase/app-skills` dependency, even if the application still has an older Skill. Fetch and inspect that guidance without first installing it.
|
|
10
|
+
|
|
11
|
+
Complete the target’s requirements in that same upgrade: reconcile dependencies, copied template and Registry sources, application-owned table/card consumers and translations while preserving business customizations, grants, records and executed migrations. For list feedback, follow the target Skill’s `references/list-feedback-upgrade.md`. Do not finish with only a version bump, dependency update or Skill sync. Missing targets, unresolved business conflicts or failed acceptance stop completion; confirm `defaultTemplateVersion` only after the checks pass. See the target Skill’s `references/versioned-upgrade-verification.md` for package integrity records and verification after publication.
|
|
12
|
+
|
|
7
13
|
## Default and Examples
|
|
8
14
|
|
|
9
15
|
Default provides the application shell and product capabilities without example plugins or business demo pages. Application-owned business API routes start empty, and new databases receive no article sample data. A bare create-app shows Home and, for unrestricted administrators only, System settings → App foundation. Foundation is installed and registered on both Client and Server; its read-only overview is available at `/foundation` under the application base path. As business capabilities are developed, the app development skill reconciles missing core administration on every iteration and registers the prepared Users and Roles and permissions contribution (`client/pages/settings/routes.ts`); each actually used configurable capability includes its administration as part of feature delivery. Its built-in Users integration lists direct Authorization Permission Sets as application roles, while access assigned to all authenticated users remains separate. Use `app-template-examples` for article management and runnable plugin demonstrations.
|
|
@@ -138,7 +144,7 @@ The three files named `runtime.ts`, `plugins.ts`, and `routes.ts` are compositio
|
|
|
138
144
|
| `pnpm dev` | Start the API server and client with hot reload |
|
|
139
145
|
| `pnpm build` | Build the client and server into `dist/` |
|
|
140
146
|
| `pnpm start` | Run the built server; requires `pnpm build` first |
|
|
141
|
-
| `pnpm build --tar` | Build, then pack `dist
|
|
147
|
+
| `pnpm build --tar` | Build, then pack `dist/`, `config.example.yml` and the `Dockerfile` with its `.dockerignore` into `storage/exports/dist.tar.gz` |
|
|
142
148
|
|
|
143
149
|
### Database
|
|
144
150
|
|
|
@@ -287,10 +293,12 @@ Pass the file's path relative to `dist/client/`, with or without a leading slash
|
|
|
287
293
|
|
|
288
294
|
`pnpm build --tar` writes `storage/exports/dist.tar.gz`, the artifact `@nocobase/app-installer` installs on a server.
|
|
289
295
|
|
|
290
|
-
The archive has two levels: `config.example.yml` and `
|
|
296
|
+
The archive has two levels: `config.example.yml`, `dist/` and, when the application has them, the `Dockerfile` and `Dockerfile.dockerignore` sit at the root, and the build is inside `dist/`. A server builds the image from the extracted archive alone with `docker build --build-arg DIST=prebuilt .`. What `dist/` holds varies with the application; this is the shape:
|
|
291
297
|
|
|
292
298
|
```text
|
|
293
299
|
dist.tar.gz
|
|
300
|
+
├── Dockerfile
|
|
301
|
+
├── Dockerfile.dockerignore
|
|
294
302
|
├── config.example.yml
|
|
295
303
|
└── dist/
|
|
296
304
|
├── cli/
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useTranslation } from '@nocobase/i18n/client';
|
|
2
2
|
import { ArrowLeftIcon } from 'lucide-react';
|
|
3
3
|
import type { ReactElement, ReactNode } from 'react';
|
|
4
|
-
import { Link, type To,
|
|
4
|
+
import { Link, type To, useResolvedPath } from 'react-router';
|
|
5
5
|
|
|
6
6
|
import { useRouteSearches } from '../routing/route-context.js';
|
|
7
7
|
|
|
@@ -10,7 +10,7 @@ import { cn } from 'cn';
|
|
|
10
10
|
export interface BackButtonProps {
|
|
11
11
|
/**
|
|
12
12
|
* Where it leads. Defaults to the parent route with its last visited query; a directly opened detail falls back
|
|
13
|
-
* to the
|
|
13
|
+
* to the default parent list. An explicit destination is kept unchanged.
|
|
14
14
|
*/
|
|
15
15
|
readonly to?: To;
|
|
16
16
|
/** The label, "Back" by default. */
|
|
@@ -33,7 +33,6 @@ export function BackButton({
|
|
|
33
33
|
to,
|
|
34
34
|
}: BackButtonProps = {}): ReactElement {
|
|
35
35
|
const { t } = useTranslation();
|
|
36
|
-
const location = useLocation();
|
|
37
36
|
const parent = useResolvedPath('..');
|
|
38
37
|
const searches = useRouteSearches();
|
|
39
38
|
|
|
@@ -48,8 +47,7 @@ export function BackButton({
|
|
|
48
47
|
to ?? {
|
|
49
48
|
pathname: '..',
|
|
50
49
|
search:
|
|
51
|
-
searches.get(parent.pathname.replace(/\/+$/u, '') || '/') ??
|
|
52
|
-
location.search,
|
|
50
|
+
searches.get(parent.pathname.replace(/\/+$/u, '') || '/') ?? '',
|
|
53
51
|
}
|
|
54
52
|
}
|
|
55
53
|
>
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { ListState } from '@nocobase/app-client';
|
|
2
|
+
import { ListFeedback, ListFeedbackActions } from '#components/list-feedback';
|
|
2
3
|
import {
|
|
3
4
|
type ColumnDef,
|
|
4
5
|
type PaginationState,
|
|
@@ -41,13 +42,17 @@ export interface DataTableProps<TData, TValue = unknown> {
|
|
|
41
42
|
readonly onSortingChange?: OnChangeFn<SortingState>;
|
|
42
43
|
readonly showSelectedCount?: boolean;
|
|
43
44
|
readonly loading?: boolean;
|
|
45
|
+
readonly listState?: ListState;
|
|
46
|
+
readonly onRetry?: () => void;
|
|
47
|
+
readonly onClearFilters?: () => void;
|
|
48
|
+
readonly emptyAction?: ReactNode;
|
|
44
49
|
readonly className?: string;
|
|
45
50
|
/**
|
|
46
51
|
* Rendered above the table and handed the table instance, so filters and
|
|
47
52
|
* `DataTableViewOptions` can read and drive its state.
|
|
48
53
|
*/
|
|
49
54
|
readonly toolbar?: (table: TableInstance<TData>) => ReactNode;
|
|
50
|
-
/** Shown in
|
|
55
|
+
/** Shown in the table body when no row survives filtering. */
|
|
51
56
|
readonly emptyMessage?: ReactNode;
|
|
52
57
|
/** Set to `false` to render every row and hide the pagination footer. */
|
|
53
58
|
readonly pagination?: boolean;
|
|
@@ -91,8 +96,11 @@ export function DataTable<TData, TValue = unknown>({
|
|
|
91
96
|
onSortingChange,
|
|
92
97
|
showSelectedCount = true,
|
|
93
98
|
loading = false,
|
|
99
|
+
listState,
|
|
100
|
+
onRetry,
|
|
101
|
+
onClearFilters,
|
|
102
|
+
emptyAction,
|
|
94
103
|
}: DataTableProps<TData, TValue>): ReactElement {
|
|
95
|
-
const { t } = useTranslation();
|
|
96
104
|
const tableData = useMemo(() => [...data], [data]);
|
|
97
105
|
const [sorting, setSorting] = useState<SortingState>([]);
|
|
98
106
|
const [columnFilters, setColumnFilters] = useState<ColumnFiltersState>([]);
|
|
@@ -134,12 +142,56 @@ export function DataTable<TData, TValue = unknown>({
|
|
|
134
142
|
});
|
|
135
143
|
|
|
136
144
|
const rows = table.getRowModel().rows;
|
|
145
|
+
const requestedState =
|
|
146
|
+
(listState === 'ready' && rows.length === 0
|
|
147
|
+
? columnFilters.length > 0
|
|
148
|
+
? 'no-results'
|
|
149
|
+
: 'empty'
|
|
150
|
+
: listState) ??
|
|
151
|
+
(loading && data.length === 0
|
|
152
|
+
? 'loading'
|
|
153
|
+
: rows.length
|
|
154
|
+
? 'ready'
|
|
155
|
+
: columnFilters.length
|
|
156
|
+
? 'no-results'
|
|
157
|
+
: 'empty');
|
|
158
|
+
const state =
|
|
159
|
+
(requestedState === 'empty' || requestedState === 'no-results') &&
|
|
160
|
+
((manualPagination && columnFilters.length === 0 && (rowCount ?? 0) > 0) ||
|
|
161
|
+
(!manualPagination && table.getPrePaginationRowModel().rows.length > 0))
|
|
162
|
+
? 'loading'
|
|
163
|
+
: requestedState;
|
|
164
|
+
const clearFilters = () => {
|
|
165
|
+
table.resetColumnFilters();
|
|
166
|
+
table.setPageIndex(0);
|
|
167
|
+
onClearFilters?.();
|
|
168
|
+
};
|
|
169
|
+
const hasStateActions =
|
|
170
|
+
(state === 'no-results' &&
|
|
171
|
+
(columnFilters.length > 0 || onClearFilters !== undefined)) ||
|
|
172
|
+
((state === 'failed' || state === 'refresh-failed') &&
|
|
173
|
+
onRetry !== undefined) ||
|
|
174
|
+
state === 'unauthenticated';
|
|
175
|
+
const showRows =
|
|
176
|
+
rows.length > 0 && (state === 'ready' || state === 'refresh-failed');
|
|
177
|
+
const showFeedback = !showRows || state === 'refresh-failed';
|
|
178
|
+
const feedbackColumnCount = Math.max(1, table.getVisibleLeafColumns().length);
|
|
137
179
|
|
|
138
180
|
return (
|
|
139
181
|
<div className={cn('flex flex-col gap-4', className)}>
|
|
140
|
-
{toolbar ? (
|
|
182
|
+
{toolbar || hasStateActions ? (
|
|
141
183
|
<div className='flex flex-wrap items-center gap-2'>
|
|
142
|
-
{toolbar(table)}
|
|
184
|
+
{toolbar?.(table)}
|
|
185
|
+
<ListFeedbackActions
|
|
186
|
+
state={state}
|
|
187
|
+
onClear={
|
|
188
|
+
columnFilters.length > 0 || onClearFilters
|
|
189
|
+
? clearFilters
|
|
190
|
+
: undefined
|
|
191
|
+
}
|
|
192
|
+
onRetry={onRetry}
|
|
193
|
+
busy={loading}
|
|
194
|
+
/>
|
|
143
195
|
</div>
|
|
144
196
|
) : null}
|
|
145
197
|
<div className='overflow-hidden rounded-lg border'>
|
|
@@ -173,39 +225,41 @@ export function DataTable<TData, TValue = unknown>({
|
|
|
173
225
|
))}
|
|
174
226
|
</TableHeader>
|
|
175
227
|
<TableBody>
|
|
176
|
-
{
|
|
177
|
-
rows.map((row) => (
|
|
178
|
-
<TableRow
|
|
179
|
-
key={row.id}
|
|
180
|
-
data-state={row.getIsSelected() ? 'selected' : undefined}
|
|
181
|
-
className={onRowClick ? 'cursor-pointer' : undefined}
|
|
182
|
-
onClick={onRowClick ? () => onRowClick(row) : undefined}
|
|
183
|
-
>
|
|
184
|
-
{row.getVisibleCells().map((cell) => (
|
|
185
|
-
<TableCell key={cell.id}>
|
|
186
|
-
{flexRender(
|
|
187
|
-
cell.column.columnDef.cell,
|
|
188
|
-
cell.getContext(),
|
|
189
|
-
)}
|
|
190
|
-
</TableCell>
|
|
191
|
-
))}
|
|
192
|
-
</TableRow>
|
|
193
|
-
))
|
|
194
|
-
) : (
|
|
228
|
+
{showFeedback ? (
|
|
195
229
|
<TableRow>
|
|
196
|
-
<TableCell
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
230
|
+
<TableCell colSpan={feedbackColumnCount} className='p-0'>
|
|
231
|
+
<ListFeedback
|
|
232
|
+
state={state}
|
|
233
|
+
empty={emptyMessage}
|
|
234
|
+
emptyAction={emptyAction}
|
|
235
|
+
/>
|
|
202
236
|
</TableCell>
|
|
203
237
|
</TableRow>
|
|
204
|
-
)}
|
|
238
|
+
) : null}
|
|
239
|
+
{showRows
|
|
240
|
+
? rows.map((row) => (
|
|
241
|
+
<TableRow
|
|
242
|
+
key={row.id}
|
|
243
|
+
data-state={row.getIsSelected() ? 'selected' : undefined}
|
|
244
|
+
className={onRowClick ? 'cursor-pointer' : undefined}
|
|
245
|
+
onClick={onRowClick ? () => onRowClick(row) : undefined}
|
|
246
|
+
>
|
|
247
|
+
{row.getVisibleCells().map((cell) => (
|
|
248
|
+
<TableCell key={cell.id}>
|
|
249
|
+
{flexRender(
|
|
250
|
+
cell.column.columnDef.cell,
|
|
251
|
+
cell.getContext(),
|
|
252
|
+
)}
|
|
253
|
+
</TableCell>
|
|
254
|
+
))}
|
|
255
|
+
</TableRow>
|
|
256
|
+
))
|
|
257
|
+
: null}
|
|
205
258
|
</TableBody>
|
|
206
259
|
</Table>
|
|
207
260
|
</div>
|
|
208
|
-
{pagination
|
|
261
|
+
{pagination &&
|
|
262
|
+
(state === 'ready' || (state === 'refresh-failed' && rows.length > 0)) ? (
|
|
209
263
|
<DataTablePagination
|
|
210
264
|
table={table}
|
|
211
265
|
pageSizeOptions={pageSizeOptions}
|
|
@@ -83,6 +83,7 @@ export function DefaultAccessEditor({
|
|
|
83
83
|
<Input
|
|
84
84
|
id={`${id}-key`}
|
|
85
85
|
name='ruleKey'
|
|
86
|
+
data-field-path='key'
|
|
86
87
|
value={ruleKey}
|
|
87
88
|
required
|
|
88
89
|
disabled={existing || readOnly}
|
|
@@ -91,7 +92,11 @@ export function DefaultAccessEditor({
|
|
|
91
92
|
onChange={(event) => onKeyChange(event.target.value)}
|
|
92
93
|
/>
|
|
93
94
|
{keyError && (
|
|
94
|
-
<p
|
|
95
|
+
<p
|
|
96
|
+
data-feedback-field-error
|
|
97
|
+
id={`${id}-key-error`}
|
|
98
|
+
className='text-sm text-destructive'
|
|
99
|
+
>
|
|
95
100
|
{keyError}
|
|
96
101
|
</p>
|
|
97
102
|
)}
|
|
@@ -108,6 +113,7 @@ export function DefaultAccessEditor({
|
|
|
108
113
|
>
|
|
109
114
|
<SelectTrigger
|
|
110
115
|
id={`${id}-resource`}
|
|
116
|
+
data-field-path='resource'
|
|
111
117
|
className='w-full'
|
|
112
118
|
aria-invalid={Boolean(resourceError)}
|
|
113
119
|
aria-describedby={
|
|
@@ -127,7 +133,11 @@ export function DefaultAccessEditor({
|
|
|
127
133
|
</SelectContent>
|
|
128
134
|
</Select>
|
|
129
135
|
{resourceError && (
|
|
130
|
-
<p
|
|
136
|
+
<p
|
|
137
|
+
data-feedback-field-error
|
|
138
|
+
id={`${id}-resource-error`}
|
|
139
|
+
className='text-sm text-destructive'
|
|
140
|
+
>
|
|
131
141
|
{resourceError}
|
|
132
142
|
</p>
|
|
133
143
|
)}
|
|
@@ -187,6 +197,7 @@ export function DefaultAccessEditor({
|
|
|
187
197
|
</Select>
|
|
188
198
|
{scope.error && (
|
|
189
199
|
<p
|
|
200
|
+
data-feedback-field-error
|
|
190
201
|
id={`${id}-${scope.id}-error`}
|
|
191
202
|
className='text-sm text-destructive'
|
|
192
203
|
>
|