@nocobase/portal-template-default 1.0.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/.env.example +10 -0
- package/Dockerfile +36 -0
- package/README.MD +50 -0
- package/components.json +25 -0
- package/eslint.config.js +28 -0
- package/index.html +39 -0
- package/package.json +84 -0
- package/pnpm-workspace.yaml +2 -0
- package/public/favicon.ico +0 -0
- package/public/logo-dark.png +0 -0
- package/public/logo-mark-dark.png +0 -0
- package/public/logo-mark.png +0 -0
- package/public/logo.png +0 -0
- package/src/App.css +158 -0
- package/src/App.tsx +151 -0
- package/src/components/app-shell/brand.tsx +70 -0
- package/src/components/app-shell/breadcrumb.tsx +81 -0
- package/src/components/app-shell/document-title-handler.tsx +169 -0
- package/src/components/app-shell/error-component.tsx +134 -0
- package/src/components/app-shell/header.tsx +127 -0
- package/src/components/app-shell/layout.tsx +39 -0
- package/src/components/app-shell/loading-overlay.tsx +36 -0
- package/src/components/app-shell/sidebar.tsx +351 -0
- package/src/components/app-shell/user-avatar.tsx +42 -0
- package/src/components/app-shell/user-info.tsx +53 -0
- package/src/components/auth/auth-layout.tsx +82 -0
- package/src/components/auth/forgot-password-form.tsx +58 -0
- package/src/components/auth/input-password.tsx +38 -0
- package/src/components/auth/sign-in-form.tsx +111 -0
- package/src/components/auth/sign-up-form.tsx +127 -0
- package/src/components/data-table/data-table-filter.tsx +994 -0
- package/src/components/data-table/data-table-pagination.tsx +146 -0
- package/src/components/data-table/data-table-sorter.tsx +47 -0
- package/src/components/data-table/data-table.tsx +321 -0
- package/src/components/notifications/toaster.tsx +23 -0
- package/src/components/notifications/undoable-notification.tsx +84 -0
- package/src/components/notifications/use-notification-provider.tsx +62 -0
- package/src/components/resources/buttons/clone.tsx +81 -0
- package/src/components/resources/buttons/create.tsx +70 -0
- package/src/components/resources/buttons/delete.tsx +113 -0
- package/src/components/resources/buttons/edit.tsx +81 -0
- package/src/components/resources/buttons/list.tsx +70 -0
- package/src/components/resources/buttons/refresh.tsx +81 -0
- package/src/components/resources/buttons/show.tsx +81 -0
- package/src/components/resources/status-badge.tsx +37 -0
- package/src/components/resources/views/create-view.tsx +81 -0
- package/src/components/resources/views/edit-view.tsx +104 -0
- package/src/components/resources/views/list-view.tsx +86 -0
- package/src/components/resources/views/show-view.tsx +108 -0
- package/src/components/theme/theme-provider.tsx +71 -0
- package/src/components/theme/theme-select.tsx +97 -0
- package/src/components/theme/theme-toggle.tsx +66 -0
- package/src/components/ui/accordion.tsx +72 -0
- package/src/components/ui/alert-dialog.tsx +185 -0
- package/src/components/ui/alert.tsx +76 -0
- package/src/components/ui/aspect-ratio.tsx +22 -0
- package/src/components/ui/avatar.tsx +107 -0
- package/src/components/ui/badge.tsx +52 -0
- package/src/components/ui/breadcrumb.tsx +125 -0
- package/src/components/ui/button.tsx +58 -0
- package/src/components/ui/calendar.tsx +221 -0
- package/src/components/ui/card.tsx +103 -0
- package/src/components/ui/carousel.tsx +240 -0
- package/src/components/ui/chart.tsx +373 -0
- package/src/components/ui/checkbox.tsx +29 -0
- package/src/components/ui/collapsible.tsx +19 -0
- package/src/components/ui/command.tsx +192 -0
- package/src/components/ui/context-menu.tsx +271 -0
- package/src/components/ui/dialog.tsx +160 -0
- package/src/components/ui/drawer.tsx +226 -0
- package/src/components/ui/dropdown-menu.tsx +268 -0
- package/src/components/ui/form.tsx +174 -0
- package/src/components/ui/hover-card.tsx +49 -0
- package/src/components/ui/input-group.tsx +158 -0
- package/src/components/ui/input-otp.tsx +87 -0
- package/src/components/ui/input.tsx +20 -0
- package/src/components/ui/label.tsx +18 -0
- package/src/components/ui/menubar.tsx +278 -0
- package/src/components/ui/navigation-menu.tsx +168 -0
- package/src/components/ui/pagination.tsx +130 -0
- package/src/components/ui/popover.tsx +91 -0
- package/src/components/ui/progress.tsx +81 -0
- package/src/components/ui/radio-group.tsx +38 -0
- package/src/components/ui/resizable.tsx +48 -0
- package/src/components/ui/scroll-area.tsx +55 -0
- package/src/components/ui/select.tsx +199 -0
- package/src/components/ui/separator.tsx +25 -0
- package/src/components/ui/sheet.tsx +136 -0
- package/src/components/ui/sidebar.tsx +723 -0
- package/src/components/ui/skeleton.tsx +13 -0
- package/src/components/ui/slider.tsx +52 -0
- package/src/components/ui/sonner.tsx +47 -0
- package/src/components/ui/switch.tsx +32 -0
- package/src/components/ui/table.tsx +114 -0
- package/src/components/ui/tabs.tsx +82 -0
- package/src/components/ui/textarea.tsx +18 -0
- package/src/components/ui/toggle-group.tsx +87 -0
- package/src/components/ui/toggle.tsx +43 -0
- package/src/components/ui/tooltip.tsx +66 -0
- package/src/hooks/use-mobile.ts +19 -0
- package/src/index.tsx +17 -0
- package/src/lib/utils.ts +19 -0
- package/src/pages/blog-posts/create.tsx +177 -0
- package/src/pages/blog-posts/edit.tsx +197 -0
- package/src/pages/blog-posts/index.ts +4 -0
- package/src/pages/blog-posts/list.tsx +131 -0
- package/src/pages/blog-posts/show.tsx +81 -0
- package/src/pages/categories/create.tsx +73 -0
- package/src/pages/categories/edit.tsx +73 -0
- package/src/pages/categories/index.ts +4 -0
- package/src/pages/categories/list.tsx +86 -0
- package/src/pages/categories/show.tsx +37 -0
- package/src/pages/forgot-password/index.tsx +6 -0
- package/src/pages/forgotPassword/index.tsx +5 -0
- package/src/pages/login/index.tsx +5 -0
- package/src/pages/register/index.tsx +5 -0
- package/src/providers/auth.ts +204 -0
- package/src/providers/constants.ts +75 -0
- package/src/providers/data.ts +232 -0
- package/src/vite-env.d.ts +1 -0
- package/tsconfig.json +25 -0
- package/tsconfig.node.json +21 -0
- package/vite.config.ts +74 -0
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import React from "react";
|
|
4
|
+
import { type BaseKey, useCloneButton } from "@refinedev/core";
|
|
5
|
+
import { Copy } from "lucide-react";
|
|
6
|
+
import { Button } from "@/components/ui/button";
|
|
7
|
+
|
|
8
|
+
type CloneButtonProps = {
|
|
9
|
+
/**
|
|
10
|
+
* Resource name for API data interactions. `identifier` of the resource can be used instead of the `name` of the resource.
|
|
11
|
+
* @default Inferred resource name from the route
|
|
12
|
+
*/
|
|
13
|
+
resource?: string;
|
|
14
|
+
/**
|
|
15
|
+
* Data item identifier for the actions with the API
|
|
16
|
+
* @default Reads `:id` from the URL
|
|
17
|
+
*/
|
|
18
|
+
recordItemId?: BaseKey;
|
|
19
|
+
/**
|
|
20
|
+
* Access Control configuration for the button
|
|
21
|
+
* @default `{ enabled: true, hideIfUnauthorized: false }`
|
|
22
|
+
*/
|
|
23
|
+
accessControl?: {
|
|
24
|
+
enabled?: boolean;
|
|
25
|
+
hideIfUnauthorized?: boolean;
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* `meta` property is used when creating the URL for the related action and path.
|
|
29
|
+
*/
|
|
30
|
+
meta?: Record<string, unknown>;
|
|
31
|
+
} & React.ComponentProps<typeof Button>;
|
|
32
|
+
|
|
33
|
+
export const CloneButton = React.forwardRef<
|
|
34
|
+
React.ComponentRef<typeof Button>,
|
|
35
|
+
CloneButtonProps
|
|
36
|
+
>(
|
|
37
|
+
(
|
|
38
|
+
{ resource, recordItemId, accessControl, meta, children, onClick, ...rest },
|
|
39
|
+
ref
|
|
40
|
+
) => {
|
|
41
|
+
const { hidden, disabled, LinkComponent, to, label } = useCloneButton({
|
|
42
|
+
accessControl,
|
|
43
|
+
resource,
|
|
44
|
+
id: recordItemId,
|
|
45
|
+
meta,
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
const isDisabled = disabled || rest.disabled;
|
|
49
|
+
const isHidden = hidden || rest.hidden;
|
|
50
|
+
|
|
51
|
+
if (isHidden) return null;
|
|
52
|
+
|
|
53
|
+
return (
|
|
54
|
+
<Button
|
|
55
|
+
{...rest}
|
|
56
|
+
ref={ref}
|
|
57
|
+
disabled={isDisabled}
|
|
58
|
+
render={<LinkComponent to={to} replace={false} />}
|
|
59
|
+
onClick={(event) => {
|
|
60
|
+
if (isDisabled) {
|
|
61
|
+
event.preventDefault();
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
if (onClick) {
|
|
65
|
+
event.preventDefault();
|
|
66
|
+
onClick(event);
|
|
67
|
+
}
|
|
68
|
+
}}
|
|
69
|
+
>
|
|
70
|
+
{children ?? (
|
|
71
|
+
<div className="flex items-center gap-2 font-semibold">
|
|
72
|
+
<Copy className="h-4 w-4" />
|
|
73
|
+
<span>{label}</span>
|
|
74
|
+
</div>
|
|
75
|
+
)}
|
|
76
|
+
</Button>
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
CloneButton.displayName = "CloneButton";
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import React from "react";
|
|
4
|
+
import { type BaseKey, useCreateButton } from "@refinedev/core";
|
|
5
|
+
import { Plus } from "lucide-react";
|
|
6
|
+
import { Button } from "@/components/ui/button";
|
|
7
|
+
|
|
8
|
+
type CreateButtonProps = {
|
|
9
|
+
/**
|
|
10
|
+
* Resource name for API data interactions. `identifier` of the resource can be used instead of the `name` of the resource.
|
|
11
|
+
* @default Inferred resource name from the route
|
|
12
|
+
*/
|
|
13
|
+
resource?: BaseKey;
|
|
14
|
+
/**
|
|
15
|
+
* Access Control configuration for the button
|
|
16
|
+
* @default `{ enabled: true, hideIfUnauthorized: false }`
|
|
17
|
+
*/
|
|
18
|
+
accessControl?: {
|
|
19
|
+
enabled?: boolean;
|
|
20
|
+
hideIfUnauthorized?: boolean;
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* `meta` property is used when creating the URL for the related action and path.
|
|
24
|
+
*/
|
|
25
|
+
meta?: Record<string, unknown>;
|
|
26
|
+
} & React.ComponentProps<typeof Button>;
|
|
27
|
+
|
|
28
|
+
export const CreateButton = React.forwardRef<
|
|
29
|
+
React.ComponentRef<typeof Button>,
|
|
30
|
+
CreateButtonProps
|
|
31
|
+
>(({ resource, accessControl, meta, children, onClick, ...rest }, ref) => {
|
|
32
|
+
const { hidden, disabled, LinkComponent, to, label } = useCreateButton({
|
|
33
|
+
resource,
|
|
34
|
+
accessControl,
|
|
35
|
+
meta,
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
const isDisabled = disabled || rest.disabled;
|
|
39
|
+
const isHidden = hidden || rest.hidden;
|
|
40
|
+
|
|
41
|
+
if (isHidden) return null;
|
|
42
|
+
|
|
43
|
+
return (
|
|
44
|
+
<Button
|
|
45
|
+
{...rest}
|
|
46
|
+
ref={ref}
|
|
47
|
+
disabled={isDisabled}
|
|
48
|
+
render={<LinkComponent to={to} replace={false} />}
|
|
49
|
+
onClick={(event) => {
|
|
50
|
+
if (isDisabled) {
|
|
51
|
+
event.preventDefault();
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
if (onClick) {
|
|
55
|
+
event.preventDefault();
|
|
56
|
+
onClick(event);
|
|
57
|
+
}
|
|
58
|
+
}}
|
|
59
|
+
>
|
|
60
|
+
{children ?? (
|
|
61
|
+
<div className="flex items-center gap-2 font-semibold">
|
|
62
|
+
<Plus className="w-4 h-4" />
|
|
63
|
+
<span>{label ?? "Create"}</span>
|
|
64
|
+
</div>
|
|
65
|
+
)}
|
|
66
|
+
</Button>
|
|
67
|
+
);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
CreateButton.displayName = "CreateButton";
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import React from "react";
|
|
4
|
+
import { type BaseKey, useDeleteButton } from "@refinedev/core";
|
|
5
|
+
import { Loader2, Trash } from "lucide-react";
|
|
6
|
+
import { Button } from "@/components/ui/button";
|
|
7
|
+
import {
|
|
8
|
+
Popover,
|
|
9
|
+
PopoverContent,
|
|
10
|
+
PopoverTrigger,
|
|
11
|
+
} from "@/components/ui/popover";
|
|
12
|
+
|
|
13
|
+
type DeleteButtonProps = {
|
|
14
|
+
/**
|
|
15
|
+
* Resource name for API data interactions. `identifier` of the resource can be used instead of the `name` of the resource.
|
|
16
|
+
* @default Inferred resource name from the route
|
|
17
|
+
*/
|
|
18
|
+
resource?: string;
|
|
19
|
+
/**
|
|
20
|
+
* Data item identifier for the actions with the API
|
|
21
|
+
* @default Reads `:id` from the URL
|
|
22
|
+
*/
|
|
23
|
+
recordItemId?: BaseKey;
|
|
24
|
+
/**
|
|
25
|
+
* Access Control configuration for the button
|
|
26
|
+
* @default `{ enabled: true, hideIfUnauthorized: false }`
|
|
27
|
+
*/
|
|
28
|
+
accessControl?: {
|
|
29
|
+
enabled?: boolean;
|
|
30
|
+
hideIfUnauthorized?: boolean;
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* `meta` property is used when creating the URL for the related action and path.
|
|
34
|
+
*/
|
|
35
|
+
meta?: Record<string, unknown>;
|
|
36
|
+
} & React.ComponentProps<typeof Button>;
|
|
37
|
+
|
|
38
|
+
export const DeleteButton = React.forwardRef<
|
|
39
|
+
React.ComponentRef<typeof Button>,
|
|
40
|
+
DeleteButtonProps
|
|
41
|
+
>(({ resource, recordItemId, accessControl, meta, children, ...rest }, ref) => {
|
|
42
|
+
const {
|
|
43
|
+
hidden,
|
|
44
|
+
disabled,
|
|
45
|
+
loading,
|
|
46
|
+
onConfirm,
|
|
47
|
+
label,
|
|
48
|
+
confirmTitle: defaultConfirmTitle,
|
|
49
|
+
confirmOkLabel: defaultConfirmOkLabel,
|
|
50
|
+
cancelLabel: defaultCancelLabel,
|
|
51
|
+
} = useDeleteButton({
|
|
52
|
+
resource,
|
|
53
|
+
id: recordItemId,
|
|
54
|
+
accessControl,
|
|
55
|
+
meta,
|
|
56
|
+
});
|
|
57
|
+
const [open, setOpen] = React.useState(false);
|
|
58
|
+
|
|
59
|
+
const isDisabled = disabled || rest.disabled || loading;
|
|
60
|
+
const isHidden = hidden || rest.hidden;
|
|
61
|
+
|
|
62
|
+
if (isHidden) return null;
|
|
63
|
+
|
|
64
|
+
const confirmCancelText = defaultCancelLabel;
|
|
65
|
+
const confirmOkText = defaultConfirmOkLabel;
|
|
66
|
+
const confirmTitle = defaultConfirmTitle;
|
|
67
|
+
|
|
68
|
+
return (
|
|
69
|
+
<Popover open={open} onOpenChange={setOpen}>
|
|
70
|
+
<PopoverTrigger render={<span className="inline-flex" />}>
|
|
71
|
+
<Button
|
|
72
|
+
variant="destructive"
|
|
73
|
+
{...rest}
|
|
74
|
+
ref={ref}
|
|
75
|
+
disabled={isDisabled}
|
|
76
|
+
>
|
|
77
|
+
{loading && <Loader2 className="mr-2 h-4 w-4 animate-spin" />}
|
|
78
|
+
{children ?? (
|
|
79
|
+
<div className="flex items-center gap-2 font-semibold">
|
|
80
|
+
<Trash className="h-4 w-4" />
|
|
81
|
+
<span>{label}</span>
|
|
82
|
+
</div>
|
|
83
|
+
)}
|
|
84
|
+
</Button>
|
|
85
|
+
</PopoverTrigger>
|
|
86
|
+
<PopoverContent className="w-auto" align="start">
|
|
87
|
+
<div className="flex flex-col gap-2">
|
|
88
|
+
<p className="text-sm">{confirmTitle}</p>
|
|
89
|
+
<div className="flex justify-end gap-2">
|
|
90
|
+
<Button variant="outline" size="sm" onClick={() => setOpen(false)}>
|
|
91
|
+
{confirmCancelText}
|
|
92
|
+
</Button>
|
|
93
|
+
<Button
|
|
94
|
+
variant="destructive"
|
|
95
|
+
size="sm"
|
|
96
|
+
disabled={loading}
|
|
97
|
+
onClick={() => {
|
|
98
|
+
if (typeof onConfirm === "function") {
|
|
99
|
+
onConfirm();
|
|
100
|
+
}
|
|
101
|
+
setOpen(false);
|
|
102
|
+
}}
|
|
103
|
+
>
|
|
104
|
+
{confirmOkText}
|
|
105
|
+
</Button>
|
|
106
|
+
</div>
|
|
107
|
+
</div>
|
|
108
|
+
</PopoverContent>
|
|
109
|
+
</Popover>
|
|
110
|
+
);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
DeleteButton.displayName = "DeleteButton";
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import React from "react";
|
|
4
|
+
import { type BaseKey, useEditButton } from "@refinedev/core";
|
|
5
|
+
import { Button } from "@/components/ui/button";
|
|
6
|
+
import { Pencil } from "lucide-react";
|
|
7
|
+
|
|
8
|
+
type EditButtonProps = {
|
|
9
|
+
/**
|
|
10
|
+
* Resource name for API data interactions. `identifier` of the resource can be used instead of the `name` of the resource.
|
|
11
|
+
* @default Inferred resource name from the route
|
|
12
|
+
*/
|
|
13
|
+
resource?: string;
|
|
14
|
+
/**
|
|
15
|
+
* Data item identifier for the actions with the API
|
|
16
|
+
* @default Reads `:id` from the URL
|
|
17
|
+
*/
|
|
18
|
+
recordItemId?: BaseKey;
|
|
19
|
+
/**
|
|
20
|
+
* Access Control configuration for the button
|
|
21
|
+
* @default `{ enabled: true, hideIfUnauthorized: false }`
|
|
22
|
+
*/
|
|
23
|
+
accessControl?: {
|
|
24
|
+
enabled?: boolean;
|
|
25
|
+
hideIfUnauthorized?: boolean;
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* `meta` property is used when creating the URL for the related action and path.
|
|
29
|
+
*/
|
|
30
|
+
meta?: Record<string, unknown>;
|
|
31
|
+
} & React.ComponentProps<typeof Button>;
|
|
32
|
+
|
|
33
|
+
export const EditButton = React.forwardRef<
|
|
34
|
+
React.ComponentRef<typeof Button>,
|
|
35
|
+
EditButtonProps
|
|
36
|
+
>(
|
|
37
|
+
(
|
|
38
|
+
{ resource, recordItemId, accessControl, meta, children, onClick, ...rest },
|
|
39
|
+
ref
|
|
40
|
+
) => {
|
|
41
|
+
const { hidden, disabled, LinkComponent, to, label } = useEditButton({
|
|
42
|
+
resource,
|
|
43
|
+
id: recordItemId,
|
|
44
|
+
accessControl,
|
|
45
|
+
meta,
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
const isDisabled = disabled || rest.disabled;
|
|
49
|
+
const isHidden = hidden || rest.hidden;
|
|
50
|
+
|
|
51
|
+
if (isHidden) return null;
|
|
52
|
+
|
|
53
|
+
return (
|
|
54
|
+
<Button
|
|
55
|
+
{...rest}
|
|
56
|
+
ref={ref}
|
|
57
|
+
disabled={isDisabled}
|
|
58
|
+
render={<LinkComponent to={to} replace={false} />}
|
|
59
|
+
onClick={(event) => {
|
|
60
|
+
if (isDisabled) {
|
|
61
|
+
event.preventDefault();
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
if (onClick) {
|
|
65
|
+
event.preventDefault();
|
|
66
|
+
onClick(event);
|
|
67
|
+
}
|
|
68
|
+
}}
|
|
69
|
+
>
|
|
70
|
+
{children ?? (
|
|
71
|
+
<div className="flex items-center gap-2 font-semibold">
|
|
72
|
+
<Pencil className="h-4 w-4" />
|
|
73
|
+
<span>{label}</span>
|
|
74
|
+
</div>
|
|
75
|
+
)}
|
|
76
|
+
</Button>
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
EditButton.displayName = "EditButton";
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import React from "react";
|
|
4
|
+
import { type BaseKey, useListButton } from "@refinedev/core";
|
|
5
|
+
import { Button } from "@/components/ui/button";
|
|
6
|
+
import { List } from "lucide-react";
|
|
7
|
+
|
|
8
|
+
type ListButtonProps = {
|
|
9
|
+
/**
|
|
10
|
+
* Resource name for API data interactions. `identifier` of the resource can be used instead of the `name` of the resource.
|
|
11
|
+
* @default Inferred resource name from the route
|
|
12
|
+
*/
|
|
13
|
+
resource?: BaseKey;
|
|
14
|
+
/**
|
|
15
|
+
* Access Control configuration for the button
|
|
16
|
+
* @default `{ enabled: true, hideIfUnauthorized: false }`
|
|
17
|
+
*/
|
|
18
|
+
accessControl?: {
|
|
19
|
+
enabled?: boolean;
|
|
20
|
+
hideIfUnauthorized?: boolean;
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* `meta` property is used when creating the URL for the related action and path.
|
|
24
|
+
*/
|
|
25
|
+
meta?: Record<string, unknown>;
|
|
26
|
+
} & React.ComponentProps<typeof Button>;
|
|
27
|
+
|
|
28
|
+
export const ListButton = React.forwardRef<
|
|
29
|
+
React.ComponentRef<typeof Button>,
|
|
30
|
+
ListButtonProps
|
|
31
|
+
>(({ resource, accessControl, meta, children, onClick, ...rest }, ref) => {
|
|
32
|
+
const { hidden, disabled, LinkComponent, to, label } = useListButton({
|
|
33
|
+
resource,
|
|
34
|
+
accessControl,
|
|
35
|
+
meta,
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
const isDisabled = disabled || rest.disabled;
|
|
39
|
+
const isHidden = hidden || rest.hidden;
|
|
40
|
+
|
|
41
|
+
if (isHidden) return null;
|
|
42
|
+
|
|
43
|
+
return (
|
|
44
|
+
<Button
|
|
45
|
+
{...rest}
|
|
46
|
+
ref={ref}
|
|
47
|
+
disabled={isDisabled}
|
|
48
|
+
render={<LinkComponent to={to} replace={false} />}
|
|
49
|
+
onClick={(event) => {
|
|
50
|
+
if (isDisabled) {
|
|
51
|
+
event.preventDefault();
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
if (onClick) {
|
|
55
|
+
event.preventDefault();
|
|
56
|
+
onClick(event);
|
|
57
|
+
}
|
|
58
|
+
}}
|
|
59
|
+
>
|
|
60
|
+
{children ?? (
|
|
61
|
+
<div className="flex items-center gap-2 font-semibold">
|
|
62
|
+
<List className="w-4 h-4" />
|
|
63
|
+
<span>{label}</span>
|
|
64
|
+
</div>
|
|
65
|
+
)}
|
|
66
|
+
</Button>
|
|
67
|
+
);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
ListButton.displayName = "ListButton";
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import React from "react";
|
|
4
|
+
import { type BaseKey, useRefreshButton } from "@refinedev/core";
|
|
5
|
+
import { RefreshCcw } from "lucide-react";
|
|
6
|
+
import { Button } from "@/components/ui/button";
|
|
7
|
+
import { cn } from "@/lib/utils";
|
|
8
|
+
|
|
9
|
+
type RefreshButtonProps = {
|
|
10
|
+
/**
|
|
11
|
+
* Resource name for API data interactions. `identifier` of the resource can be used instead of the `name` of the resource.
|
|
12
|
+
* @default Inferred resource name from the route
|
|
13
|
+
*/
|
|
14
|
+
resource?: string;
|
|
15
|
+
/**
|
|
16
|
+
* Data item identifier for the actions with the API
|
|
17
|
+
* @default Reads `:id` from the URL
|
|
18
|
+
*/
|
|
19
|
+
recordItemId?: BaseKey;
|
|
20
|
+
/**
|
|
21
|
+
* Target data provider name for API call to be made
|
|
22
|
+
* @default `"default"`
|
|
23
|
+
*/
|
|
24
|
+
dataProviderName?: string;
|
|
25
|
+
/**
|
|
26
|
+
* `meta` property is used when creating the URL for the related action and path.
|
|
27
|
+
*/
|
|
28
|
+
meta?: Record<string, unknown>;
|
|
29
|
+
} & React.ComponentProps<typeof Button>;
|
|
30
|
+
|
|
31
|
+
export const RefreshButton = React.forwardRef<
|
|
32
|
+
React.ComponentRef<typeof Button>,
|
|
33
|
+
RefreshButtonProps
|
|
34
|
+
>(
|
|
35
|
+
(
|
|
36
|
+
{ resource, recordItemId, dataProviderName, meta, children, onClick, ...rest },
|
|
37
|
+
ref
|
|
38
|
+
) => {
|
|
39
|
+
const {
|
|
40
|
+
onClick: refresh,
|
|
41
|
+
loading,
|
|
42
|
+
label,
|
|
43
|
+
} = useRefreshButton({
|
|
44
|
+
resource,
|
|
45
|
+
id: recordItemId,
|
|
46
|
+
dataProviderName,
|
|
47
|
+
meta,
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
const isDisabled = rest.disabled || loading;
|
|
51
|
+
|
|
52
|
+
return (
|
|
53
|
+
<Button
|
|
54
|
+
{...rest}
|
|
55
|
+
onClick={(event) => {
|
|
56
|
+
if (isDisabled) {
|
|
57
|
+
event.preventDefault();
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
refresh();
|
|
61
|
+
onClick?.(event);
|
|
62
|
+
}}
|
|
63
|
+
ref={ref}
|
|
64
|
+
disabled={isDisabled}
|
|
65
|
+
>
|
|
66
|
+
{children ?? (
|
|
67
|
+
<div className="flex items-center gap-2">
|
|
68
|
+
<RefreshCcw
|
|
69
|
+
className={cn("h-4 w-4", {
|
|
70
|
+
"animate-spin": loading,
|
|
71
|
+
})}
|
|
72
|
+
/>
|
|
73
|
+
<span>{label ?? "Refresh"}</span>
|
|
74
|
+
</div>
|
|
75
|
+
)}
|
|
76
|
+
</Button>
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
RefreshButton.displayName = "RefreshButton";
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import React from "react";
|
|
4
|
+
import { type BaseKey, useShowButton } from "@refinedev/core";
|
|
5
|
+
import { Eye } from "lucide-react";
|
|
6
|
+
import { Button } from "@/components/ui/button";
|
|
7
|
+
|
|
8
|
+
type ShowButtonProps = {
|
|
9
|
+
/**
|
|
10
|
+
* Resource name for API data interactions. `identifier` of the resource can be used instead of the `name` of the resource.
|
|
11
|
+
* @default Inferred resource name from the route
|
|
12
|
+
*/
|
|
13
|
+
resource?: string;
|
|
14
|
+
/**
|
|
15
|
+
* Data item identifier for the actions with the API
|
|
16
|
+
* @default Reads `:id` from the URL
|
|
17
|
+
*/
|
|
18
|
+
recordItemId?: BaseKey;
|
|
19
|
+
/**
|
|
20
|
+
* Access Control configuration for the button
|
|
21
|
+
* @default `{ enabled: true, hideIfUnauthorized: false }`
|
|
22
|
+
*/
|
|
23
|
+
accessControl?: {
|
|
24
|
+
enabled?: boolean;
|
|
25
|
+
hideIfUnauthorized?: boolean;
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* `meta` property is used when creating the URL for the related action and path.
|
|
29
|
+
*/
|
|
30
|
+
meta?: Record<string, unknown>;
|
|
31
|
+
} & React.ComponentProps<typeof Button>;
|
|
32
|
+
|
|
33
|
+
export const ShowButton = React.forwardRef<
|
|
34
|
+
React.ComponentRef<typeof Button>,
|
|
35
|
+
ShowButtonProps
|
|
36
|
+
>(
|
|
37
|
+
(
|
|
38
|
+
{ resource, recordItemId, accessControl, meta, children, onClick, ...rest },
|
|
39
|
+
ref
|
|
40
|
+
) => {
|
|
41
|
+
const { hidden, disabled, LinkComponent, to, label } = useShowButton({
|
|
42
|
+
resource,
|
|
43
|
+
id: recordItemId,
|
|
44
|
+
accessControl,
|
|
45
|
+
meta,
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
const isDisabled = disabled || rest.disabled;
|
|
49
|
+
const isHidden = hidden || rest.hidden;
|
|
50
|
+
|
|
51
|
+
if (isHidden) return null;
|
|
52
|
+
|
|
53
|
+
return (
|
|
54
|
+
<Button
|
|
55
|
+
{...rest}
|
|
56
|
+
ref={ref}
|
|
57
|
+
disabled={isDisabled}
|
|
58
|
+
render={<LinkComponent to={to} replace={false} />}
|
|
59
|
+
onClick={(event) => {
|
|
60
|
+
if (isDisabled) {
|
|
61
|
+
event.preventDefault();
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
if (onClick) {
|
|
65
|
+
event.preventDefault();
|
|
66
|
+
onClick(event);
|
|
67
|
+
}
|
|
68
|
+
}}
|
|
69
|
+
>
|
|
70
|
+
{children ?? (
|
|
71
|
+
<div className="flex items-center gap-2 font-semibold">
|
|
72
|
+
<Eye className="h-4 w-4" />
|
|
73
|
+
<span>{label}</span>
|
|
74
|
+
</div>
|
|
75
|
+
)}
|
|
76
|
+
</Button>
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
ShowButton.displayName = "ShowButton";
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { Badge } from "@/components/ui/badge";
|
|
2
|
+
import { cn } from "@/lib/utils";
|
|
3
|
+
|
|
4
|
+
type StatusBadgeProps = {
|
|
5
|
+
status?: string | null;
|
|
6
|
+
className?: string;
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
const dotStyles: Record<string, string> = {
|
|
10
|
+
published: "bg-foreground",
|
|
11
|
+
draft: "bg-neutral-400 dark:bg-neutral-500",
|
|
12
|
+
rejected: "border border-current bg-transparent",
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export function StatusBadge({ status, className }: StatusBadgeProps) {
|
|
16
|
+
const normalizedStatus = status?.toLowerCase() || "unknown";
|
|
17
|
+
|
|
18
|
+
return (
|
|
19
|
+
<Badge
|
|
20
|
+
variant="outline"
|
|
21
|
+
className={cn(
|
|
22
|
+
"h-6 gap-1.5 rounded-md border-border/80 bg-card px-2 text-[11px] font-medium capitalize text-foreground shadow-none",
|
|
23
|
+
normalizedStatus === "rejected" && "text-muted-foreground",
|
|
24
|
+
className
|
|
25
|
+
)}
|
|
26
|
+
>
|
|
27
|
+
<span
|
|
28
|
+
aria-hidden="true"
|
|
29
|
+
className={cn(
|
|
30
|
+
"size-1.5 rounded-full",
|
|
31
|
+
dotStyles[normalizedStatus] ?? "bg-muted-foreground"
|
|
32
|
+
)}
|
|
33
|
+
/>
|
|
34
|
+
{normalizedStatus}
|
|
35
|
+
</Badge>
|
|
36
|
+
);
|
|
37
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import { cn } from "@/lib/utils";
|
|
4
|
+
import {
|
|
5
|
+
useBack,
|
|
6
|
+
useResourceParams,
|
|
7
|
+
useUserFriendlyName,
|
|
8
|
+
} from "@refinedev/core";
|
|
9
|
+
import type { PropsWithChildren } from "react";
|
|
10
|
+
import { Breadcrumb } from "@/components/app-shell/breadcrumb";
|
|
11
|
+
import { Button } from "@/components/ui/button";
|
|
12
|
+
import { ArrowLeftIcon } from "lucide-react";
|
|
13
|
+
|
|
14
|
+
type CreateViewProps = PropsWithChildren<{
|
|
15
|
+
className?: string;
|
|
16
|
+
}>;
|
|
17
|
+
|
|
18
|
+
export function CreateView({ children, className }: CreateViewProps) {
|
|
19
|
+
return (
|
|
20
|
+
<div className={cn("flex flex-col", "gap-6", className)}>
|
|
21
|
+
<CreateViewHeader />
|
|
22
|
+
{children}
|
|
23
|
+
</div>
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
type CreateHeaderProps = PropsWithChildren<{
|
|
28
|
+
resource?: string;
|
|
29
|
+
title?: string;
|
|
30
|
+
wrapperClassName?: string;
|
|
31
|
+
headerClassName?: string;
|
|
32
|
+
}>;
|
|
33
|
+
|
|
34
|
+
export const CreateViewHeader = ({
|
|
35
|
+
resource: resourceFromProps,
|
|
36
|
+
title: titleFromProps,
|
|
37
|
+
wrapperClassName,
|
|
38
|
+
headerClassName,
|
|
39
|
+
}: CreateHeaderProps) => {
|
|
40
|
+
const back = useBack();
|
|
41
|
+
|
|
42
|
+
const getUserFriendlyName = useUserFriendlyName();
|
|
43
|
+
|
|
44
|
+
const { resource, identifier } = useResourceParams({
|
|
45
|
+
resource: resourceFromProps,
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
const resourceTitle = getUserFriendlyName(
|
|
49
|
+
resource?.meta?.label ?? identifier ?? resource?.name,
|
|
50
|
+
"singular"
|
|
51
|
+
);
|
|
52
|
+
const title = titleFromProps ?? `Create ${resourceTitle}`;
|
|
53
|
+
|
|
54
|
+
return (
|
|
55
|
+
<div className={cn("flex flex-col", "gap-3", wrapperClassName)}>
|
|
56
|
+
<div className="flex items-center text-muted-foreground">
|
|
57
|
+
<Breadcrumb />
|
|
58
|
+
</div>
|
|
59
|
+
<div className={cn("flex items-start gap-2", headerClassName)}>
|
|
60
|
+
<Button
|
|
61
|
+
variant="ghost"
|
|
62
|
+
size="icon"
|
|
63
|
+
className="mt-0.5 rounded-lg"
|
|
64
|
+
onClick={back}
|
|
65
|
+
>
|
|
66
|
+
<ArrowLeftIcon className="h-4 w-4" />
|
|
67
|
+
</Button>
|
|
68
|
+
<div>
|
|
69
|
+
<h2 className="text-3xl font-semibold tracking-[-0.035em]">
|
|
70
|
+
{title}
|
|
71
|
+
</h2>
|
|
72
|
+
<p className="mt-2 text-sm text-muted-foreground">
|
|
73
|
+
Add a new {resourceTitle.toLowerCase()} to your NocoBase workspace.
|
|
74
|
+
</p>
|
|
75
|
+
</div>
|
|
76
|
+
</div>
|
|
77
|
+
</div>
|
|
78
|
+
);
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
CreateView.displayName = "CreateView";
|