@arqel-dev/ui 0.9.0 → 0.9.2
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/pages.d.ts +13 -0
- package/dist/pages.js +638 -3
- package/dist/pages.js.map +1 -1
- package/package.json +6 -6
package/dist/pages.d.ts
CHANGED
|
@@ -29,6 +29,19 @@ declare function ArqelEditPage<TRecord extends RecordType = RecordType>(): JSX.E
|
|
|
29
29
|
* `<ResourceIndex>`. Apps can override per-resource by registering
|
|
30
30
|
* their own page component at `Pages/Arqel/{Slug}/Index.tsx` (the
|
|
31
31
|
* lookup falls through to user pages first inside `createArqelApp`).
|
|
32
|
+
*
|
|
33
|
+
* Wires the standard table interactions — search / filter / sort /
|
|
34
|
+
* pagination — into Inertia partial `router.get` visits so the
|
|
35
|
+
* server can re-run TableQueryBuilder with the new query string.
|
|
36
|
+
*
|
|
37
|
+
* Query-string contract (matches `Arqel\Table\TableQueryBuilder`):
|
|
38
|
+
* - `?search=foo` global search
|
|
39
|
+
* - `?sort=column&direction=asc|desc`
|
|
40
|
+
* - `?page=N&per_page=M`
|
|
41
|
+
* - `?filter[name]=value` one entry per filter
|
|
42
|
+
*
|
|
43
|
+
* `search` is debounced (300ms) so typing doesn't hammer the
|
|
44
|
+
* backend; the rest fire immediately.
|
|
32
45
|
*/
|
|
33
46
|
|
|
34
47
|
declare function ArqelIndexPage<TRecord extends RecordType = RecordType>(): JSX.Element;
|
package/dist/pages.js
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import { clsx } from 'clsx';
|
|
2
2
|
import { twMerge } from 'tailwind-merge';
|
|
3
|
-
import { useRef,
|
|
3
|
+
import { useMemo, useState, useRef, useEffect, useCallback, useId } from 'react';
|
|
4
4
|
import { cva } from 'class-variance-authority';
|
|
5
|
-
import { Slot } from 'radix-ui';
|
|
5
|
+
import { Slot, DropdownMenu as DropdownMenu$1, Dialog as Dialog$1 } from 'radix-ui';
|
|
6
6
|
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
7
7
|
import { isFieldEntry, isLayoutEntry } from '@arqel-dev/types/forms';
|
|
8
8
|
import { useArqelForm } from '@arqel-dev/hooks';
|
|
9
9
|
import { usePage, router } from '@inertiajs/react';
|
|
10
|
+
import { XIcon } from 'lucide-react';
|
|
10
11
|
import { useReactTable, getCoreRowModel, flexRender } from '@tanstack/react-table';
|
|
11
12
|
|
|
12
13
|
var __defProp = Object.defineProperty;
|
|
@@ -865,6 +866,477 @@ var init_ArqelEditPage = __esm({
|
|
|
865
866
|
};
|
|
866
867
|
}
|
|
867
868
|
});
|
|
869
|
+
function DropdownMenu({
|
|
870
|
+
...props
|
|
871
|
+
}) {
|
|
872
|
+
return /* @__PURE__ */ jsx(DropdownMenu$1.Root, { "data-slot": "dropdown-menu", ...props });
|
|
873
|
+
}
|
|
874
|
+
function DropdownMenuTrigger({
|
|
875
|
+
...props
|
|
876
|
+
}) {
|
|
877
|
+
return /* @__PURE__ */ jsx(
|
|
878
|
+
DropdownMenu$1.Trigger,
|
|
879
|
+
{
|
|
880
|
+
"data-slot": "dropdown-menu-trigger",
|
|
881
|
+
...props
|
|
882
|
+
}
|
|
883
|
+
);
|
|
884
|
+
}
|
|
885
|
+
function DropdownMenuContent({
|
|
886
|
+
className,
|
|
887
|
+
sideOffset = 4,
|
|
888
|
+
...props
|
|
889
|
+
}) {
|
|
890
|
+
return /* @__PURE__ */ jsx(DropdownMenu$1.Portal, { children: /* @__PURE__ */ jsx(
|
|
891
|
+
DropdownMenu$1.Content,
|
|
892
|
+
{
|
|
893
|
+
"data-slot": "dropdown-menu-content",
|
|
894
|
+
sideOffset,
|
|
895
|
+
className: cn(
|
|
896
|
+
"z-50 max-h-(--radix-dropdown-menu-content-available-height) min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border bg-popover p-1 text-popover-foreground shadow-md data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95",
|
|
897
|
+
className
|
|
898
|
+
),
|
|
899
|
+
...props
|
|
900
|
+
}
|
|
901
|
+
) });
|
|
902
|
+
}
|
|
903
|
+
function DropdownMenuItem({
|
|
904
|
+
className,
|
|
905
|
+
inset,
|
|
906
|
+
variant = "default",
|
|
907
|
+
...props
|
|
908
|
+
}) {
|
|
909
|
+
return /* @__PURE__ */ jsx(
|
|
910
|
+
DropdownMenu$1.Item,
|
|
911
|
+
{
|
|
912
|
+
"data-slot": "dropdown-menu-item",
|
|
913
|
+
"data-inset": inset,
|
|
914
|
+
"data-variant": variant,
|
|
915
|
+
className: cn(
|
|
916
|
+
"relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 data-[variant=destructive]:focus:text-destructive dark:data-[variant=destructive]:focus:bg-destructive/20 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 [&_svg:not([class*='text-'])]:text-muted-foreground data-[variant=destructive]:*:[svg]:text-destructive!",
|
|
917
|
+
className
|
|
918
|
+
),
|
|
919
|
+
...props
|
|
920
|
+
}
|
|
921
|
+
);
|
|
922
|
+
}
|
|
923
|
+
var init_dropdown_menu = __esm({
|
|
924
|
+
"src/shadcn/ui/dropdown-menu.tsx"() {
|
|
925
|
+
"use client";
|
|
926
|
+
init_cn();
|
|
927
|
+
}
|
|
928
|
+
});
|
|
929
|
+
function Dialog({
|
|
930
|
+
...props
|
|
931
|
+
}) {
|
|
932
|
+
return /* @__PURE__ */ jsx(Dialog$1.Root, { "data-slot": "dialog", ...props });
|
|
933
|
+
}
|
|
934
|
+
function DialogPortal({
|
|
935
|
+
...props
|
|
936
|
+
}) {
|
|
937
|
+
return /* @__PURE__ */ jsx(Dialog$1.Portal, { "data-slot": "dialog-portal", ...props });
|
|
938
|
+
}
|
|
939
|
+
function DialogClose({
|
|
940
|
+
...props
|
|
941
|
+
}) {
|
|
942
|
+
return /* @__PURE__ */ jsx(Dialog$1.Close, { "data-slot": "dialog-close", ...props });
|
|
943
|
+
}
|
|
944
|
+
function DialogOverlay({
|
|
945
|
+
className,
|
|
946
|
+
...props
|
|
947
|
+
}) {
|
|
948
|
+
return /* @__PURE__ */ jsx(
|
|
949
|
+
Dialog$1.Overlay,
|
|
950
|
+
{
|
|
951
|
+
"data-slot": "dialog-overlay",
|
|
952
|
+
className: cn(
|
|
953
|
+
"fixed inset-0 z-50 bg-black/50 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:animate-in data-[state=open]:fade-in-0",
|
|
954
|
+
className
|
|
955
|
+
),
|
|
956
|
+
...props
|
|
957
|
+
}
|
|
958
|
+
);
|
|
959
|
+
}
|
|
960
|
+
function DialogContent({
|
|
961
|
+
className,
|
|
962
|
+
children,
|
|
963
|
+
showCloseButton = true,
|
|
964
|
+
...props
|
|
965
|
+
}) {
|
|
966
|
+
return /* @__PURE__ */ jsxs(DialogPortal, { "data-slot": "dialog-portal", children: [
|
|
967
|
+
/* @__PURE__ */ jsx(DialogOverlay, {}),
|
|
968
|
+
/* @__PURE__ */ jsxs(
|
|
969
|
+
Dialog$1.Content,
|
|
970
|
+
{
|
|
971
|
+
"data-slot": "dialog-content",
|
|
972
|
+
className: cn(
|
|
973
|
+
"fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border bg-background p-6 shadow-lg duration-200 outline-none data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95 sm:max-w-lg",
|
|
974
|
+
className
|
|
975
|
+
),
|
|
976
|
+
...props,
|
|
977
|
+
children: [
|
|
978
|
+
children,
|
|
979
|
+
showCloseButton && /* @__PURE__ */ jsxs(
|
|
980
|
+
Dialog$1.Close,
|
|
981
|
+
{
|
|
982
|
+
"data-slot": "dialog-close",
|
|
983
|
+
className: "absolute top-4 right-4 rounded-xs opacity-70 ring-offset-background transition-opacity hover:opacity-100 focus:ring-2 focus:ring-ring focus:ring-offset-2 focus:outline-hidden disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
|
|
984
|
+
children: [
|
|
985
|
+
/* @__PURE__ */ jsx(XIcon, {}),
|
|
986
|
+
/* @__PURE__ */ jsx("span", { className: "sr-only", children: "Close" })
|
|
987
|
+
]
|
|
988
|
+
}
|
|
989
|
+
)
|
|
990
|
+
]
|
|
991
|
+
}
|
|
992
|
+
)
|
|
993
|
+
] });
|
|
994
|
+
}
|
|
995
|
+
function DialogHeader({ className, ...props }) {
|
|
996
|
+
return /* @__PURE__ */ jsx(
|
|
997
|
+
"div",
|
|
998
|
+
{
|
|
999
|
+
"data-slot": "dialog-header",
|
|
1000
|
+
className: cn("flex flex-col gap-2 text-center sm:text-left", className),
|
|
1001
|
+
...props
|
|
1002
|
+
}
|
|
1003
|
+
);
|
|
1004
|
+
}
|
|
1005
|
+
function DialogFooter({
|
|
1006
|
+
className,
|
|
1007
|
+
showCloseButton = false,
|
|
1008
|
+
children,
|
|
1009
|
+
...props
|
|
1010
|
+
}) {
|
|
1011
|
+
return /* @__PURE__ */ jsxs(
|
|
1012
|
+
"div",
|
|
1013
|
+
{
|
|
1014
|
+
"data-slot": "dialog-footer",
|
|
1015
|
+
className: cn(
|
|
1016
|
+
"flex flex-col-reverse gap-2 sm:flex-row sm:justify-end",
|
|
1017
|
+
className
|
|
1018
|
+
),
|
|
1019
|
+
...props,
|
|
1020
|
+
children: [
|
|
1021
|
+
children,
|
|
1022
|
+
showCloseButton && /* @__PURE__ */ jsx(Dialog$1.Close, { asChild: true, children: /* @__PURE__ */ jsx(Button, { variant: "outline", children: "Close" }) })
|
|
1023
|
+
]
|
|
1024
|
+
}
|
|
1025
|
+
);
|
|
1026
|
+
}
|
|
1027
|
+
function DialogTitle({
|
|
1028
|
+
className,
|
|
1029
|
+
...props
|
|
1030
|
+
}) {
|
|
1031
|
+
return /* @__PURE__ */ jsx(
|
|
1032
|
+
Dialog$1.Title,
|
|
1033
|
+
{
|
|
1034
|
+
"data-slot": "dialog-title",
|
|
1035
|
+
className: cn("text-lg leading-none font-semibold", className),
|
|
1036
|
+
...props
|
|
1037
|
+
}
|
|
1038
|
+
);
|
|
1039
|
+
}
|
|
1040
|
+
function DialogDescription({
|
|
1041
|
+
className,
|
|
1042
|
+
...props
|
|
1043
|
+
}) {
|
|
1044
|
+
return /* @__PURE__ */ jsx(
|
|
1045
|
+
Dialog$1.Description,
|
|
1046
|
+
{
|
|
1047
|
+
"data-slot": "dialog-description",
|
|
1048
|
+
className: cn("text-sm text-muted-foreground", className),
|
|
1049
|
+
...props
|
|
1050
|
+
}
|
|
1051
|
+
);
|
|
1052
|
+
}
|
|
1053
|
+
var init_dialog = __esm({
|
|
1054
|
+
"src/shadcn/ui/dialog.tsx"() {
|
|
1055
|
+
init_cn();
|
|
1056
|
+
init_button();
|
|
1057
|
+
}
|
|
1058
|
+
});
|
|
1059
|
+
function ActionFormModal({
|
|
1060
|
+
open,
|
|
1061
|
+
onOpenChange,
|
|
1062
|
+
action,
|
|
1063
|
+
fields,
|
|
1064
|
+
onSubmit,
|
|
1065
|
+
processing = false,
|
|
1066
|
+
errors = {},
|
|
1067
|
+
initialValues = {}
|
|
1068
|
+
}) {
|
|
1069
|
+
const [values, setValues] = useState(initialValues);
|
|
1070
|
+
const size = action.modalSize ?? "md";
|
|
1071
|
+
const schema = useMemo(
|
|
1072
|
+
() => ({
|
|
1073
|
+
schema: (action.form ?? []).map((entry) => ({
|
|
1074
|
+
kind: "field",
|
|
1075
|
+
name: entry.name,
|
|
1076
|
+
type: entry.type
|
|
1077
|
+
})),
|
|
1078
|
+
columns: 1,
|
|
1079
|
+
model: null,
|
|
1080
|
+
inline: false,
|
|
1081
|
+
disabled: false
|
|
1082
|
+
}),
|
|
1083
|
+
[action.form]
|
|
1084
|
+
);
|
|
1085
|
+
return /* @__PURE__ */ jsx(Dialog, { open, onOpenChange: (next) => onOpenChange(next), children: /* @__PURE__ */ jsxs(DialogContent, { className: cn("overflow-auto", SIZE_CLASSES[size]), children: [
|
|
1086
|
+
/* @__PURE__ */ jsx(DialogHeader, { children: /* @__PURE__ */ jsx(DialogTitle, { children: action.label }) }),
|
|
1087
|
+
/* @__PURE__ */ jsxs(
|
|
1088
|
+
"form",
|
|
1089
|
+
{
|
|
1090
|
+
className: "flex flex-col gap-4",
|
|
1091
|
+
onSubmit: (e) => {
|
|
1092
|
+
e.preventDefault();
|
|
1093
|
+
onSubmit(values);
|
|
1094
|
+
},
|
|
1095
|
+
children: [
|
|
1096
|
+
/* @__PURE__ */ jsx(
|
|
1097
|
+
FormRenderer,
|
|
1098
|
+
{
|
|
1099
|
+
schema,
|
|
1100
|
+
fields,
|
|
1101
|
+
values,
|
|
1102
|
+
errors,
|
|
1103
|
+
onChange: (name, value) => setValues((prev) => ({ ...prev, [name]: value })),
|
|
1104
|
+
disabled: processing
|
|
1105
|
+
}
|
|
1106
|
+
),
|
|
1107
|
+
/* @__PURE__ */ jsx(
|
|
1108
|
+
FormActions,
|
|
1109
|
+
{
|
|
1110
|
+
processing,
|
|
1111
|
+
submitLabel: action.label,
|
|
1112
|
+
extra: /* @__PURE__ */ jsx(DialogClose, { asChild: true, children: /* @__PURE__ */ jsx(Button, { type: "button", variant: "ghost", disabled: processing, children: "Cancel" }) })
|
|
1113
|
+
}
|
|
1114
|
+
)
|
|
1115
|
+
]
|
|
1116
|
+
}
|
|
1117
|
+
)
|
|
1118
|
+
] }) });
|
|
1119
|
+
}
|
|
1120
|
+
var SIZE_CLASSES;
|
|
1121
|
+
var init_ActionFormModal = __esm({
|
|
1122
|
+
"src/action/ActionFormModal.tsx"() {
|
|
1123
|
+
init_FormActions();
|
|
1124
|
+
init_FormRenderer();
|
|
1125
|
+
init_dialog();
|
|
1126
|
+
init_cn();
|
|
1127
|
+
init_Button();
|
|
1128
|
+
SIZE_CLASSES = {
|
|
1129
|
+
sm: "sm:max-w-[24rem]",
|
|
1130
|
+
md: "sm:max-w-[32rem]",
|
|
1131
|
+
lg: "sm:max-w-[44rem]",
|
|
1132
|
+
xl: "sm:max-w-[56rem]",
|
|
1133
|
+
full: "sm:max-w-[90vw] h-[90vh]"
|
|
1134
|
+
};
|
|
1135
|
+
}
|
|
1136
|
+
});
|
|
1137
|
+
function ConfirmDialog({
|
|
1138
|
+
open,
|
|
1139
|
+
onOpenChange,
|
|
1140
|
+
config,
|
|
1141
|
+
onConfirm,
|
|
1142
|
+
processing = false
|
|
1143
|
+
}) {
|
|
1144
|
+
const [typed, setTyped] = useState("");
|
|
1145
|
+
useEffect(() => {
|
|
1146
|
+
if (!open) setTyped("");
|
|
1147
|
+
}, [open]);
|
|
1148
|
+
const requiresText = config?.requiresText;
|
|
1149
|
+
const submitDisabled = processing || requiresText !== void 0 && typed !== requiresText;
|
|
1150
|
+
const variant = COLOR_VARIANT[config?.color ?? "destructive"];
|
|
1151
|
+
const submitLabel = config?.submitLabel ?? (config?.color === "info" ? "Confirm" : "Delete");
|
|
1152
|
+
const cancelLabel = config?.cancelLabel ?? "Cancel";
|
|
1153
|
+
return /* @__PURE__ */ jsx(Dialog, { open, onOpenChange: (next) => onOpenChange(next), children: /* @__PURE__ */ jsxs(DialogContent, { className: "sm:max-w-[28rem]", showCloseButton: false, children: [
|
|
1154
|
+
/* @__PURE__ */ jsxs(DialogHeader, { children: [
|
|
1155
|
+
/* @__PURE__ */ jsx(DialogTitle, { children: config?.heading ?? "Are you sure?" }),
|
|
1156
|
+
config?.description && /* @__PURE__ */ jsx(DialogDescription, { children: config.description })
|
|
1157
|
+
] }),
|
|
1158
|
+
requiresText !== void 0 && /* @__PURE__ */ jsxs("label", { className: "flex flex-col gap-1 text-xs text-muted-foreground", children: [
|
|
1159
|
+
/* @__PURE__ */ jsxs("span", { children: [
|
|
1160
|
+
"Type ",
|
|
1161
|
+
/* @__PURE__ */ jsx("code", { className: "font-mono text-foreground", children: requiresText }),
|
|
1162
|
+
" to confirm"
|
|
1163
|
+
] }),
|
|
1164
|
+
/* @__PURE__ */ jsx(
|
|
1165
|
+
"input",
|
|
1166
|
+
{
|
|
1167
|
+
type: "text",
|
|
1168
|
+
ref: (el) => el?.focus(),
|
|
1169
|
+
value: typed,
|
|
1170
|
+
onChange: (e) => setTyped(e.target.value),
|
|
1171
|
+
onKeyDown: (e) => {
|
|
1172
|
+
if (e.key === "Enter" && !submitDisabled) {
|
|
1173
|
+
e.preventDefault();
|
|
1174
|
+
onConfirm();
|
|
1175
|
+
}
|
|
1176
|
+
},
|
|
1177
|
+
className: "h-9 rounded-md border border-input bg-background px-3 text-sm focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
|
1178
|
+
}
|
|
1179
|
+
)
|
|
1180
|
+
] }),
|
|
1181
|
+
/* @__PURE__ */ jsxs(DialogFooter, { children: [
|
|
1182
|
+
/* @__PURE__ */ jsx(DialogClose, { asChild: true, children: /* @__PURE__ */ jsx(Button, { type: "button", variant: "ghost", disabled: processing, children: cancelLabel }) }),
|
|
1183
|
+
/* @__PURE__ */ jsx(Button, { type: "button", variant, disabled: submitDisabled, onClick: onConfirm, children: processing ? "Working\u2026" : submitLabel })
|
|
1184
|
+
] })
|
|
1185
|
+
] }) });
|
|
1186
|
+
}
|
|
1187
|
+
var COLOR_VARIANT;
|
|
1188
|
+
var init_ConfirmDialog = __esm({
|
|
1189
|
+
"src/action/ConfirmDialog.tsx"() {
|
|
1190
|
+
init_dialog();
|
|
1191
|
+
init_Button();
|
|
1192
|
+
COLOR_VARIANT = {
|
|
1193
|
+
destructive: "destructive",
|
|
1194
|
+
warning: "default",
|
|
1195
|
+
info: "default"
|
|
1196
|
+
};
|
|
1197
|
+
}
|
|
1198
|
+
});
|
|
1199
|
+
function ActionButton({
|
|
1200
|
+
action,
|
|
1201
|
+
formFields = [],
|
|
1202
|
+
onInvoke,
|
|
1203
|
+
processing = false,
|
|
1204
|
+
errors,
|
|
1205
|
+
size = "default",
|
|
1206
|
+
className
|
|
1207
|
+
}) {
|
|
1208
|
+
const [confirmOpen, setConfirmOpen] = useState(false);
|
|
1209
|
+
const [formOpen, setFormOpen] = useState(false);
|
|
1210
|
+
const variant = VARIANT_OVERRIDE[action.variant] ?? COLOR_TO_VARIANT[action.color];
|
|
1211
|
+
const handleClick = () => {
|
|
1212
|
+
if (action.disabled) return;
|
|
1213
|
+
if (action.requiresConfirmation) {
|
|
1214
|
+
setConfirmOpen(true);
|
|
1215
|
+
} else if (action.form && action.form.length > 0) {
|
|
1216
|
+
setFormOpen(true);
|
|
1217
|
+
} else {
|
|
1218
|
+
onInvoke();
|
|
1219
|
+
}
|
|
1220
|
+
};
|
|
1221
|
+
const handleConfirm = () => {
|
|
1222
|
+
setConfirmOpen(false);
|
|
1223
|
+
if (action.form && action.form.length > 0) {
|
|
1224
|
+
setFormOpen(true);
|
|
1225
|
+
} else {
|
|
1226
|
+
onInvoke();
|
|
1227
|
+
}
|
|
1228
|
+
};
|
|
1229
|
+
const handleFormSubmit = (values) => {
|
|
1230
|
+
onInvoke(values);
|
|
1231
|
+
setFormOpen(false);
|
|
1232
|
+
};
|
|
1233
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
1234
|
+
/* @__PURE__ */ jsx(
|
|
1235
|
+
Button,
|
|
1236
|
+
{
|
|
1237
|
+
variant,
|
|
1238
|
+
size,
|
|
1239
|
+
disabled: action.disabled === true || processing,
|
|
1240
|
+
title: action.tooltip,
|
|
1241
|
+
onClick: handleClick,
|
|
1242
|
+
className,
|
|
1243
|
+
children: action.label
|
|
1244
|
+
}
|
|
1245
|
+
),
|
|
1246
|
+
action.requiresConfirmation && /* @__PURE__ */ jsx(
|
|
1247
|
+
ConfirmDialog,
|
|
1248
|
+
{
|
|
1249
|
+
open: confirmOpen,
|
|
1250
|
+
onOpenChange: setConfirmOpen,
|
|
1251
|
+
config: action.confirmation,
|
|
1252
|
+
onConfirm: handleConfirm,
|
|
1253
|
+
processing
|
|
1254
|
+
}
|
|
1255
|
+
),
|
|
1256
|
+
action.form && action.form.length > 0 && /* @__PURE__ */ jsx(
|
|
1257
|
+
ActionFormModal,
|
|
1258
|
+
{
|
|
1259
|
+
open: formOpen,
|
|
1260
|
+
onOpenChange: setFormOpen,
|
|
1261
|
+
action,
|
|
1262
|
+
fields: formFields,
|
|
1263
|
+
onSubmit: handleFormSubmit,
|
|
1264
|
+
processing,
|
|
1265
|
+
errors
|
|
1266
|
+
}
|
|
1267
|
+
)
|
|
1268
|
+
] });
|
|
1269
|
+
}
|
|
1270
|
+
var COLOR_TO_VARIANT, VARIANT_OVERRIDE;
|
|
1271
|
+
var init_ActionButton = __esm({
|
|
1272
|
+
"src/action/ActionButton.tsx"() {
|
|
1273
|
+
init_ActionFormModal();
|
|
1274
|
+
init_Button();
|
|
1275
|
+
init_ConfirmDialog();
|
|
1276
|
+
COLOR_TO_VARIANT = {
|
|
1277
|
+
primary: "default",
|
|
1278
|
+
secondary: "outline",
|
|
1279
|
+
destructive: "destructive",
|
|
1280
|
+
success: "default",
|
|
1281
|
+
warning: "default",
|
|
1282
|
+
info: "default"
|
|
1283
|
+
};
|
|
1284
|
+
VARIANT_OVERRIDE = {
|
|
1285
|
+
default: "default",
|
|
1286
|
+
outline: "outline",
|
|
1287
|
+
ghost: "ghost",
|
|
1288
|
+
destructive: "destructive"
|
|
1289
|
+
};
|
|
1290
|
+
}
|
|
1291
|
+
});
|
|
1292
|
+
function ActionMenu({
|
|
1293
|
+
actions,
|
|
1294
|
+
formFieldsByAction = {},
|
|
1295
|
+
onInvoke,
|
|
1296
|
+
inlineThreshold = 3,
|
|
1297
|
+
processing = false,
|
|
1298
|
+
trigger,
|
|
1299
|
+
className
|
|
1300
|
+
}) {
|
|
1301
|
+
if (actions.length === 0) return null;
|
|
1302
|
+
if (actions.length <= inlineThreshold) {
|
|
1303
|
+
return /* @__PURE__ */ jsx("div", { className: cn("flex flex-wrap items-center gap-1", className), children: actions.map((action) => /* @__PURE__ */ jsx(
|
|
1304
|
+
ActionButton,
|
|
1305
|
+
{
|
|
1306
|
+
action,
|
|
1307
|
+
formFields: formFieldsByAction[action.name] ?? [],
|
|
1308
|
+
onInvoke: (values) => onInvoke(action, values),
|
|
1309
|
+
processing,
|
|
1310
|
+
size: "sm"
|
|
1311
|
+
},
|
|
1312
|
+
action.name
|
|
1313
|
+
)) });
|
|
1314
|
+
}
|
|
1315
|
+
return /* @__PURE__ */ jsxs(DropdownMenu, { children: [
|
|
1316
|
+
/* @__PURE__ */ jsx(DropdownMenuTrigger, { asChild: true, children: trigger ?? /* @__PURE__ */ jsx(Button, { variant: "ghost", size: "icon", "aria-label": "Actions", children: "\u22EF" }) }),
|
|
1317
|
+
/* @__PURE__ */ jsx(DropdownMenuContent, { align: "end", sideOffset: 6, className: cn("min-w-[12rem]", className), children: actions.map((action) => /* @__PURE__ */ jsx(
|
|
1318
|
+
DropdownMenuItem,
|
|
1319
|
+
{
|
|
1320
|
+
disabled: action.disabled === true,
|
|
1321
|
+
variant: action.color === "destructive" ? "destructive" : "default",
|
|
1322
|
+
onSelect: () => {
|
|
1323
|
+
if (action.disabled) return;
|
|
1324
|
+
onInvoke(action);
|
|
1325
|
+
},
|
|
1326
|
+
children: action.label
|
|
1327
|
+
},
|
|
1328
|
+
action.name
|
|
1329
|
+
)) })
|
|
1330
|
+
] });
|
|
1331
|
+
}
|
|
1332
|
+
var init_ActionMenu = __esm({
|
|
1333
|
+
"src/action/ActionMenu.tsx"() {
|
|
1334
|
+
init_dropdown_menu();
|
|
1335
|
+
init_cn();
|
|
1336
|
+
init_ActionButton();
|
|
1337
|
+
init_Button();
|
|
1338
|
+
}
|
|
1339
|
+
});
|
|
868
1340
|
function TableCell({ column, value }) {
|
|
869
1341
|
switch (column.type) {
|
|
870
1342
|
case "text":
|
|
@@ -1566,13 +2038,176 @@ var ArqelIndexPage_exports = {};
|
|
|
1566
2038
|
__export(ArqelIndexPage_exports, {
|
|
1567
2039
|
default: () => ArqelIndexPage
|
|
1568
2040
|
});
|
|
2041
|
+
function pruneEmptyFilters(values) {
|
|
2042
|
+
const out = {};
|
|
2043
|
+
for (const [k, v] of Object.entries(values)) {
|
|
2044
|
+
if (v === void 0 || v === null || v === "") continue;
|
|
2045
|
+
if (Array.isArray(v) && v.length === 0) continue;
|
|
2046
|
+
out[k] = v;
|
|
2047
|
+
}
|
|
2048
|
+
return out;
|
|
2049
|
+
}
|
|
2050
|
+
function invokeAction(action, record, formValues) {
|
|
2051
|
+
const recordId = record?.id;
|
|
2052
|
+
const url = action.url ? action.url.replace("{id}", String(recordId ?? "")) : `/arqel-dev/actions/${action.name}`;
|
|
2053
|
+
const method = action.method.toLowerCase();
|
|
2054
|
+
const data = { ...formValues ?? {} };
|
|
2055
|
+
if (recordId !== void 0) data["record_id"] = recordId;
|
|
2056
|
+
router.visit(url, {
|
|
2057
|
+
method,
|
|
2058
|
+
data,
|
|
2059
|
+
preserveScroll: true
|
|
2060
|
+
});
|
|
2061
|
+
}
|
|
2062
|
+
function buildQuery(params) {
|
|
2063
|
+
const data = {};
|
|
2064
|
+
const filters = pruneEmptyFilters(params.filters);
|
|
2065
|
+
if (Object.keys(filters).length > 0) data["filter"] = filters;
|
|
2066
|
+
if (params.search !== void 0) data["search"] = params.search;
|
|
2067
|
+
if (params.sort?.column) {
|
|
2068
|
+
data["sort"] = params.sort.column;
|
|
2069
|
+
if (params.sort.direction) data["direction"] = params.sort.direction;
|
|
2070
|
+
}
|
|
2071
|
+
if (params.page && params.page > 1) data["page"] = params.page;
|
|
2072
|
+
if (params.perPage) data["per_page"] = params.perPage;
|
|
2073
|
+
return data;
|
|
2074
|
+
}
|
|
1569
2075
|
function ArqelIndexPage() {
|
|
1570
2076
|
const page = usePage();
|
|
1571
2077
|
const props = page.props;
|
|
1572
|
-
|
|
2078
|
+
const initialFilters = useMemo(() => {
|
|
2079
|
+
if (typeof window === "undefined") return {};
|
|
2080
|
+
const sp = new URLSearchParams(window.location.search);
|
|
2081
|
+
const fromUrl = {};
|
|
2082
|
+
for (const [key, value] of sp.entries()) {
|
|
2083
|
+
const m = key.match(/^filter\[([^\]]+)\]$/);
|
|
2084
|
+
if (m?.[1]) fromUrl[m[1]] = value;
|
|
2085
|
+
}
|
|
2086
|
+
return fromUrl;
|
|
2087
|
+
}, []);
|
|
2088
|
+
const [filterValues, setFilterValues] = useState(initialFilters);
|
|
2089
|
+
const [searchValue, setSearchValue] = useState(props.search ?? "");
|
|
2090
|
+
const [perPage, setPerPage] = useState(props.pagination?.perPage);
|
|
2091
|
+
const [selectedIds, setSelectedIds] = useState([]);
|
|
2092
|
+
const searchDebounceRef = useRef(null);
|
|
2093
|
+
const visit = (data) => {
|
|
2094
|
+
const url = typeof window !== "undefined" ? window.location.pathname : page.url.split("?")[0];
|
|
2095
|
+
router.get(url ?? "/", data, {
|
|
2096
|
+
preserveState: true,
|
|
2097
|
+
preserveScroll: true,
|
|
2098
|
+
replace: true
|
|
2099
|
+
});
|
|
2100
|
+
};
|
|
2101
|
+
const currentSort = props.sort ?? null;
|
|
2102
|
+
const handleFilterChange = (name, value) => {
|
|
2103
|
+
const nextFilters = { ...filterValues };
|
|
2104
|
+
if (value === void 0 || value === null || value === "") {
|
|
2105
|
+
delete nextFilters[name];
|
|
2106
|
+
} else {
|
|
2107
|
+
nextFilters[name] = value;
|
|
2108
|
+
}
|
|
2109
|
+
setFilterValues(nextFilters);
|
|
2110
|
+
visit(buildQuery({ filters: nextFilters, search: searchValue, sort: currentSort, perPage }));
|
|
2111
|
+
};
|
|
2112
|
+
const handleClearFilters = () => {
|
|
2113
|
+
setFilterValues({});
|
|
2114
|
+
visit(buildQuery({ filters: {}, search: searchValue, sort: currentSort, perPage }));
|
|
2115
|
+
};
|
|
2116
|
+
const handleSearchChange = (value) => {
|
|
2117
|
+
setSearchValue(value);
|
|
2118
|
+
if (searchDebounceRef.current) clearTimeout(searchDebounceRef.current);
|
|
2119
|
+
searchDebounceRef.current = setTimeout(() => {
|
|
2120
|
+
visit(buildQuery({ filters: filterValues, search: value, sort: currentSort, perPage }));
|
|
2121
|
+
}, 300);
|
|
2122
|
+
};
|
|
2123
|
+
useEffect(
|
|
2124
|
+
() => () => {
|
|
2125
|
+
if (searchDebounceRef.current) clearTimeout(searchDebounceRef.current);
|
|
2126
|
+
},
|
|
2127
|
+
[]
|
|
2128
|
+
);
|
|
2129
|
+
const handleSortChange = (column, direction) => {
|
|
2130
|
+
visit(
|
|
2131
|
+
buildQuery({
|
|
2132
|
+
filters: filterValues,
|
|
2133
|
+
search: searchValue,
|
|
2134
|
+
sort: { column, direction },
|
|
2135
|
+
perPage
|
|
2136
|
+
})
|
|
2137
|
+
);
|
|
2138
|
+
};
|
|
2139
|
+
const handlePageChange = (pageNum) => {
|
|
2140
|
+
visit(
|
|
2141
|
+
buildQuery({
|
|
2142
|
+
filters: filterValues,
|
|
2143
|
+
search: searchValue,
|
|
2144
|
+
sort: currentSort,
|
|
2145
|
+
page: pageNum,
|
|
2146
|
+
perPage
|
|
2147
|
+
})
|
|
2148
|
+
);
|
|
2149
|
+
};
|
|
2150
|
+
const handlePerPageChange = (newPerPage) => {
|
|
2151
|
+
setPerPage(newPerPage);
|
|
2152
|
+
visit(
|
|
2153
|
+
buildQuery({
|
|
2154
|
+
filters: filterValues,
|
|
2155
|
+
search: searchValue,
|
|
2156
|
+
sort: currentSort,
|
|
2157
|
+
perPage: newPerPage
|
|
2158
|
+
})
|
|
2159
|
+
);
|
|
2160
|
+
};
|
|
2161
|
+
const bulkActionsList = props.actions?.bulk ?? [];
|
|
2162
|
+
const bulkActions = bulkActionsList.length > 0 && selectedIds.length > 0 ? /* @__PURE__ */ jsx("div", { className: "flex items-center gap-2", children: bulkActionsList.map((action) => /* @__PURE__ */ jsx(
|
|
2163
|
+
"button",
|
|
2164
|
+
{
|
|
2165
|
+
type: "button",
|
|
2166
|
+
onClick: () => {
|
|
2167
|
+
const url = action.url ?? `/arqel-dev/actions/${action.name}`;
|
|
2168
|
+
const method = action.method.toLowerCase();
|
|
2169
|
+
router.visit(url, {
|
|
2170
|
+
method,
|
|
2171
|
+
data: { record_ids: selectedIds },
|
|
2172
|
+
preserveScroll: true,
|
|
2173
|
+
onSuccess: () => setSelectedIds([])
|
|
2174
|
+
});
|
|
2175
|
+
},
|
|
2176
|
+
className: "inline-flex h-8 items-center gap-1 rounded-sm border border-destructive/40 bg-destructive/10 px-3 text-xs font-medium text-destructive hover:bg-destructive/20",
|
|
2177
|
+
children: action.label
|
|
2178
|
+
},
|
|
2179
|
+
action.name
|
|
2180
|
+
)) }) : void 0;
|
|
2181
|
+
const rowActionsList = props.actions?.row ?? [];
|
|
2182
|
+
const rowActions = rowActionsList.length > 0 ? (record) => /* @__PURE__ */ jsx(
|
|
2183
|
+
ActionMenu,
|
|
2184
|
+
{
|
|
2185
|
+
actions: rowActionsList,
|
|
2186
|
+
onInvoke: (action, formValues) => invokeAction(action, record, formValues)
|
|
2187
|
+
}
|
|
2188
|
+
) : void 0;
|
|
2189
|
+
return /* @__PURE__ */ jsx(
|
|
2190
|
+
ResourceIndex,
|
|
2191
|
+
{
|
|
2192
|
+
...props,
|
|
2193
|
+
filterValues,
|
|
2194
|
+
onFilterChange: handleFilterChange,
|
|
2195
|
+
onClearFilters: handleClearFilters,
|
|
2196
|
+
onSearchChange: handleSearchChange,
|
|
2197
|
+
onSortChange: handleSortChange,
|
|
2198
|
+
onPageChange: handlePageChange,
|
|
2199
|
+
onPerPageChange: handlePerPageChange,
|
|
2200
|
+
search: searchValue,
|
|
2201
|
+
selectedIds,
|
|
2202
|
+
onSelectionChange: (ids) => setSelectedIds(ids),
|
|
2203
|
+
...rowActions ? { rowActions } : {},
|
|
2204
|
+
...bulkActions ? { bulkActions } : {}
|
|
2205
|
+
}
|
|
2206
|
+
);
|
|
1573
2207
|
}
|
|
1574
2208
|
var init_ArqelIndexPage = __esm({
|
|
1575
2209
|
"src/pages/ArqelIndexPage.tsx"() {
|
|
2210
|
+
init_ActionMenu();
|
|
1576
2211
|
init_ResourceIndex();
|
|
1577
2212
|
}
|
|
1578
2213
|
});
|