dirk-cfx-react 1.1.91 → 1.1.93

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.
@@ -1058,7 +1058,8 @@ type ThemeOverrideSectionProps = {
1058
1058
  declare function ThemeOverrideSection({ schemaKey, title, }: ThemeOverrideSectionProps): react_jsx_runtime.JSX.Element;
1059
1059
 
1060
1060
  type AccessOverrideValue = {
1061
- /** Job / gang / ACE group names allowed to access this resource. */
1061
+ /** ACE groups / permissions allowed to access this resource. Checked
1062
+ * server-side via IsPlayerAceAllowed (e.g. group.admin). NOT jobs/gangs. */
1062
1063
  groups: string[];
1063
1064
  /** Persistent player ids (citizenid on qb/qbx, license on esx — what
1064
1065
  * PlayerSelect yields). dirk_lib also matches raw FiveM identifiers. */
@@ -1080,18 +1081,13 @@ type AccessOverrideSectionProps = {
1080
1081
  * explanation with a fallback.
1081
1082
  */
1082
1083
  description?: string;
1083
- /**
1084
- * Restrict the group picker to "job" or "gang" only. Omit to allow any
1085
- * group (jobs + gangs grouped in the dropdown). Forwarded to GroupName.
1086
- */
1087
- groupType?: "job" | "gang";
1088
1084
  /**
1089
1085
  * When true, the player picker searches offline players (DB-backed) too.
1090
1086
  * Defaults to true so admins can grant access to players who aren't online.
1091
1087
  */
1092
1088
  includeOffline?: boolean;
1093
1089
  };
1094
- declare function AccessOverrideSection({ schemaKey, title, description, groupType, includeOffline, }: AccessOverrideSectionProps): react_jsx_runtime.JSX.Element;
1090
+ declare function AccessOverrideSection({ schemaKey, title, description, includeOffline, }: AccessOverrideSectionProps): react_jsx_runtime.JSX.Element;
1095
1091
 
1096
1092
  type AnimPostFxSelectProps = Omit<SelectProps, "data" | "value" | "onChange"> & {
1097
1093
  /** Current effect name. Empty string = no effect selected. */
@@ -1058,7 +1058,8 @@ type ThemeOverrideSectionProps = {
1058
1058
  declare function ThemeOverrideSection({ schemaKey, title, }: ThemeOverrideSectionProps): react_jsx_runtime.JSX.Element;
1059
1059
 
1060
1060
  type AccessOverrideValue = {
1061
- /** Job / gang / ACE group names allowed to access this resource. */
1061
+ /** ACE groups / permissions allowed to access this resource. Checked
1062
+ * server-side via IsPlayerAceAllowed (e.g. group.admin). NOT jobs/gangs. */
1062
1063
  groups: string[];
1063
1064
  /** Persistent player ids (citizenid on qb/qbx, license on esx — what
1064
1065
  * PlayerSelect yields). dirk_lib also matches raw FiveM identifiers. */
@@ -1080,18 +1081,13 @@ type AccessOverrideSectionProps = {
1080
1081
  * explanation with a fallback.
1081
1082
  */
1082
1083
  description?: string;
1083
- /**
1084
- * Restrict the group picker to "job" or "gang" only. Omit to allow any
1085
- * group (jobs + gangs grouped in the dropdown). Forwarded to GroupName.
1086
- */
1087
- groupType?: "job" | "gang";
1088
1084
  /**
1089
1085
  * When true, the player picker searches offline players (DB-backed) too.
1090
1086
  * Defaults to true so admins can grant access to players who aren't online.
1091
1087
  */
1092
1088
  includeOffline?: boolean;
1093
1089
  };
1094
- declare function AccessOverrideSection({ schemaKey, title, description, groupType, includeOffline, }: AccessOverrideSectionProps): react_jsx_runtime.JSX.Element;
1090
+ declare function AccessOverrideSection({ schemaKey, title, description, includeOffline, }: AccessOverrideSectionProps): react_jsx_runtime.JSX.Element;
1095
1091
 
1096
1092
  type AnimPostFxSelectProps = Omit<SelectProps, "data" | "value" | "onChange"> & {
1097
1093
  /** Current effect name. Empty string = no effect selected. */
@@ -5,7 +5,7 @@ import { create, useStore, createStore } from 'zustand';
5
5
  import { z } from 'zod';
6
6
  import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
7
7
  import { motion, AnimatePresence, useMotionValue } from 'framer-motion';
8
- import { Info, X, AlertTriangle, Trash2, RefreshCw, ChevronDown, Check, Copy, MapPin, Crosshair, EyeOff, Eye, RotateCcw, FlaskConical, ChevronUp, Palette, ShieldCheck, Users, UserRound, Plus, DoorOpen, Minus, ArrowLeft, Undo2, Redo2, Save, History, XCircle, Code2, Search, Filter, User } from 'lucide-react';
8
+ import { Info, X, AlertTriangle, Trash2, RefreshCw, ChevronDown, Check, Copy, MapPin, Crosshair, EyeOff, Eye, RotateCcw, FlaskConical, ChevronUp, Palette, ShieldCheck, Users, Plus, UserRound, DoorOpen, Minus, ArrowLeft, Undo2, Redo2, Save, History, XCircle, Code2, Search, Filter, User } from 'lucide-react';
9
9
  import clickSoundUrl from '../click_sound-PNCRRTM4.mp3';
10
10
  import hoverSoundUrl from '../hover_sound-NBUA222C.mp3';
11
11
  import { notifications } from '@mantine/notifications';
@@ -1258,11 +1258,11 @@ var colorNames = {
1258
1258
  Yellow: { r: 255, g: 255, b: 0 },
1259
1259
  YellowGreen: { r: 154, g: 205, b: 50 }
1260
1260
  };
1261
- function colorWithAlpha(color, alpha17) {
1261
+ function colorWithAlpha(color, alpha18) {
1262
1262
  const lowerCasedColor = color.toLowerCase();
1263
1263
  if (colorNames[lowerCasedColor]) {
1264
1264
  const rgb = colorNames[lowerCasedColor];
1265
- return `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, ${alpha17})`;
1265
+ return `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, ${alpha18})`;
1266
1266
  }
1267
1267
  if (/^#([A-Fa-f0-9]{6})$/.test(color)) {
1268
1268
  const hex = color.slice(1);
@@ -1270,12 +1270,12 @@ function colorWithAlpha(color, alpha17) {
1270
1270
  const r = bigint >> 16 & 255;
1271
1271
  const g = bigint >> 8 & 255;
1272
1272
  const b = bigint & 255;
1273
- return `rgba(${r}, ${g}, ${b}, ${alpha17})`;
1273
+ return `rgba(${r}, ${g}, ${b}, ${alpha18})`;
1274
1274
  }
1275
1275
  if (/^rgb\((\d{1,3}), (\d{1,3}), (\d{1,3})\)$/.test(color)) {
1276
1276
  const result = color.match(/^rgb\((\d{1,3}), (\d{1,3}), (\d{1,3})\)$/);
1277
1277
  if (result) {
1278
- return `rgba(${result[1]}, ${result[2]}, ${result[3]}, ${alpha17})`;
1278
+ return `rgba(${result[1]}, ${result[2]}, ${result[3]}, ${alpha18})`;
1279
1279
  }
1280
1280
  }
1281
1281
  return color;
@@ -4445,11 +4445,11 @@ function cloneConfig(value) {
4445
4445
  return JSON.parse(JSON.stringify(value));
4446
4446
  }
4447
4447
  function ServerOnlyFetcher() {
4448
- const { fetchConfig } = getScriptConfigInstance();
4448
+ const { fetchServerOnly } = getScriptConfigInstance();
4449
4449
  const { reinitialize } = useFormActions();
4450
4450
  useEffect(() => {
4451
4451
  let cancelled = false;
4452
- fetchConfig().then((full) => {
4452
+ fetchServerOnly().then((full) => {
4453
4453
  if (!cancelled && full) reinitialize(full);
4454
4454
  }).catch(() => {
4455
4455
  });
@@ -4473,7 +4473,7 @@ function ConfigPanel(props) {
4473
4473
  if (isSaving) return;
4474
4474
  setIsSaving(true);
4475
4475
  try {
4476
- const result = await updateConfig(form.values);
4476
+ const result = await updateConfig(form.values, form.changedFields);
4477
4477
  if (result?.success) {
4478
4478
  form.reinitialize(cloneConfig(form.values));
4479
4479
  dirkQueryClient.invalidateQueries({ queryKey: ["scriptConfigHistory"] });
@@ -5900,6 +5900,7 @@ function formatLabel(p) {
5900
5900
  }
5901
5901
  return parts.join(" \xB7 ");
5902
5902
  }
5903
+ var COMMON_ACE_GROUPS = ["group.admin", "group.mod", "group.superadmin"];
5903
5904
  var t = (key, fallback) => {
5904
5905
  const v = locale(key);
5905
5906
  return v === key ? fallback : v;
@@ -5922,7 +5923,6 @@ function AccessOverrideSection({
5922
5923
  schemaKey = "access",
5923
5924
  title,
5924
5925
  description,
5925
- groupType,
5926
5926
  includeOffline = true
5927
5927
  }) {
5928
5928
  const mantineTheme = useMantineTheme();
@@ -5937,14 +5937,19 @@ function AccessOverrideSection({
5937
5937
  const [addingGroup, setAddingGroup] = useState(false);
5938
5938
  const [addingPlayer, setAddingPlayer] = useState(false);
5939
5939
  const commitGroup = (name) => {
5940
- if (!name) return;
5941
- set("groups", [...value.groups, name]);
5940
+ const g = name.trim();
5941
+ if (!g) return;
5942
+ if (!value.groups.includes(g)) set("groups", [...value.groups, g]);
5942
5943
  setAddingGroup(false);
5943
5944
  };
5944
5945
  const setGroup = (index, name) => {
5945
5946
  const next = [...value.groups];
5946
5947
  next[index] = name;
5947
- set("groups", next.filter((g) => g !== ""));
5948
+ set("groups", next);
5949
+ };
5950
+ const quickAddGroup = (name) => {
5951
+ if (value.groups.includes(name)) return;
5952
+ set("groups", [...value.groups, name]);
5948
5953
  };
5949
5954
  const removeGroup = (index) => set("groups", value.groups.filter((_, i) => i !== index));
5950
5955
  const commitIdentifier = (id) => {
@@ -5976,19 +5981,68 @@ function AccessOverrideSection({
5976
5981
  ),
5977
5982
  /* @__PURE__ */ jsx(Text, { ff: "Akrobat Bold", size: "xxs", c: "rgba(255,255,255,0.4)", children: description || t(
5978
5983
  "AccessOverrideDesc",
5979
- "Grant access to this resource's admin panel to specific groups and players, in addition to your global dirk_lib admins. Enforcement is server-side."
5984
+ "Grant access to this resource's admin panel to specific ACE groups and players, in addition to your global dirk_lib admins. Enforcement is server-side."
5980
5985
  ) }),
5981
- /* @__PURE__ */ jsx(SectionLabel, { icon: Users, label: t("AccessGroups", "Groups") }),
5986
+ /* @__PURE__ */ jsx(SectionLabel, { icon: Users, label: t("AccessGroups", "Admin groups (ACE)") }),
5987
+ /* @__PURE__ */ jsx(Flex, { gap: "xs", wrap: "wrap", children: COMMON_ACE_GROUPS.map((g) => {
5988
+ const added = value.groups.includes(g);
5989
+ return /* @__PURE__ */ jsxs(
5990
+ Flex,
5991
+ {
5992
+ align: "center",
5993
+ gap: "xxs",
5994
+ onClick: added ? void 0 : () => quickAddGroup(g),
5995
+ role: "button",
5996
+ tabIndex: added ? -1 : 0,
5997
+ "aria-disabled": added,
5998
+ onKeyDown: (e) => {
5999
+ if (added) return;
6000
+ if (e.key === "Enter" || e.key === " ") {
6001
+ e.preventDefault();
6002
+ quickAddGroup(g);
6003
+ }
6004
+ },
6005
+ px: "xs",
6006
+ py: "xxs",
6007
+ style: {
6008
+ cursor: added ? "default" : "pointer",
6009
+ opacity: added ? 0.4 : 1,
6010
+ background: alpha(color, 0.12),
6011
+ border: `0.1vh solid ${alpha(color, 0.4)}`,
6012
+ borderRadius: "0.4vh",
6013
+ transition: "background 0.15s, border-color 0.15s"
6014
+ },
6015
+ onMouseEnter: (e) => {
6016
+ if (added) return;
6017
+ e.currentTarget.style.background = alpha(color, 0.22);
6018
+ },
6019
+ onMouseLeave: (e) => {
6020
+ if (added) return;
6021
+ e.currentTarget.style.background = alpha(color, 0.12);
6022
+ },
6023
+ children: [
6024
+ !added && /* @__PURE__ */ jsx(Plus, { size: "1.2vh", color }),
6025
+ /* @__PURE__ */ jsx(Text, { ff: "Akrobat Bold", size: "xxs", lts: "0.04em", c: color, children: g })
6026
+ ]
6027
+ },
6028
+ g
6029
+ );
6030
+ }) }),
5982
6031
  /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: "xxs", children: [
5983
- value.groups.length === 0 && !addingGroup && /* @__PURE__ */ jsx(EmptyHint, { text: t("AccessNoGroups", "No groups added.") }),
6032
+ value.groups.length === 0 && !addingGroup && /* @__PURE__ */ jsx(
6033
+ EmptyHint,
6034
+ {
6035
+ text: t("AccessNoGroups", "No ACE groups added. e.g. group.admin")
6036
+ }
6037
+ ),
5984
6038
  value.groups.map((name, i) => /* @__PURE__ */ jsxs(Flex, { align: "center", gap: "xs", children: [
5985
6039
  /* @__PURE__ */ jsx(
5986
- GroupName,
6040
+ TextInput,
5987
6041
  {
5988
6042
  value: name,
5989
- onChange: (v) => setGroup(i, v),
5990
- type: groupType,
5991
- placeholder: t("AccessPickGroup", "Pick a group\u2026"),
6043
+ onChange: (e) => setGroup(i, e.currentTarget.value),
6044
+ placeholder: t("AccessGroupPlaceholder", "ACE group or permission, e.g. group.admin"),
6045
+ size: "xs",
5992
6046
  style: { flex: 1 }
5993
6047
  }
5994
6048
  ),
@@ -6005,30 +6059,15 @@ function AccessOverrideSection({
6005
6059
  }
6006
6060
  )
6007
6061
  ] }, i)),
6008
- addingGroup && /* @__PURE__ */ jsxs(Flex, { align: "center", gap: "xs", children: [
6009
- /* @__PURE__ */ jsx(
6010
- GroupName,
6011
- {
6012
- value: "",
6013
- onChange: (v) => commitGroup(v),
6014
- type: groupType,
6015
- placeholder: t("AccessPickGroup", "Pick a group\u2026"),
6016
- style: { flex: 1 }
6017
- }
6018
- ),
6019
- /* @__PURE__ */ jsx(
6020
- ActionIcon,
6021
- {
6022
- variant: "subtle",
6023
- color: "red",
6024
- size: "md",
6025
- onClick: () => setAddingGroup(false),
6026
- title: t("Remove", "Remove"),
6027
- "aria-label": t("Remove", "Remove"),
6028
- children: /* @__PURE__ */ jsx(Trash2, { size: "1.4vh" })
6029
- }
6030
- )
6031
- ] })
6062
+ addingGroup && /* @__PURE__ */ jsx(
6063
+ AceGroupAddRow,
6064
+ {
6065
+ placeholder: t("AccessGroupPlaceholder", "ACE group or permission, e.g. group.admin"),
6066
+ onCommit: commitGroup,
6067
+ onCancel: () => setAddingGroup(false),
6068
+ removeLabel: t("Remove", "Remove")
6069
+ }
6070
+ )
6032
6071
  ] }),
6033
6072
  !addingGroup && /* @__PURE__ */ jsx(AddRowButton, { label: t("AccessAddGroup", "Add group"), onClick: () => setAddingGroup(true) }),
6034
6073
  /* @__PURE__ */ jsx(SectionLabel, { icon: UserRound, label: t("AccessPlayers", "Players") }),
@@ -6128,6 +6167,53 @@ function AddRowButton({ label, onClick }) {
6128
6167
  }
6129
6168
  );
6130
6169
  }
6170
+ function AceGroupAddRow({
6171
+ placeholder,
6172
+ onCommit,
6173
+ onCancel,
6174
+ removeLabel
6175
+ }) {
6176
+ const [draft, setDraft] = useState("");
6177
+ const commit = () => {
6178
+ if (draft.trim()) onCommit(draft);
6179
+ else onCancel();
6180
+ };
6181
+ return /* @__PURE__ */ jsxs(Flex, { align: "center", gap: "xs", children: [
6182
+ /* @__PURE__ */ jsx(
6183
+ TextInput,
6184
+ {
6185
+ value: draft,
6186
+ onChange: (e) => setDraft(e.currentTarget.value),
6187
+ onKeyDown: (e) => {
6188
+ if (e.key === "Enter") {
6189
+ e.preventDefault();
6190
+ commit();
6191
+ } else if (e.key === "Escape") {
6192
+ e.preventDefault();
6193
+ onCancel();
6194
+ }
6195
+ },
6196
+ onBlur: commit,
6197
+ placeholder,
6198
+ size: "xs",
6199
+ autoFocus: true,
6200
+ style: { flex: 1 }
6201
+ }
6202
+ ),
6203
+ /* @__PURE__ */ jsx(
6204
+ ActionIcon,
6205
+ {
6206
+ variant: "subtle",
6207
+ color: "red",
6208
+ size: "md",
6209
+ onClick: onCancel,
6210
+ title: removeLabel,
6211
+ "aria-label": removeLabel,
6212
+ children: /* @__PURE__ */ jsx(Trash2, { size: "1.4vh" })
6213
+ }
6214
+ )
6215
+ ] });
6216
+ }
6131
6217
 
6132
6218
  // src/utils/gtaAnimPostFx.ts
6133
6219
  var GTA_ANIM_POST_FX_GROUP_ORDER = [