@zaamx/netme-pos 0.0.11 → 0.0.12

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.
@@ -6,6 +6,7 @@ const ui = require("@medusajs/ui");
6
6
  const react = require("react");
7
7
  const reactQuery = require("@tanstack/react-query");
8
8
  const Medusa = require("@medusajs/js-sdk");
9
+ const reactRouterDom = require("react-router-dom");
9
10
  const _interopDefault = (e) => e && e.__esModule ? e : { default: e };
10
11
  const Medusa__default = /* @__PURE__ */ _interopDefault(Medusa);
11
12
  const sdk = new Medusa__default.default({
@@ -166,6 +167,7 @@ const DeleteOperatorModal = ({
166
167
  const OperatorActions = ({ operator }) => {
167
168
  const [editOpen, setEditOpen] = react.useState(false);
168
169
  const [deleteOpen, setDeleteOpen] = react.useState(false);
170
+ const navigate = reactRouterDom.useNavigate();
169
171
  return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
170
172
  /* @__PURE__ */ jsxRuntime.jsxs(ui.DropdownMenu, { children: [
171
173
  /* @__PURE__ */ jsxRuntime.jsx(ui.DropdownMenu.Trigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(ui.IconButton, { size: "small", variant: "transparent", children: /* @__PURE__ */ jsxRuntime.jsx(icons.EllipsisHorizontal, {}) }) }),
@@ -174,6 +176,17 @@ const OperatorActions = ({ operator }) => {
174
176
  /* @__PURE__ */ jsxRuntime.jsx(icons.PencilSquare, { className: "text-ui-fg-subtle" }),
175
177
  "Edit"
176
178
  ] }),
179
+ /* @__PURE__ */ jsxRuntime.jsxs(
180
+ ui.DropdownMenu.Item,
181
+ {
182
+ onClick: () => navigate(`/pos-permissions/${operator.id}`),
183
+ className: "gap-x-2",
184
+ children: [
185
+ /* @__PURE__ */ jsxRuntime.jsx(icons.ShieldCheck, { className: "text-ui-fg-subtle" }),
186
+ "Permisos POS"
187
+ ]
188
+ }
189
+ ),
177
190
  /* @__PURE__ */ jsxRuntime.jsx(ui.DropdownMenu.Separator, {}),
178
191
  /* @__PURE__ */ jsxRuntime.jsxs(ui.DropdownMenu.Item, { onClick: () => setDeleteOpen(true), className: "gap-x-2", children: [
179
192
  /* @__PURE__ */ jsxRuntime.jsx(icons.Trash, { className: "text-ui-fg-subtle" }),
@@ -311,24 +324,24 @@ const CreateOperatorModal = () => {
311
324
  ] }) }) })
312
325
  ] });
313
326
  };
314
- const columnHelper$1 = ui.createDataTableColumnHelper();
315
- const useColumns$1 = () => {
327
+ const columnHelper$2 = ui.createDataTableColumnHelper();
328
+ const useColumns$2 = () => {
316
329
  return react.useMemo(
317
330
  () => [
318
- columnHelper$1.accessor("first_name", {
331
+ columnHelper$2.accessor("first_name", {
319
332
  header: "First Name"
320
333
  }),
321
- columnHelper$1.accessor("last_name", {
334
+ columnHelper$2.accessor("last_name", {
322
335
  header: "Last Name"
323
336
  }),
324
- columnHelper$1.accessor("email", {
337
+ columnHelper$2.accessor("email", {
325
338
  header: "Email"
326
339
  }),
327
- columnHelper$1.accessor("created_at", {
340
+ columnHelper$2.accessor("created_at", {
328
341
  header: "Date Added",
329
342
  cell: ({ getValue }) => new Date(getValue()).toLocaleDateString()
330
343
  }),
331
- columnHelper$1.display({
344
+ columnHelper$2.display({
332
345
  id: "actions",
333
346
  cell: ({ row }) => /* @__PURE__ */ jsxRuntime.jsx(OperatorActions, { operator: row.original })
334
347
  })
@@ -355,7 +368,7 @@ const OperatorTable = () => {
355
368
  queryKey: ["operators", limit, offset, searchValue],
356
369
  placeholderData: (previousData) => previousData
357
370
  });
358
- const columns = useColumns$1();
371
+ const columns = useColumns$2();
359
372
  const table = ui.useDataTable({
360
373
  data: (data == null ? void 0 : data.operators) || [],
361
374
  columns,
@@ -386,7 +399,7 @@ const OperatorsPage = () => {
386
399
  /* @__PURE__ */ jsxRuntime.jsx(OperatorTable, {})
387
400
  ] });
388
401
  };
389
- const config$1 = adminSdk.defineRouteConfig({
402
+ const config$2 = adminSdk.defineRouteConfig({
390
403
  label: "Operators",
391
404
  icon: icons.Users
392
405
  });
@@ -722,7 +735,7 @@ const PaymentOptionActions = ({
722
735
  )
723
736
  ] });
724
737
  };
725
- const columnHelper = ui.createDataTableColumnHelper();
738
+ const columnHelper$1 = ui.createDataTableColumnHelper();
726
739
  const formatAmount = (value) => {
727
740
  const n = Number(value ?? 0);
728
741
  return n.toLocaleString("es-MX", {
@@ -753,10 +766,10 @@ const statusColor = (status) => {
753
766
  return "grey";
754
767
  }
755
768
  };
756
- const useColumns = () => {
769
+ const useColumns$1 = () => {
757
770
  return react.useMemo(
758
771
  () => [
759
- columnHelper.accessor("order_display_id", {
772
+ columnHelper$1.accessor("order_display_id", {
760
773
  header: "Orden",
761
774
  cell: ({ row }) => {
762
775
  const displayId = row.original.order_display_id;
@@ -781,19 +794,19 @@ const useColumns = () => {
781
794
  );
782
795
  }
783
796
  }),
784
- columnHelper.accessor("reference_code", {
797
+ columnHelper$1.accessor("reference_code", {
785
798
  header: "Folio / Referencia",
786
799
  cell: ({ getValue }) => getValue() || "—"
787
800
  }),
788
- columnHelper.accessor("payment_method", {
801
+ columnHelper$1.accessor("payment_method", {
789
802
  header: "Método",
790
803
  cell: ({ getValue }) => getValue() || "—"
791
804
  }),
792
- columnHelper.accessor("amount", {
805
+ columnHelper$1.accessor("amount", {
793
806
  header: "Monto",
794
807
  cell: ({ getValue }) => formatAmount(getValue())
795
808
  }),
796
- columnHelper.accessor("status", {
809
+ columnHelper$1.accessor("status", {
797
810
  header: "Estatus",
798
811
  cell: ({ getValue }) => {
799
812
  const status = getValue();
@@ -803,15 +816,15 @@ const useColumns = () => {
803
816
  return /* @__PURE__ */ jsxRuntime.jsx(ui.Badge, { size: "2xsmall", color: statusColor(status), children: status });
804
817
  }
805
818
  }),
806
- columnHelper.accessor("concept", {
819
+ columnHelper$1.accessor("concept", {
807
820
  header: "Concepto",
808
821
  cell: ({ getValue }) => getValue() || "—"
809
822
  }),
810
- columnHelper.accessor("created_at", {
823
+ columnHelper$1.accessor("created_at", {
811
824
  header: "Fecha",
812
825
  cell: ({ getValue }) => new Date(getValue()).toLocaleString()
813
826
  }),
814
- columnHelper.display({
827
+ columnHelper$1.display({
815
828
  id: "actions",
816
829
  cell: ({ row }) => /* @__PURE__ */ jsxRuntime.jsx(PaymentOptionActions, { paymentOption: row.original })
817
830
  })
@@ -838,7 +851,7 @@ const PaymentOptionTable = () => {
838
851
  queryKey: ["payment-options", limit, offset, searchValue],
839
852
  placeholderData: (previousData) => previousData
840
853
  });
841
- const columns = useColumns();
854
+ const columns = useColumns$1();
842
855
  const table = ui.useDataTable({
843
856
  data: (data == null ? void 0 : data.payment_options) || [],
844
857
  columns,
@@ -875,10 +888,811 @@ const PaymentOptionsPage = () => {
875
888
  /* @__PURE__ */ jsxRuntime.jsx(PaymentOptionTable, {})
876
889
  ] });
877
890
  };
878
- const config = adminSdk.defineRouteConfig({
891
+ const config$1 = adminSdk.defineRouteConfig({
879
892
  label: "Notas de pago",
880
893
  icon: icons.CurrencyDollar
881
894
  });
895
+ const POS_ROLES = [
896
+ { value: "cashier", label: "Cajero" },
897
+ { value: "supervisor", label: "Supervisor" },
898
+ { value: "manager", label: "Gerente" }
899
+ ];
900
+ const CAPABILITY_GROUPS = [
901
+ {
902
+ title: "Venta / carrito",
903
+ items: [
904
+ { key: "cancel_sale", label: "Cancelar venta", description: "Botón que borra el draft" },
905
+ { key: "pause_sale", label: "Pausar venta", description: "Guardar para después" },
906
+ { key: "remove_item", label: "Eliminar ítems del carrito" },
907
+ { key: "edit_quantity", label: "Cambiar cantidades" },
908
+ { key: "apply_promotion", label: "Aplicar / quitar cupón" },
909
+ { key: "apply_manual_discount", label: "Descuento manual (override de precio)", description: "Acción sensible" }
910
+ ]
911
+ },
912
+ {
913
+ title: "Configuración de la sesión",
914
+ items: [
915
+ { key: "change_region", label: "Cambiar región" },
916
+ { key: "change_sales_channel", label: "Cambiar canal de venta" },
917
+ { key: "change_location", label: "Cambiar ubicación de surtido" }
918
+ ]
919
+ },
920
+ {
921
+ title: "Pago",
922
+ items: [
923
+ { key: "pay_cash", label: "Cobrar en caja (efectivo)" },
924
+ { key: "pay_card", label: "Cobrar con tarjeta (Stripe)" },
925
+ { key: "use_store_credit", label: "Pagar con Monedero (store credit)" },
926
+ { key: "capture_payment", label: "Capturar pago" },
927
+ { key: "capture_without_docs", label: "Capturar sin documentación", description: "Acción sensible" }
928
+ ]
929
+ },
930
+ {
931
+ title: "Envío / dirección",
932
+ items: [
933
+ { key: "edit_shipping", label: "Elegir / cambiar método de envío" },
934
+ { key: "edit_customer_address", label: "Editar dirección de envío del cliente" }
935
+ ]
936
+ },
937
+ {
938
+ title: "Ventas pendientes",
939
+ items: [
940
+ { key: "view_pending_drafts", label: "Ver ventas pendientes (/pos-drafts)" },
941
+ { key: "recover_draft", label: "Recuperar / cargar un draft" }
942
+ ]
943
+ }
944
+ ];
945
+ const CAPABILITY_KEYS = CAPABILITY_GROUPS.flatMap(
946
+ (g) => g.items.map((i) => i.key)
947
+ );
948
+ const allTrue = () => CAPABILITY_KEYS.reduce(
949
+ (acc, key) => ({ ...acc, [key]: true }),
950
+ {}
951
+ );
952
+ const ROLE_PRESETS = {
953
+ cashier: {
954
+ ...allTrue(),
955
+ cancel_sale: false,
956
+ apply_manual_discount: false,
957
+ change_region: false,
958
+ change_sales_channel: false,
959
+ capture_payment: false,
960
+ capture_without_docs: false,
961
+ view_pending_drafts: false,
962
+ recover_draft: false
963
+ },
964
+ supervisor: {
965
+ ...allTrue(),
966
+ capture_without_docs: false
967
+ },
968
+ manager: {
969
+ ...allTrue()
970
+ }
971
+ };
972
+ const presetForRole = (role) => {
973
+ if (role && ROLE_PRESETS[role]) {
974
+ return ROLE_PRESETS[role];
975
+ }
976
+ return allTrue();
977
+ };
978
+ const resolveCapabilities = (role, stored) => {
979
+ const preset = presetForRole(role);
980
+ return CAPABILITY_KEYS.reduce((acc, key) => {
981
+ const storedValue = stored && typeof stored[key] === "boolean" ? stored[key] : void 0;
982
+ acc[key] = storedValue ?? preset[key] ?? true;
983
+ return acc;
984
+ }, {});
985
+ };
986
+ const columnHelper = ui.createDataTableColumnHelper();
987
+ const roleLabel = (role) => {
988
+ var _a;
989
+ return ((_a = POS_ROLES.find((r) => r.value === role)) == null ? void 0 : _a.label) || null;
990
+ };
991
+ const scopeSummary = (ids) => Array.isArray(ids) ? `${ids.length} seleccionada${ids.length === 1 ? "" : "s"}` : "Todas";
992
+ const useColumns = () => react.useMemo(
993
+ () => [
994
+ columnHelper.accessor("first_name", {
995
+ header: "Nombre",
996
+ cell: ({ row }) => `${row.original.first_name ?? ""} ${row.original.last_name ?? ""}`.trim() || "—"
997
+ }),
998
+ columnHelper.accessor("email", {
999
+ header: "Email"
1000
+ }),
1001
+ columnHelper.display({
1002
+ id: "role",
1003
+ header: "Rol POS",
1004
+ cell: ({ row }) => {
1005
+ var _a;
1006
+ const label = roleLabel((_a = row.original.metadata) == null ? void 0 : _a.pos_role);
1007
+ return label ? /* @__PURE__ */ jsxRuntime.jsx(ui.Badge, { size: "2xsmall", color: "blue", children: label }) : /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-muted", children: "Sin rol" });
1008
+ }
1009
+ }),
1010
+ columnHelper.display({
1011
+ id: "locations",
1012
+ header: "Ubicaciones",
1013
+ cell: ({ row }) => {
1014
+ var _a;
1015
+ return /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", children: scopeSummary((_a = row.original.metadata) == null ? void 0 : _a.pos_stock_location_ids) });
1016
+ }
1017
+ }),
1018
+ columnHelper.display({
1019
+ id: "actions",
1020
+ cell: ({ row }) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex justify-end", children: /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { asChild: true, size: "small", variant: "secondary", children: /* @__PURE__ */ jsxRuntime.jsx(reactRouterDom.Link, { to: `/pos-permissions/${row.original.id}`, children: "Editar permisos" }) }) })
1021
+ })
1022
+ ],
1023
+ []
1024
+ );
1025
+ const PosPermissionsPage = () => {
1026
+ const [searchValue, setSearchValue] = react.useState("");
1027
+ const [pagination, setPagination] = react.useState({
1028
+ pageIndex: 0,
1029
+ pageSize: 15
1030
+ });
1031
+ const limit = pagination.pageSize;
1032
+ const offset = pagination.pageIndex * limit;
1033
+ const { data, isLoading } = reactQuery.useQuery({
1034
+ queryFn: () => sdk.client.fetch("/admin/operators", {
1035
+ query: {
1036
+ limit,
1037
+ offset,
1038
+ q: searchValue || void 0
1039
+ }
1040
+ }),
1041
+ queryKey: ["operators", "pos-permissions", limit, offset, searchValue],
1042
+ placeholderData: (previousData) => previousData
1043
+ });
1044
+ const columns = useColumns();
1045
+ const table = ui.useDataTable({
1046
+ data: (data == null ? void 0 : data.operators) || [],
1047
+ columns,
1048
+ getRowId: (row) => row.id,
1049
+ rowCount: (data == null ? void 0 : data.count) || 0,
1050
+ isLoading,
1051
+ search: {
1052
+ state: searchValue,
1053
+ onSearchChange: setSearchValue
1054
+ },
1055
+ pagination: {
1056
+ state: pagination,
1057
+ onPaginationChange: setPagination
1058
+ }
1059
+ });
1060
+ return /* @__PURE__ */ jsxRuntime.jsxs(ui.Container, { className: "flex flex-col items-stretch overflow-hidden p-0", children: [
1061
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-between border-b border-ui-border-base px-6 py-4", children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-y-1", children: [
1062
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h2", children: "Permisos POS" }),
1063
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: "Define el alcance (ubicaciones, canales, regiones) y las capacidades de cada operador del Punto de Venta." })
1064
+ ] }) }),
1065
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.DataTable, { instance: table, children: [
1066
+ /* @__PURE__ */ jsxRuntime.jsx(ui.DataTable.Toolbar, { children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex w-full items-center justify-end", children: /* @__PURE__ */ jsxRuntime.jsx(ui.DataTable.Search, { placeholder: "Buscar operador..." }) }) }),
1067
+ /* @__PURE__ */ jsxRuntime.jsx(ui.DataTable.Table, {}),
1068
+ /* @__PURE__ */ jsxRuntime.jsx(ui.DataTable.Pagination, {})
1069
+ ] })
1070
+ ] });
1071
+ };
1072
+ const config = adminSdk.defineRouteConfig({
1073
+ label: "Permisos POS",
1074
+ icon: icons.ShieldCheck
1075
+ });
1076
+ const MultiSelect = ({
1077
+ options,
1078
+ value,
1079
+ onChange,
1080
+ placeholder = "Seleccionar...",
1081
+ searchPlaceholder = "Buscar...",
1082
+ disabled = false
1083
+ }) => {
1084
+ const [open, setOpen] = react.useState(false);
1085
+ const [search, setSearch] = react.useState("");
1086
+ const filtered = react.useMemo(() => {
1087
+ const q = search.trim().toLowerCase();
1088
+ if (!q) {
1089
+ return options;
1090
+ }
1091
+ return options.filter(
1092
+ (o) => o.label.toLowerCase().includes(q) || o.value.toLowerCase().includes(q)
1093
+ );
1094
+ }, [options, search]);
1095
+ const selectedOptions = react.useMemo(
1096
+ () => value.map(
1097
+ (v) => options.find((o) => o.value === v) || { value: v, label: v }
1098
+ ),
1099
+ [value, options]
1100
+ );
1101
+ const toggle = (val) => {
1102
+ if (value.includes(val)) {
1103
+ onChange(value.filter((v) => v !== val));
1104
+ } else {
1105
+ onChange([...value, val]);
1106
+ }
1107
+ };
1108
+ return /* @__PURE__ */ jsxRuntime.jsxs(ui.Popover, { open, onOpenChange: setOpen, children: [
1109
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Popover.Trigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsxs(
1110
+ "button",
1111
+ {
1112
+ type: "button",
1113
+ disabled,
1114
+ className: ui.clx(
1115
+ "flex min-h-8 w-full items-center justify-between gap-2 rounded-md border bg-ui-bg-field px-3 py-1.5 text-left shadow-borders-base transition-colors",
1116
+ "hover:bg-ui-bg-field-hover focus:outline-none focus:shadow-borders-interactive-with-active",
1117
+ disabled && "cursor-not-allowed opacity-60"
1118
+ ),
1119
+ children: [
1120
+ selectedOptions.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-muted", children: placeholder }) : /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-wrap gap-1", children: selectedOptions.map((o) => /* @__PURE__ */ jsxRuntime.jsxs(ui.Badge, { size: "2xsmall", className: "gap-x-1", children: [
1121
+ o.label,
1122
+ /* @__PURE__ */ jsxRuntime.jsx(
1123
+ "span",
1124
+ {
1125
+ role: "button",
1126
+ tabIndex: -1,
1127
+ onClick: (e) => {
1128
+ e.stopPropagation();
1129
+ toggle(o.value);
1130
+ },
1131
+ className: "hover:text-ui-fg-base",
1132
+ children: /* @__PURE__ */ jsxRuntime.jsx(icons.XMark, { className: "h-3 w-3" })
1133
+ }
1134
+ )
1135
+ ] }, o.value)) }),
1136
+ /* @__PURE__ */ jsxRuntime.jsx(icons.ChevronUpDown, { className: "shrink-0 text-ui-fg-muted" })
1137
+ ]
1138
+ }
1139
+ ) }),
1140
+ /* @__PURE__ */ jsxRuntime.jsxs(
1141
+ ui.Popover.Content,
1142
+ {
1143
+ align: "start",
1144
+ sideOffset: 4,
1145
+ className: "z-50 w-[var(--radix-popover-trigger-width)] min-w-[280px] p-0",
1146
+ children: [
1147
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "border-b border-ui-border-base p-2", children: /* @__PURE__ */ jsxRuntime.jsx(
1148
+ ui.Input,
1149
+ {
1150
+ autoFocus: true,
1151
+ placeholder: searchPlaceholder,
1152
+ value: search,
1153
+ onChange: (e) => setSearch(e.target.value)
1154
+ }
1155
+ ) }),
1156
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "max-h-64 overflow-auto p-1", children: filtered.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx(
1157
+ ui.Text,
1158
+ {
1159
+ size: "small",
1160
+ className: "px-2 py-3 text-center text-ui-fg-muted",
1161
+ children: "Sin resultados"
1162
+ }
1163
+ ) : filtered.map((o) => {
1164
+ const checked = value.includes(o.value);
1165
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1166
+ "button",
1167
+ {
1168
+ type: "button",
1169
+ onClick: () => toggle(o.value),
1170
+ className: "flex w-full items-center gap-x-2 rounded-md px-2 py-1.5 text-left hover:bg-ui-bg-base-hover",
1171
+ children: [
1172
+ /* @__PURE__ */ jsxRuntime.jsx(
1173
+ ui.Checkbox,
1174
+ {
1175
+ checked,
1176
+ className: "pointer-events-none"
1177
+ }
1178
+ ),
1179
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", children: o.label })
1180
+ ]
1181
+ },
1182
+ o.value
1183
+ );
1184
+ }) }),
1185
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between border-t border-ui-border-base px-2 py-1.5", children: [
1186
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Text, { size: "xsmall", className: "text-ui-fg-subtle", children: [
1187
+ value.length,
1188
+ " seleccionado",
1189
+ value.length === 1 ? "" : "s"
1190
+ ] }),
1191
+ value.length > 0 && /* @__PURE__ */ jsxRuntime.jsx(
1192
+ ui.Button,
1193
+ {
1194
+ size: "small",
1195
+ variant: "transparent",
1196
+ onClick: () => onChange([]),
1197
+ children: "Limpiar"
1198
+ }
1199
+ )
1200
+ ] })
1201
+ ]
1202
+ }
1203
+ )
1204
+ ] });
1205
+ };
1206
+ const AllowListField = ({
1207
+ label,
1208
+ description,
1209
+ icon,
1210
+ options,
1211
+ restricted,
1212
+ onRestrictedChange,
1213
+ selected,
1214
+ onSelectedChange,
1215
+ error,
1216
+ searchPlaceholder
1217
+ }) => {
1218
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-y-3 rounded-lg border border-ui-border-base p-4", children: [
1219
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start justify-between gap-x-4", children: [
1220
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-start gap-x-2", children: [
1221
+ icon ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "mt-0.5 text-ui-fg-subtle", children: icon }) : null,
1222
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col", children: [
1223
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { weight: "plus", children: label }),
1224
+ description ? /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: description }) : null
1225
+ ] })
1226
+ ] }),
1227
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex shrink-0 items-center gap-x-2", children: [
1228
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: restricted ? "Restringido" : "Todas" }),
1229
+ /* @__PURE__ */ jsxRuntime.jsx(
1230
+ ui.Switch,
1231
+ {
1232
+ checked: restricted,
1233
+ onCheckedChange: onRestrictedChange
1234
+ }
1235
+ )
1236
+ ] })
1237
+ ] }),
1238
+ restricted ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-y-1", children: [
1239
+ /* @__PURE__ */ jsxRuntime.jsx(
1240
+ MultiSelect,
1241
+ {
1242
+ options,
1243
+ value: selected,
1244
+ onChange: onSelectedChange,
1245
+ searchPlaceholder
1246
+ }
1247
+ ),
1248
+ error ? /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-error", children: error }) : null
1249
+ ] }) : null
1250
+ ] });
1251
+ };
1252
+ const NONE$1 = "__none__";
1253
+ const CapabilitiesSection = ({
1254
+ role,
1255
+ onRoleChange,
1256
+ capabilities,
1257
+ onCapabilitiesChange
1258
+ }) => {
1259
+ const preset = presetForRole(role);
1260
+ const setCapability = (key, nextValue) => {
1261
+ onCapabilitiesChange({ ...capabilities, [key]: nextValue });
1262
+ };
1263
+ const diffCount = Object.keys(preset).filter(
1264
+ (k) => capabilities[k] !== preset[k]
1265
+ ).length;
1266
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-y-4", children: [
1267
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-y-2", children: [
1268
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { weight: "plus", children: "Rol POS" }),
1269
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: "El rol aplica un preset de capacidades. Puedes ajustar capacidades puntuales abajo; se guarda el mapa completo resuelto." }),
1270
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-x-2", children: [
1271
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "max-w-xs flex-1", children: /* @__PURE__ */ jsxRuntime.jsxs(
1272
+ ui.Select,
1273
+ {
1274
+ value: role || NONE$1,
1275
+ onValueChange: (v) => onRoleChange(v === NONE$1 ? "" : v),
1276
+ children: [
1277
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Trigger, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Value, { placeholder: "Sin rol" }) }),
1278
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Select.Content, { children: [
1279
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: NONE$1, children: "Sin rol (todo permitido)" }),
1280
+ POS_ROLES.map((r) => /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: r.value, children: r.label }, r.value))
1281
+ ] })
1282
+ ]
1283
+ }
1284
+ ) }),
1285
+ diffCount > 0 ? /* @__PURE__ */ jsxRuntime.jsxs(
1286
+ ui.Button,
1287
+ {
1288
+ size: "small",
1289
+ variant: "secondary",
1290
+ onClick: () => onCapabilitiesChange({ ...preset }),
1291
+ children: [
1292
+ /* @__PURE__ */ jsxRuntime.jsx(icons.ArrowUturnLeft, {}),
1293
+ "Restablecer al preset"
1294
+ ]
1295
+ }
1296
+ ) : null
1297
+ ] })
1298
+ ] }),
1299
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-x-2", children: [
1300
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { weight: "plus", children: "Capacidades" }),
1301
+ diffCount > 0 ? /* @__PURE__ */ jsxRuntime.jsxs(ui.Badge, { size: "2xsmall", color: "orange", children: [
1302
+ diffCount,
1303
+ " distinta",
1304
+ diffCount === 1 ? "" : "s",
1305
+ " del preset"
1306
+ ] }) : null
1307
+ ] }),
1308
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col gap-y-5", children: CAPABILITY_GROUPS.map((group) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-y-2", children: [
1309
+ /* @__PURE__ */ jsxRuntime.jsx(
1310
+ ui.Text,
1311
+ {
1312
+ size: "small",
1313
+ weight: "plus",
1314
+ className: "text-ui-fg-subtle",
1315
+ children: group.title
1316
+ }
1317
+ ),
1318
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col divide-y divide-ui-border-base rounded-lg border border-ui-border-base", children: group.items.map((item) => {
1319
+ const value = capabilities[item.key] ?? true;
1320
+ const differs = value !== (preset[item.key] ?? true);
1321
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1322
+ "div",
1323
+ {
1324
+ className: "flex items-center justify-between gap-x-4 px-4 py-3",
1325
+ children: [
1326
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col", children: [
1327
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-x-2", children: [
1328
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", weight: "plus", children: item.label }),
1329
+ differs ? /* @__PURE__ */ jsxRuntime.jsx(ui.Badge, { size: "2xsmall", color: "orange", children: "≠ preset" }) : null
1330
+ ] }),
1331
+ item.description ? /* @__PURE__ */ jsxRuntime.jsx(
1332
+ ui.Text,
1333
+ {
1334
+ size: "small",
1335
+ className: "text-ui-fg-subtle",
1336
+ children: item.description
1337
+ }
1338
+ ) : null
1339
+ ] }),
1340
+ /* @__PURE__ */ jsxRuntime.jsx(
1341
+ ui.Switch,
1342
+ {
1343
+ checked: value,
1344
+ onCheckedChange: (checked) => setCapability(item.key, checked)
1345
+ }
1346
+ )
1347
+ ]
1348
+ },
1349
+ item.key
1350
+ );
1351
+ }) })
1352
+ ] }, group.title)) })
1353
+ ] });
1354
+ };
1355
+ const NONE = "__none__";
1356
+ const buildInitialState = (metadata) => {
1357
+ const role = metadata.pos_role ?? "";
1358
+ return {
1359
+ stockRestricted: Array.isArray(metadata.pos_stock_location_ids),
1360
+ stockIds: metadata.pos_stock_location_ids ?? [],
1361
+ channelRestricted: Array.isArray(metadata.pos_sales_channel_ids),
1362
+ channelIds: metadata.pos_sales_channel_ids ?? [],
1363
+ regionRestricted: Array.isArray(metadata.pos_region_ids),
1364
+ regionIds: metadata.pos_region_ids ?? [],
1365
+ shippingRestricted: Array.isArray(metadata.pos_shipping_option_ids),
1366
+ shippingIds: metadata.pos_shipping_option_ids ?? [],
1367
+ defaultRegionId: metadata.pos_default_region_id ?? "",
1368
+ defaultChannelId: metadata.pos_default_sales_channel_id ?? "",
1369
+ defaultStockId: metadata.pos_default_stock_location_id ?? "",
1370
+ role,
1371
+ // Full resolved map (folds any stored partial caps onto the role preset).
1372
+ capabilities: resolveCapabilities(role, metadata.pos_capabilities)
1373
+ };
1374
+ };
1375
+ const toOptions = (items, decorate) => (items ?? []).map((i) => ({
1376
+ value: i.id,
1377
+ label: decorate ? decorate(i) : i.name || i.id
1378
+ }));
1379
+ const DefaultSelect = ({
1380
+ label,
1381
+ value,
1382
+ onChange,
1383
+ options: opts,
1384
+ disabledHint
1385
+ }) => /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-y-2", children: [
1386
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Label, { weight: "plus", children: label }),
1387
+ /* @__PURE__ */ jsxRuntime.jsxs(
1388
+ ui.Select,
1389
+ {
1390
+ value: value || NONE,
1391
+ onValueChange: (v) => onChange(v === NONE ? "" : v),
1392
+ children: [
1393
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Trigger, { children: /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Value, { placeholder: "Sin valor por defecto" }) }),
1394
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Select.Content, { children: [
1395
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: NONE, children: "Sin valor por defecto" }),
1396
+ opts.map((o) => /* @__PURE__ */ jsxRuntime.jsx(ui.Select.Item, { value: o.value, children: o.label }, o.value))
1397
+ ] })
1398
+ ]
1399
+ }
1400
+ ),
1401
+ opts.length === 0 && disabledHint ? /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: disabledHint }) : null
1402
+ ] });
1403
+ const PosPermissionEditorPage = () => {
1404
+ const { id } = reactRouterDom.useParams();
1405
+ const navigate = reactRouterDom.useNavigate();
1406
+ const queryClient = reactQuery.useQueryClient();
1407
+ const { data: operatorData, isLoading: operatorLoading } = reactQuery.useQuery({
1408
+ queryFn: () => sdk.client.fetch(`/admin/pos-permissions/${id}`),
1409
+ queryKey: ["pos-permissions-operator", id],
1410
+ enabled: !!id
1411
+ });
1412
+ const { data: options, isLoading: optionsLoading } = reactQuery.useQuery({
1413
+ queryFn: () => sdk.client.fetch(`/admin/pos-permissions/options`),
1414
+ queryKey: ["pos-permissions-options"]
1415
+ });
1416
+ const [form, setForm] = react.useState(null);
1417
+ const initializedFor = react.useRef(null);
1418
+ react.useEffect(() => {
1419
+ const operator2 = operatorData == null ? void 0 : operatorData.operator;
1420
+ if (operator2 && initializedFor.current !== operator2.id) {
1421
+ initializedFor.current = operator2.id;
1422
+ setForm(buildInitialState(operator2.metadata ?? {}));
1423
+ }
1424
+ }, [operatorData]);
1425
+ const stockOptions = react.useMemo(
1426
+ () => toOptions(options == null ? void 0 : options.stock_locations),
1427
+ [options]
1428
+ );
1429
+ const channelOptions = react.useMemo(
1430
+ () => toOptions(options == null ? void 0 : options.sales_channels),
1431
+ [options]
1432
+ );
1433
+ const regionOptions = react.useMemo(
1434
+ () => toOptions(
1435
+ options == null ? void 0 : options.regions,
1436
+ (r) => r.currency_code ? `${r.name} (${String(r.currency_code).toUpperCase()})` : r.name
1437
+ ),
1438
+ [options]
1439
+ );
1440
+ const shippingOptions = react.useMemo(
1441
+ () => toOptions(options == null ? void 0 : options.shipping_options),
1442
+ [options]
1443
+ );
1444
+ const saveMutation = reactQuery.useMutation({
1445
+ mutationFn: (payload) => sdk.client.fetch(`/admin/pos-permissions/${id}`, {
1446
+ method: "POST",
1447
+ body: payload
1448
+ }),
1449
+ onSuccess: () => {
1450
+ queryClient.invalidateQueries({ queryKey: ["operators"] });
1451
+ queryClient.invalidateQueries({
1452
+ queryKey: ["pos-permissions-operator", id]
1453
+ });
1454
+ ui.toast.success("Permisos del operador actualizados");
1455
+ },
1456
+ onError: (error) => {
1457
+ ui.toast.error(error.message || "No se pudieron guardar los permisos");
1458
+ }
1459
+ });
1460
+ if (operatorLoading || optionsLoading || !form) {
1461
+ return /* @__PURE__ */ jsxRuntime.jsx(ui.Container, { className: "flex items-center justify-center py-16", children: /* @__PURE__ */ jsxRuntime.jsx(icons.Spinner, { className: "animate-spin text-ui-fg-subtle" }) });
1462
+ }
1463
+ const operator = operatorData == null ? void 0 : operatorData.operator;
1464
+ const operatorName = `${(operator == null ? void 0 : operator.first_name) ?? ""} ${(operator == null ? void 0 : operator.last_name) ?? ""}`.trim() || (operator == null ? void 0 : operator.email) || id;
1465
+ const patch = (changes) => setForm((prev) => prev ? { ...prev, ...changes } : prev);
1466
+ const setStockIds = (ids) => setForm((prev) => {
1467
+ if (!prev) return prev;
1468
+ const next = { ...prev, stockIds: ids };
1469
+ if (prev.defaultStockId && !ids.includes(prev.defaultStockId)) {
1470
+ next.defaultStockId = "";
1471
+ }
1472
+ return next;
1473
+ });
1474
+ const setStockRestricted = (restricted) => setForm((prev) => {
1475
+ if (!prev) return prev;
1476
+ const next = { ...prev, stockRestricted: restricted };
1477
+ if (restricted && prev.defaultStockId && !prev.stockIds.includes(prev.defaultStockId)) {
1478
+ next.defaultStockId = "";
1479
+ }
1480
+ return next;
1481
+ });
1482
+ const setChannelIds = (ids) => setForm((prev) => {
1483
+ if (!prev) return prev;
1484
+ const next = { ...prev, channelIds: ids };
1485
+ if (prev.defaultChannelId && !ids.includes(prev.defaultChannelId)) {
1486
+ next.defaultChannelId = "";
1487
+ }
1488
+ return next;
1489
+ });
1490
+ const setChannelRestricted = (restricted) => setForm((prev) => {
1491
+ if (!prev) return prev;
1492
+ const next = { ...prev, channelRestricted: restricted };
1493
+ if (restricted && prev.defaultChannelId && !prev.channelIds.includes(prev.defaultChannelId)) {
1494
+ next.defaultChannelId = "";
1495
+ }
1496
+ return next;
1497
+ });
1498
+ const setRegionIds = (ids) => setForm((prev) => {
1499
+ if (!prev) return prev;
1500
+ const next = { ...prev, regionIds: ids };
1501
+ if (prev.defaultRegionId && !ids.includes(prev.defaultRegionId)) {
1502
+ next.defaultRegionId = "";
1503
+ }
1504
+ return next;
1505
+ });
1506
+ const setRegionRestricted = (restricted) => setForm((prev) => {
1507
+ if (!prev) return prev;
1508
+ const next = { ...prev, regionRestricted: restricted };
1509
+ if (restricted && prev.defaultRegionId && !prev.regionIds.includes(prev.defaultRegionId)) {
1510
+ next.defaultRegionId = "";
1511
+ }
1512
+ return next;
1513
+ });
1514
+ const regionDefaultOptions = form.regionRestricted ? regionOptions.filter((o) => form.regionIds.includes(o.value)) : regionOptions;
1515
+ const channelDefaultOptions = form.channelRestricted ? channelOptions.filter((o) => form.channelIds.includes(o.value)) : channelOptions;
1516
+ const stockDefaultOptions = form.stockRestricted ? stockOptions.filter((o) => form.stockIds.includes(o.value)) : stockOptions;
1517
+ const handleSave = () => {
1518
+ if (form.stockRestricted && form.stockIds.length === 0) {
1519
+ ui.toast.error(
1520
+ "Selecciona al menos una ubicación o desactiva la restricción de ubicaciones."
1521
+ );
1522
+ return;
1523
+ }
1524
+ const payload = {
1525
+ pos_stock_location_ids: form.stockRestricted ? form.stockIds : null,
1526
+ pos_sales_channel_ids: form.channelRestricted ? form.channelIds : null,
1527
+ pos_region_ids: form.regionRestricted ? form.regionIds : null,
1528
+ pos_shipping_option_ids: form.shippingRestricted ? form.shippingIds : null,
1529
+ pos_default_region_id: form.defaultRegionId || null,
1530
+ pos_default_sales_channel_id: form.defaultChannelId || null,
1531
+ pos_default_stock_location_id: form.defaultStockId || null,
1532
+ pos_role: form.role || null,
1533
+ // Store the full resolved capability map so the frontend reads each
1534
+ // flag directly (cap ausente ⇒ permitido) without a preset table.
1535
+ pos_capabilities: form.capabilities
1536
+ };
1537
+ saveMutation.mutate(payload);
1538
+ };
1539
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-y-3", children: [
1540
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Container, { className: "flex items-center justify-between p-4", children: [
1541
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-x-3", children: [
1542
+ /* @__PURE__ */ jsxRuntime.jsx(
1543
+ ui.IconButton,
1544
+ {
1545
+ size: "small",
1546
+ variant: "transparent",
1547
+ onClick: () => navigate("/pos-permissions"),
1548
+ children: /* @__PURE__ */ jsxRuntime.jsx(icons.ArrowLeft, {})
1549
+ }
1550
+ ),
1551
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col", children: [
1552
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h2", children: operatorName }),
1553
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: [
1554
+ operator == null ? void 0 : operator.email,
1555
+ " · Permisos del Punto de Venta"
1556
+ ] })
1557
+ ] })
1558
+ ] }),
1559
+ /* @__PURE__ */ jsxRuntime.jsx(
1560
+ ui.Button,
1561
+ {
1562
+ size: "small",
1563
+ onClick: handleSave,
1564
+ isLoading: saveMutation.isPending,
1565
+ children: "Guardar cambios"
1566
+ }
1567
+ )
1568
+ ] }),
1569
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Container, { className: "flex flex-col gap-y-4 p-6", children: [
1570
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-y-1", children: [
1571
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h3", children: "Alcance de catálogo" }),
1572
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: 'Restringe a qué ubicaciones, canales, regiones y envíos puede acceder. "Todas" significa sin restricción.' })
1573
+ ] }),
1574
+ /* @__PURE__ */ jsxRuntime.jsx(
1575
+ AllowListField,
1576
+ {
1577
+ label: "Ubicaciones (sucursales pickup)",
1578
+ description: "Ubicaciones de surtido/recolección que puede usar.",
1579
+ icon: /* @__PURE__ */ jsxRuntime.jsx(icons.Buildings, {}),
1580
+ options: stockOptions,
1581
+ restricted: form.stockRestricted,
1582
+ onRestrictedChange: setStockRestricted,
1583
+ selected: form.stockIds,
1584
+ onSelectedChange: setStockIds,
1585
+ searchPlaceholder: "Buscar ubicación...",
1586
+ error: form.stockRestricted && form.stockIds.length === 0 ? "Selecciona al menos una ubicación." : void 0
1587
+ }
1588
+ ),
1589
+ /* @__PURE__ */ jsxRuntime.jsx(
1590
+ AllowListField,
1591
+ {
1592
+ label: "Canales de venta",
1593
+ icon: /* @__PURE__ */ jsxRuntime.jsx(icons.ChannelsSolid, {}),
1594
+ options: channelOptions,
1595
+ restricted: form.channelRestricted,
1596
+ onRestrictedChange: setChannelRestricted,
1597
+ selected: form.channelIds,
1598
+ onSelectedChange: setChannelIds,
1599
+ searchPlaceholder: "Buscar canal..."
1600
+ }
1601
+ ),
1602
+ /* @__PURE__ */ jsxRuntime.jsx(
1603
+ AllowListField,
1604
+ {
1605
+ label: "Regiones",
1606
+ icon: /* @__PURE__ */ jsxRuntime.jsx(icons.CurrencyDollar, {}),
1607
+ options: regionOptions,
1608
+ restricted: form.regionRestricted,
1609
+ onRestrictedChange: setRegionRestricted,
1610
+ selected: form.regionIds,
1611
+ onSelectedChange: setRegionIds,
1612
+ searchPlaceholder: "Buscar región..."
1613
+ }
1614
+ ),
1615
+ /* @__PURE__ */ jsxRuntime.jsx(
1616
+ AllowListField,
1617
+ {
1618
+ label: "Opciones de envío",
1619
+ icon: /* @__PURE__ */ jsxRuntime.jsx(icons.Tag, {}),
1620
+ options: shippingOptions,
1621
+ restricted: form.shippingRestricted,
1622
+ onRestrictedChange: (r) => patch({ shippingRestricted: r }),
1623
+ selected: form.shippingIds,
1624
+ onSelectedChange: (ids) => patch({ shippingIds: ids }),
1625
+ searchPlaceholder: "Buscar opción de envío..."
1626
+ }
1627
+ )
1628
+ ] }),
1629
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Container, { className: "flex flex-col gap-y-4 p-6", children: [
1630
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-y-1", children: [
1631
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h3", children: "Valores por defecto" }),
1632
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: "Pre-selección al abrir la sesión. Deben pertenecer a su allow-list cuando esté restringida." })
1633
+ ] }),
1634
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-1 gap-4 md:grid-cols-3", children: [
1635
+ /* @__PURE__ */ jsxRuntime.jsx(
1636
+ DefaultSelect,
1637
+ {
1638
+ label: "Región por defecto",
1639
+ value: form.defaultRegionId,
1640
+ onChange: (v) => patch({ defaultRegionId: v }),
1641
+ options: regionDefaultOptions,
1642
+ disabledHint: "Restringe regiones y selecciona alguna para elegir un default."
1643
+ }
1644
+ ),
1645
+ /* @__PURE__ */ jsxRuntime.jsx(
1646
+ DefaultSelect,
1647
+ {
1648
+ label: "Canal por defecto",
1649
+ value: form.defaultChannelId,
1650
+ onChange: (v) => patch({ defaultChannelId: v }),
1651
+ options: channelDefaultOptions,
1652
+ disabledHint: "Restringe canales y selecciona alguno para elegir un default."
1653
+ }
1654
+ ),
1655
+ /* @__PURE__ */ jsxRuntime.jsx(
1656
+ DefaultSelect,
1657
+ {
1658
+ label: "Ubicación por defecto",
1659
+ value: form.defaultStockId,
1660
+ onChange: (v) => patch({ defaultStockId: v }),
1661
+ options: stockDefaultOptions,
1662
+ disabledHint: "Restringe ubicaciones y selecciona alguna para elegir un default."
1663
+ }
1664
+ )
1665
+ ] })
1666
+ ] }),
1667
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Container, { className: "flex flex-col gap-y-4 p-6", children: [
1668
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-y-1", children: [
1669
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Heading, { level: "h3", children: "Rol y capacidades" }),
1670
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Text, { size: "small", className: "text-ui-fg-subtle", children: "El rol define un preset; las capacidades permiten ajustes puntuales. El backend debe re-validar las acciones sensibles." })
1671
+ ] }),
1672
+ /* @__PURE__ */ jsxRuntime.jsx(
1673
+ CapabilitiesSection,
1674
+ {
1675
+ role: form.role,
1676
+ onRoleChange: (role) => patch({ role, capabilities: presetForRole(role) }),
1677
+ capabilities: form.capabilities,
1678
+ onCapabilitiesChange: (capabilities) => patch({ capabilities })
1679
+ }
1680
+ )
1681
+ ] }),
1682
+ /* @__PURE__ */ jsxRuntime.jsxs(ui.Container, { className: "flex items-center justify-end gap-x-2 p-4", children: [
1683
+ /* @__PURE__ */ jsxRuntime.jsx(ui.Button, { asChild: true, size: "small", variant: "secondary", children: /* @__PURE__ */ jsxRuntime.jsx(reactRouterDom.Link, { to: "/pos-permissions", children: "Volver" }) }),
1684
+ /* @__PURE__ */ jsxRuntime.jsx(
1685
+ ui.Button,
1686
+ {
1687
+ size: "small",
1688
+ onClick: handleSave,
1689
+ isLoading: saveMutation.isPending,
1690
+ children: "Guardar cambios"
1691
+ }
1692
+ )
1693
+ ] })
1694
+ ] });
1695
+ };
882
1696
  const i18nTranslations0 = {};
883
1697
  const widgetModule = { widgets: [] };
884
1698
  const routeModule = {
@@ -890,6 +1704,14 @@ const routeModule = {
890
1704
  {
891
1705
  Component: PaymentOptionsPage,
892
1706
  path: "/payment-options"
1707
+ },
1708
+ {
1709
+ Component: PosPermissionsPage,
1710
+ path: "/pos-permissions"
1711
+ },
1712
+ {
1713
+ Component: PosPermissionEditorPage,
1714
+ path: "/pos-permissions/:id"
893
1715
  }
894
1716
  ]
895
1717
  };
@@ -898,6 +1720,14 @@ const menuItemModule = {
898
1720
  {
899
1721
  label: config$1.label,
900
1722
  icon: config$1.icon,
1723
+ path: "/payment-options",
1724
+ nested: void 0,
1725
+ rank: void 0,
1726
+ translationNs: void 0
1727
+ },
1728
+ {
1729
+ label: config$2.label,
1730
+ icon: config$2.icon,
901
1731
  path: "/operators",
902
1732
  nested: void 0,
903
1733
  rank: void 0,
@@ -906,7 +1736,7 @@ const menuItemModule = {
906
1736
  {
907
1737
  label: config.label,
908
1738
  icon: config.icon,
909
- path: "/payment-options",
1739
+ path: "/pos-permissions",
910
1740
  nested: void 0,
911
1741
  rank: void 0,
912
1742
  translationNs: void 0