dirk-cfx-react 1.1.90 → 1.1.92
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/components/index.cjs +540 -227
- package/dist/components/index.cjs.map +1 -1
- package/dist/components/index.d.cts +35 -9
- package/dist/components/index.d.ts +35 -9
- package/dist/components/index.js +541 -229
- package/dist/components/index.js.map +1 -1
- package/dist/hooks/index.cjs +55 -25
- package/dist/hooks/index.cjs.map +1 -1
- package/dist/hooks/index.d.cts +2 -2
- package/dist/hooks/index.d.ts +2 -2
- package/dist/hooks/index.js +55 -25
- package/dist/hooks/index.js.map +1 -1
- package/dist/index.cjs +782 -447
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +43 -2
- package/dist/index.d.ts +43 -2
- package/dist/index.js +780 -449
- package/dist/index.js.map +1 -1
- package/dist/providers/index.cjs +22 -12
- package/dist/providers/index.cjs.map +1 -1
- package/dist/providers/index.js +22 -12
- package/dist/providers/index.js.map +1 -1
- package/dist/utils/index.cjs +22 -12
- package/dist/utils/index.cjs.map +1 -1
- package/dist/utils/index.js +22 -12
- package/dist/utils/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -4,6 +4,7 @@ var core = require('@mantine/core');
|
|
|
4
4
|
var jsxRuntime = require('react/jsx-runtime');
|
|
5
5
|
var react = require('react');
|
|
6
6
|
var zustand = require('zustand');
|
|
7
|
+
var zod = require('zod');
|
|
7
8
|
var reactFontawesome = require('@fortawesome/react-fontawesome');
|
|
8
9
|
var framerMotion = require('framer-motion');
|
|
9
10
|
var lucideReact = require('lucide-react');
|
|
@@ -1264,11 +1265,11 @@ var colorNames = {
|
|
|
1264
1265
|
Yellow: { r: 255, g: 255, b: 0 },
|
|
1265
1266
|
YellowGreen: { r: 154, g: 205, b: 50 }
|
|
1266
1267
|
};
|
|
1267
|
-
function colorWithAlpha(color,
|
|
1268
|
+
function colorWithAlpha(color, alpha18) {
|
|
1268
1269
|
const lowerCasedColor = color.toLowerCase();
|
|
1269
1270
|
if (colorNames[lowerCasedColor]) {
|
|
1270
1271
|
const rgb = colorNames[lowerCasedColor];
|
|
1271
|
-
return `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, ${
|
|
1272
|
+
return `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, ${alpha18})`;
|
|
1272
1273
|
}
|
|
1273
1274
|
if (/^#([A-Fa-f0-9]{6})$/.test(color)) {
|
|
1274
1275
|
const hex = color.slice(1);
|
|
@@ -1276,12 +1277,12 @@ function colorWithAlpha(color, alpha17) {
|
|
|
1276
1277
|
const r = bigint >> 16 & 255;
|
|
1277
1278
|
const g = bigint >> 8 & 255;
|
|
1278
1279
|
const b = bigint & 255;
|
|
1279
|
-
return `rgba(${r}, ${g}, ${b}, ${
|
|
1280
|
+
return `rgba(${r}, ${g}, ${b}, ${alpha18})`;
|
|
1280
1281
|
}
|
|
1281
1282
|
if (/^rgb\((\d{1,3}), (\d{1,3}), (\d{1,3})\)$/.test(color)) {
|
|
1282
1283
|
const result = color.match(/^rgb\((\d{1,3}), (\d{1,3}), (\d{1,3})\)$/);
|
|
1283
1284
|
if (result) {
|
|
1284
|
-
return `rgba(${result[1]}, ${result[2]}, ${result[3]}, ${
|
|
1285
|
+
return `rgba(${result[1]}, ${result[2]}, ${result[3]}, ${alpha18})`;
|
|
1285
1286
|
}
|
|
1286
1287
|
}
|
|
1287
1288
|
return color;
|
|
@@ -1289,6 +1290,32 @@ function colorWithAlpha(color, alpha17) {
|
|
|
1289
1290
|
|
|
1290
1291
|
// src/utils/misc.ts
|
|
1291
1292
|
var isEnvBrowser = () => !window.invokeNative;
|
|
1293
|
+
var DEFAULT_PALETTE = [
|
|
1294
|
+
"#f0f4ff",
|
|
1295
|
+
"#d9e3ff",
|
|
1296
|
+
"#bfcfff",
|
|
1297
|
+
"#a6bbff",
|
|
1298
|
+
"#8ca7ff",
|
|
1299
|
+
"#7393ff",
|
|
1300
|
+
"#5a7fff",
|
|
1301
|
+
"#406bff",
|
|
1302
|
+
"#2547ff",
|
|
1303
|
+
"#0b33ff"
|
|
1304
|
+
];
|
|
1305
|
+
zod.z.object({
|
|
1306
|
+
useOverride: zod.z.boolean(),
|
|
1307
|
+
primaryColor: zod.z.string(),
|
|
1308
|
+
primaryShade: zod.z.number(),
|
|
1309
|
+
customTheme: zod.z.array(zod.z.string())
|
|
1310
|
+
});
|
|
1311
|
+
var defaultThemeOverride = {
|
|
1312
|
+
useOverride: false,
|
|
1313
|
+
primaryColor: "dirk",
|
|
1314
|
+
primaryShade: 5,
|
|
1315
|
+
customTheme: DEFAULT_PALETTE
|
|
1316
|
+
};
|
|
1317
|
+
|
|
1318
|
+
// src/utils/useSettings.ts
|
|
1292
1319
|
var useSettings = zustand.create(() => ({
|
|
1293
1320
|
currency: "$",
|
|
1294
1321
|
game: "fivem",
|
|
@@ -1296,18 +1323,7 @@ var useSettings = zustand.create(() => ({
|
|
|
1296
1323
|
primaryShade: 9,
|
|
1297
1324
|
itemImgPath: "",
|
|
1298
1325
|
resourceVersion: "dev",
|
|
1299
|
-
customTheme:
|
|
1300
|
-
"#f0f4ff",
|
|
1301
|
-
"#d9e3ff",
|
|
1302
|
-
"#bfcfff",
|
|
1303
|
-
"#a6bbff",
|
|
1304
|
-
"#8ca7ff",
|
|
1305
|
-
"#7393ff",
|
|
1306
|
-
"#5a7fff",
|
|
1307
|
-
"#406bff",
|
|
1308
|
-
"#2547ff",
|
|
1309
|
-
"#0b33ff"
|
|
1310
|
-
]
|
|
1326
|
+
customTheme: DEFAULT_PALETTE
|
|
1311
1327
|
}));
|
|
1312
1328
|
|
|
1313
1329
|
// src/utils/fetchNui.ts
|
|
@@ -3161,11 +3177,11 @@ function ConfirmModal({
|
|
|
3161
3177
|
placeholder: confirmText,
|
|
3162
3178
|
value: typed,
|
|
3163
3179
|
onChange: (e) => setTyped(e.currentTarget.value),
|
|
3164
|
-
styles: (
|
|
3180
|
+
styles: (t4) => ({
|
|
3165
3181
|
input: {
|
|
3166
|
-
backgroundColor: core.alpha(
|
|
3182
|
+
backgroundColor: core.alpha(t4.colors.dark[7], 0.5),
|
|
3167
3183
|
border: `0.1vh solid ${core.alpha(
|
|
3168
|
-
typed === confirmText ? "#ef4444" :
|
|
3184
|
+
typed === confirmText ? "#ef4444" : t4.colors.dark[5],
|
|
3169
3185
|
0.5
|
|
3170
3186
|
)}`,
|
|
3171
3187
|
color: "rgba(255,255,255,0.85)",
|
|
@@ -3810,7 +3826,7 @@ function useAdminState(key, initial) {
|
|
|
3810
3826
|
react.useEffect(() => {
|
|
3811
3827
|
const set = listeners.get(key) ?? /* @__PURE__ */ new Set();
|
|
3812
3828
|
listeners.set(key, set);
|
|
3813
|
-
const handler = () => setTick((
|
|
3829
|
+
const handler = () => setTick((t4) => t4 + 1);
|
|
3814
3830
|
set.add(handler);
|
|
3815
3831
|
return () => {
|
|
3816
3832
|
set.delete(handler);
|
|
@@ -4464,7 +4480,7 @@ function ConfigPanel(props) {
|
|
|
4464
4480
|
if (isSaving) return;
|
|
4465
4481
|
setIsSaving(true);
|
|
4466
4482
|
try {
|
|
4467
|
-
const result = await updateConfig(form.values);
|
|
4483
|
+
const result = await updateConfig(form.values, form.changedFields);
|
|
4468
4484
|
if (result?.success) {
|
|
4469
4485
|
form.reinitialize(cloneConfig(form.values));
|
|
4470
4486
|
dirkQueryClient.invalidateQueries({ queryKey: ["scriptConfigHistory"] });
|
|
@@ -5498,24 +5514,6 @@ var MANTINE_COLOR_OPTIONS = [
|
|
|
5498
5514
|
"yellow",
|
|
5499
5515
|
"orange"
|
|
5500
5516
|
].map((value) => ({ value, label: value }));
|
|
5501
|
-
var DEFAULT_PALETTE = [
|
|
5502
|
-
"#f0f4ff",
|
|
5503
|
-
"#d9e3ff",
|
|
5504
|
-
"#bfcfff",
|
|
5505
|
-
"#a6bbff",
|
|
5506
|
-
"#8ca7ff",
|
|
5507
|
-
"#7393ff",
|
|
5508
|
-
"#5a7fff",
|
|
5509
|
-
"#406bff",
|
|
5510
|
-
"#2547ff",
|
|
5511
|
-
"#0b33ff"
|
|
5512
|
-
];
|
|
5513
|
-
var DEFAULT_VALUE = {
|
|
5514
|
-
useOverride: false,
|
|
5515
|
-
primaryColor: "dirk",
|
|
5516
|
-
primaryShade: 5,
|
|
5517
|
-
customTheme: DEFAULT_PALETTE
|
|
5518
|
-
};
|
|
5519
5517
|
function GroupLabel({ label }) {
|
|
5520
5518
|
return /* @__PURE__ */ jsxRuntime.jsxs(core.Flex, { align: "center", gap: "xs", mt: "xxs", children: [
|
|
5521
5519
|
/* @__PURE__ */ jsxRuntime.jsx(core.Text, { ff: "Akrobat Bold", size: "xxs", tt: "uppercase", lts: "0.07em", c: "rgba(255,255,255,0.2)", children: label }),
|
|
@@ -5530,10 +5528,10 @@ function ThemeOverrideSection({
|
|
|
5530
5528
|
const color = mantineTheme.colors[mantineTheme.primaryColor][5];
|
|
5531
5529
|
const raw = useFormField(schemaKey);
|
|
5532
5530
|
const value = {
|
|
5533
|
-
useOverride: raw?.useOverride ??
|
|
5534
|
-
primaryColor: raw?.primaryColor ??
|
|
5535
|
-
primaryShade: raw?.primaryShade ??
|
|
5536
|
-
customTheme: Array.isArray(raw?.customTheme) && raw.customTheme.length === 10 ? raw.customTheme :
|
|
5531
|
+
useOverride: raw?.useOverride ?? defaultThemeOverride.useOverride,
|
|
5532
|
+
primaryColor: raw?.primaryColor ?? defaultThemeOverride.primaryColor,
|
|
5533
|
+
primaryShade: raw?.primaryShade ?? defaultThemeOverride.primaryShade,
|
|
5534
|
+
customTheme: Array.isArray(raw?.customTheme) && raw.customTheme.length === 10 ? raw.customTheme : defaultThemeOverride.customTheme
|
|
5537
5535
|
};
|
|
5538
5536
|
const { setValue } = useFormActions();
|
|
5539
5537
|
const set = (key, val) => setValue(schemaKey, { ...value, [key]: val });
|
|
@@ -5740,6 +5738,489 @@ function SwatchTile({
|
|
|
5740
5738
|
) })
|
|
5741
5739
|
] });
|
|
5742
5740
|
}
|
|
5741
|
+
function usePlayers(opts = {}) {
|
|
5742
|
+
const {
|
|
5743
|
+
includeOffline = false,
|
|
5744
|
+
search = "",
|
|
5745
|
+
limit = 50,
|
|
5746
|
+
staleTimeMs,
|
|
5747
|
+
refetchIntervalMs
|
|
5748
|
+
} = opts;
|
|
5749
|
+
const query = reactQuery.useQuery({
|
|
5750
|
+
queryKey: includeOffline ? ["dirk:players", "search", search.trim().toLowerCase(), limit] : ["dirk:players", "online"],
|
|
5751
|
+
queryFn: async () => {
|
|
5752
|
+
const toolId = includeOffline ? "searchPlayers" : "getOnlinePlayers";
|
|
5753
|
+
const payload = includeOffline ? { id: toolId, value: { search: search.trim(), limit } } : { id: toolId };
|
|
5754
|
+
const result = await fetchNui(
|
|
5755
|
+
"ADMIN_TOOL_QUERY",
|
|
5756
|
+
payload,
|
|
5757
|
+
// Browser-dev fallback. Returns a couple of mock players so the
|
|
5758
|
+
// dev shell isn't blank.
|
|
5759
|
+
includeOffline ? [
|
|
5760
|
+
{ id: 1, citizenId: "ABC12345", name: "Dev User", charName: "John Doe", online: true },
|
|
5761
|
+
{ id: null, citizenId: "DEF67890", name: "", charName: "Jane Offline", online: false }
|
|
5762
|
+
] : [
|
|
5763
|
+
{ id: 1, citizenId: "ABC12345", name: "Dev User", charName: "John Doe", online: true }
|
|
5764
|
+
]
|
|
5765
|
+
);
|
|
5766
|
+
return Array.isArray(result) ? result : [];
|
|
5767
|
+
},
|
|
5768
|
+
staleTime: staleTimeMs ?? (includeOffline ? 3e4 : 5e3),
|
|
5769
|
+
gcTime: 5 * 6e4,
|
|
5770
|
+
refetchInterval: refetchIntervalMs ?? false,
|
|
5771
|
+
refetchOnWindowFocus: false,
|
|
5772
|
+
placeholderData: includeOffline ? reactQuery.keepPreviousData : void 0
|
|
5773
|
+
});
|
|
5774
|
+
return {
|
|
5775
|
+
players: query.data ?? [],
|
|
5776
|
+
isLoading: query.isLoading,
|
|
5777
|
+
isFetching: query.isFetching,
|
|
5778
|
+
error: query.error ?? null,
|
|
5779
|
+
refresh: () => query.refetch()
|
|
5780
|
+
};
|
|
5781
|
+
}
|
|
5782
|
+
var DEBOUNCE_MS = 300;
|
|
5783
|
+
var ONLINE_COLOR = "#3FA83F";
|
|
5784
|
+
var OFFLINE_COLOR = "#E54141";
|
|
5785
|
+
function PlayerSelect({
|
|
5786
|
+
value,
|
|
5787
|
+
onChange,
|
|
5788
|
+
includeOffline = false,
|
|
5789
|
+
limit = 50,
|
|
5790
|
+
searchable: searchableProp,
|
|
5791
|
+
placeholder,
|
|
5792
|
+
nothingFoundMessage: nothingFoundMessageProp,
|
|
5793
|
+
loadingLabel = "Loading\u2026",
|
|
5794
|
+
onlineLabel = "Online",
|
|
5795
|
+
offlineLabel = "Offline",
|
|
5796
|
+
refreshLabel = "Refresh player list",
|
|
5797
|
+
...rest
|
|
5798
|
+
}) {
|
|
5799
|
+
const theme = core.useMantineTheme();
|
|
5800
|
+
const color = theme.colors[theme.primaryColor][5];
|
|
5801
|
+
const [searchInput, setSearchInput] = react.useState("");
|
|
5802
|
+
const [debouncedSearch, setDebouncedSearch] = react.useState("");
|
|
5803
|
+
const { players, isFetching, refresh } = usePlayers({
|
|
5804
|
+
includeOffline,
|
|
5805
|
+
search: includeOffline ? debouncedSearch : void 0,
|
|
5806
|
+
limit
|
|
5807
|
+
});
|
|
5808
|
+
const sortedPlayers = react.useMemo(
|
|
5809
|
+
() => [...players].sort((a, b) => {
|
|
5810
|
+
if (a.online !== b.online) return a.online ? -1 : 1;
|
|
5811
|
+
return a.charName.localeCompare(b.charName);
|
|
5812
|
+
}),
|
|
5813
|
+
[players]
|
|
5814
|
+
);
|
|
5815
|
+
const playerByCitizen = react.useMemo(() => {
|
|
5816
|
+
const m = /* @__PURE__ */ new Map();
|
|
5817
|
+
for (const p of sortedPlayers) m.set(p.citizenId, p);
|
|
5818
|
+
return m;
|
|
5819
|
+
}, [sortedPlayers]);
|
|
5820
|
+
const data = react.useMemo(() => {
|
|
5821
|
+
const items = sortedPlayers.map((p) => ({
|
|
5822
|
+
value: p.citizenId,
|
|
5823
|
+
label: formatLabel(p)
|
|
5824
|
+
}));
|
|
5825
|
+
if (value && !items.some((i) => i.value === value)) {
|
|
5826
|
+
items.unshift({ value, label: value });
|
|
5827
|
+
}
|
|
5828
|
+
return items;
|
|
5829
|
+
}, [sortedPlayers, value]);
|
|
5830
|
+
const selectedPlayer = value ? playerByCitizen.get(value) ?? null : null;
|
|
5831
|
+
const selectedLabel = selectedPlayer ? formatLabel(selectedPlayer) : null;
|
|
5832
|
+
react.useEffect(() => {
|
|
5833
|
+
if (!includeOffline) return;
|
|
5834
|
+
if (selectedLabel && searchInput === selectedLabel) return;
|
|
5835
|
+
const t4 = setTimeout(() => setDebouncedSearch(searchInput), DEBOUNCE_MS);
|
|
5836
|
+
return () => clearTimeout(t4);
|
|
5837
|
+
}, [searchInput, includeOffline, selectedLabel]);
|
|
5838
|
+
const renderOption = ({ option }) => {
|
|
5839
|
+
const p = playerByCitizen.get(option.value);
|
|
5840
|
+
if (!p) return option.label;
|
|
5841
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(core.Group, { justify: "space-between", wrap: "nowrap", gap: "xs", style: { width: "100%" }, children: [
|
|
5842
|
+
/* @__PURE__ */ jsxRuntime.jsx(core.Text, { size: "xs", truncate: true, style: { flex: 1 }, children: formatLabel(p) }),
|
|
5843
|
+
/* @__PURE__ */ jsxRuntime.jsx(StatusDot, { online: p.online, onlineLabel, offlineLabel })
|
|
5844
|
+
] });
|
|
5845
|
+
};
|
|
5846
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
5847
|
+
core.Select,
|
|
5848
|
+
{
|
|
5849
|
+
...rest,
|
|
5850
|
+
data,
|
|
5851
|
+
value: value ?? null,
|
|
5852
|
+
onChange: (v) => {
|
|
5853
|
+
if (!v) return onChange(null);
|
|
5854
|
+
const player = playerByCitizen.get(v) ?? null;
|
|
5855
|
+
onChange(player);
|
|
5856
|
+
},
|
|
5857
|
+
searchable: searchableProp ?? true,
|
|
5858
|
+
searchValue: includeOffline ? searchInput : void 0,
|
|
5859
|
+
onSearchChange: includeOffline ? setSearchInput : void 0,
|
|
5860
|
+
placeholder,
|
|
5861
|
+
nothingFoundMessage: isFetching ? loadingLabel : nothingFoundMessageProp ?? "No players found",
|
|
5862
|
+
maxDropdownHeight: 300,
|
|
5863
|
+
renderOption,
|
|
5864
|
+
leftSection: selectedPlayer ? /* @__PURE__ */ jsxRuntime.jsx(StatusDot, { online: selectedPlayer.online, onlineLabel, offlineLabel }) : void 0,
|
|
5865
|
+
rightSectionWidth: "3.5vh",
|
|
5866
|
+
rightSectionPointerEvents: "all",
|
|
5867
|
+
rightSection: /* @__PURE__ */ jsxRuntime.jsx(
|
|
5868
|
+
core.ActionIcon,
|
|
5869
|
+
{
|
|
5870
|
+
variant: "subtle",
|
|
5871
|
+
size: "sm",
|
|
5872
|
+
onClick: (e) => {
|
|
5873
|
+
e.stopPropagation();
|
|
5874
|
+
refresh();
|
|
5875
|
+
},
|
|
5876
|
+
"aria-label": refreshLabel,
|
|
5877
|
+
title: refreshLabel,
|
|
5878
|
+
style: { marginRight: "0.6vh" },
|
|
5879
|
+
children: isFetching ? /* @__PURE__ */ jsxRuntime.jsx(core.Loader, { size: "1.2vh", color }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.RefreshCw, { size: "1.4vh", color })
|
|
5880
|
+
}
|
|
5881
|
+
)
|
|
5882
|
+
}
|
|
5883
|
+
);
|
|
5884
|
+
}
|
|
5885
|
+
function StatusDot({ online, onlineLabel, offlineLabel }) {
|
|
5886
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
5887
|
+
"span",
|
|
5888
|
+
{
|
|
5889
|
+
title: online ? onlineLabel : offlineLabel,
|
|
5890
|
+
style: {
|
|
5891
|
+
display: "inline-block",
|
|
5892
|
+
width: "0.9vh",
|
|
5893
|
+
height: "0.9vh",
|
|
5894
|
+
borderRadius: "50%",
|
|
5895
|
+
backgroundColor: online ? ONLINE_COLOR : OFFLINE_COLOR,
|
|
5896
|
+
boxShadow: `0 0 0.4vh ${online ? ONLINE_COLOR : OFFLINE_COLOR}`,
|
|
5897
|
+
flexShrink: 0
|
|
5898
|
+
}
|
|
5899
|
+
}
|
|
5900
|
+
);
|
|
5901
|
+
}
|
|
5902
|
+
function formatLabel(p) {
|
|
5903
|
+
const parts = [p.charName || p.citizenId];
|
|
5904
|
+
if (p.online) {
|
|
5905
|
+
if (p.name) parts.push(p.name);
|
|
5906
|
+
if (p.id != null) parts.push(`#${p.id}`);
|
|
5907
|
+
}
|
|
5908
|
+
return parts.join(" \xB7 ");
|
|
5909
|
+
}
|
|
5910
|
+
var COMMON_ACE_GROUPS = ["group.admin", "group.mod", "group.superadmin"];
|
|
5911
|
+
var t = (key, fallback) => {
|
|
5912
|
+
const v = locale(key);
|
|
5913
|
+
return v === key ? fallback : v;
|
|
5914
|
+
};
|
|
5915
|
+
var DEFAULT_VALUE = {
|
|
5916
|
+
groups: [],
|
|
5917
|
+
identifiers: []
|
|
5918
|
+
};
|
|
5919
|
+
function SectionLabel({ icon: Icon, label }) {
|
|
5920
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(core.Flex, { align: "center", gap: "xs", mt: "xs", children: [
|
|
5921
|
+
/* @__PURE__ */ jsxRuntime.jsx(Icon, { size: "1.4vh", color: "rgba(255,255,255,0.3)" }),
|
|
5922
|
+
/* @__PURE__ */ jsxRuntime.jsx(core.Text, { ff: "Akrobat Bold", size: "xxs", tt: "uppercase", lts: "0.07em", c: "rgba(255,255,255,0.2)", children: label }),
|
|
5923
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { style: { flex: 1, height: "0.05vh", background: "rgba(255,255,255,0.06)" } })
|
|
5924
|
+
] });
|
|
5925
|
+
}
|
|
5926
|
+
function EmptyHint({ text }) {
|
|
5927
|
+
return /* @__PURE__ */ jsxRuntime.jsx(core.Text, { ff: "Akrobat Bold", size: "xxs", c: "rgba(255,255,255,0.25)", ta: "center", py: "xs", children: text });
|
|
5928
|
+
}
|
|
5929
|
+
function AccessOverrideSection({
|
|
5930
|
+
schemaKey = "access",
|
|
5931
|
+
title,
|
|
5932
|
+
description,
|
|
5933
|
+
includeOffline = true
|
|
5934
|
+
}) {
|
|
5935
|
+
const mantineTheme = core.useMantineTheme();
|
|
5936
|
+
const color = mantineTheme.colors[mantineTheme.primaryColor][5];
|
|
5937
|
+
const raw = useFormField(schemaKey);
|
|
5938
|
+
const value = {
|
|
5939
|
+
groups: Array.isArray(raw?.groups) ? raw.groups : DEFAULT_VALUE.groups,
|
|
5940
|
+
identifiers: Array.isArray(raw?.identifiers) ? raw.identifiers : DEFAULT_VALUE.identifiers
|
|
5941
|
+
};
|
|
5942
|
+
const { setValue } = useFormActions();
|
|
5943
|
+
const set = (key, val) => setValue(schemaKey, { ...value, [key]: val });
|
|
5944
|
+
const [addingGroup, setAddingGroup] = react.useState(false);
|
|
5945
|
+
const [addingPlayer, setAddingPlayer] = react.useState(false);
|
|
5946
|
+
const commitGroup = (name) => {
|
|
5947
|
+
const g = name.trim();
|
|
5948
|
+
if (!g) return;
|
|
5949
|
+
if (!value.groups.includes(g)) set("groups", [...value.groups, g]);
|
|
5950
|
+
setAddingGroup(false);
|
|
5951
|
+
};
|
|
5952
|
+
const setGroup = (index, name) => {
|
|
5953
|
+
const next = [...value.groups];
|
|
5954
|
+
next[index] = name;
|
|
5955
|
+
set("groups", next);
|
|
5956
|
+
};
|
|
5957
|
+
const quickAddGroup = (name) => {
|
|
5958
|
+
if (value.groups.includes(name)) return;
|
|
5959
|
+
set("groups", [...value.groups, name]);
|
|
5960
|
+
};
|
|
5961
|
+
const removeGroup = (index) => set("groups", value.groups.filter((_, i) => i !== index));
|
|
5962
|
+
const commitIdentifier = (id) => {
|
|
5963
|
+
if (!id) return;
|
|
5964
|
+
set("identifiers", [...value.identifiers, id]);
|
|
5965
|
+
setAddingPlayer(false);
|
|
5966
|
+
};
|
|
5967
|
+
const setIdentifier = (index, id) => {
|
|
5968
|
+
const next = [...value.identifiers];
|
|
5969
|
+
next[index] = id;
|
|
5970
|
+
set("identifiers", next.filter((x) => x !== ""));
|
|
5971
|
+
};
|
|
5972
|
+
const removeIdentifier = (index) => set("identifiers", value.identifiers.filter((_, i) => i !== index));
|
|
5973
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
5974
|
+
core.Flex,
|
|
5975
|
+
{
|
|
5976
|
+
direction: "column",
|
|
5977
|
+
gap: "xs",
|
|
5978
|
+
p: "sm",
|
|
5979
|
+
style: { flex: 1, minHeight: 0, overflowY: "auto" },
|
|
5980
|
+
children: [
|
|
5981
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
5982
|
+
AdminPageTitle,
|
|
5983
|
+
{
|
|
5984
|
+
icon: lucideReact.ShieldCheck,
|
|
5985
|
+
title: title || t("Access", "Access"),
|
|
5986
|
+
color
|
|
5987
|
+
}
|
|
5988
|
+
),
|
|
5989
|
+
/* @__PURE__ */ jsxRuntime.jsx(core.Text, { ff: "Akrobat Bold", size: "xxs", c: "rgba(255,255,255,0.4)", children: description || t(
|
|
5990
|
+
"AccessOverrideDesc",
|
|
5991
|
+
"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."
|
|
5992
|
+
) }),
|
|
5993
|
+
/* @__PURE__ */ jsxRuntime.jsx(SectionLabel, { icon: lucideReact.Users, label: t("AccessGroups", "Admin groups (ACE)") }),
|
|
5994
|
+
/* @__PURE__ */ jsxRuntime.jsx(core.Flex, { gap: "xs", wrap: "wrap", children: COMMON_ACE_GROUPS.map((g) => {
|
|
5995
|
+
const added = value.groups.includes(g);
|
|
5996
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
5997
|
+
core.Flex,
|
|
5998
|
+
{
|
|
5999
|
+
align: "center",
|
|
6000
|
+
gap: "xxs",
|
|
6001
|
+
onClick: added ? void 0 : () => quickAddGroup(g),
|
|
6002
|
+
role: "button",
|
|
6003
|
+
tabIndex: added ? -1 : 0,
|
|
6004
|
+
"aria-disabled": added,
|
|
6005
|
+
onKeyDown: (e) => {
|
|
6006
|
+
if (added) return;
|
|
6007
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
6008
|
+
e.preventDefault();
|
|
6009
|
+
quickAddGroup(g);
|
|
6010
|
+
}
|
|
6011
|
+
},
|
|
6012
|
+
px: "xs",
|
|
6013
|
+
py: "xxs",
|
|
6014
|
+
style: {
|
|
6015
|
+
cursor: added ? "default" : "pointer",
|
|
6016
|
+
opacity: added ? 0.4 : 1,
|
|
6017
|
+
background: core.alpha(color, 0.12),
|
|
6018
|
+
border: `0.1vh solid ${core.alpha(color, 0.4)}`,
|
|
6019
|
+
borderRadius: "0.4vh",
|
|
6020
|
+
transition: "background 0.15s, border-color 0.15s"
|
|
6021
|
+
},
|
|
6022
|
+
onMouseEnter: (e) => {
|
|
6023
|
+
if (added) return;
|
|
6024
|
+
e.currentTarget.style.background = core.alpha(color, 0.22);
|
|
6025
|
+
},
|
|
6026
|
+
onMouseLeave: (e) => {
|
|
6027
|
+
if (added) return;
|
|
6028
|
+
e.currentTarget.style.background = core.alpha(color, 0.12);
|
|
6029
|
+
},
|
|
6030
|
+
children: [
|
|
6031
|
+
!added && /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Plus, { size: "1.2vh", color }),
|
|
6032
|
+
/* @__PURE__ */ jsxRuntime.jsx(core.Text, { ff: "Akrobat Bold", size: "xxs", lts: "0.04em", c: color, children: g })
|
|
6033
|
+
]
|
|
6034
|
+
},
|
|
6035
|
+
g
|
|
6036
|
+
);
|
|
6037
|
+
}) }),
|
|
6038
|
+
/* @__PURE__ */ jsxRuntime.jsxs(core.Flex, { direction: "column", gap: "xxs", children: [
|
|
6039
|
+
value.groups.length === 0 && !addingGroup && /* @__PURE__ */ jsxRuntime.jsx(
|
|
6040
|
+
EmptyHint,
|
|
6041
|
+
{
|
|
6042
|
+
text: t("AccessNoGroups", "No ACE groups added. e.g. group.admin")
|
|
6043
|
+
}
|
|
6044
|
+
),
|
|
6045
|
+
value.groups.map((name, i) => /* @__PURE__ */ jsxRuntime.jsxs(core.Flex, { align: "center", gap: "xs", children: [
|
|
6046
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
6047
|
+
core.TextInput,
|
|
6048
|
+
{
|
|
6049
|
+
value: name,
|
|
6050
|
+
onChange: (e) => setGroup(i, e.currentTarget.value),
|
|
6051
|
+
placeholder: t("AccessGroupPlaceholder", "ACE group or permission, e.g. group.admin"),
|
|
6052
|
+
size: "xs",
|
|
6053
|
+
style: { flex: 1 }
|
|
6054
|
+
}
|
|
6055
|
+
),
|
|
6056
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
6057
|
+
core.ActionIcon,
|
|
6058
|
+
{
|
|
6059
|
+
variant: "subtle",
|
|
6060
|
+
color: "red",
|
|
6061
|
+
size: "md",
|
|
6062
|
+
onClick: () => removeGroup(i),
|
|
6063
|
+
title: t("Remove", "Remove"),
|
|
6064
|
+
"aria-label": t("Remove", "Remove"),
|
|
6065
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Trash2, { size: "1.4vh" })
|
|
6066
|
+
}
|
|
6067
|
+
)
|
|
6068
|
+
] }, i)),
|
|
6069
|
+
addingGroup && /* @__PURE__ */ jsxRuntime.jsx(
|
|
6070
|
+
AceGroupAddRow,
|
|
6071
|
+
{
|
|
6072
|
+
placeholder: t("AccessGroupPlaceholder", "ACE group or permission, e.g. group.admin"),
|
|
6073
|
+
onCommit: commitGroup,
|
|
6074
|
+
onCancel: () => setAddingGroup(false),
|
|
6075
|
+
removeLabel: t("Remove", "Remove")
|
|
6076
|
+
}
|
|
6077
|
+
)
|
|
6078
|
+
] }),
|
|
6079
|
+
!addingGroup && /* @__PURE__ */ jsxRuntime.jsx(AddRowButton, { label: t("AccessAddGroup", "Add group"), onClick: () => setAddingGroup(true) }),
|
|
6080
|
+
/* @__PURE__ */ jsxRuntime.jsx(SectionLabel, { icon: lucideReact.UserRound, label: t("AccessPlayers", "Players") }),
|
|
6081
|
+
/* @__PURE__ */ jsxRuntime.jsxs(core.Flex, { direction: "column", gap: "xxs", children: [
|
|
6082
|
+
value.identifiers.length === 0 && !addingPlayer && /* @__PURE__ */ jsxRuntime.jsx(EmptyHint, { text: t("AccessNoPlayers", "No players added.") }),
|
|
6083
|
+
value.identifiers.map((id, i) => /* @__PURE__ */ jsxRuntime.jsxs(core.Flex, { align: "center", gap: "xs", children: [
|
|
6084
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
6085
|
+
PlayerSelect,
|
|
6086
|
+
{
|
|
6087
|
+
value: id || null,
|
|
6088
|
+
onChange: (p) => setIdentifier(i, p?.citizenId ?? ""),
|
|
6089
|
+
includeOffline,
|
|
6090
|
+
placeholder: t("AccessPickPlayer", "Pick a player\u2026"),
|
|
6091
|
+
style: { flex: 1 },
|
|
6092
|
+
size: "xs"
|
|
6093
|
+
}
|
|
6094
|
+
),
|
|
6095
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
6096
|
+
core.ActionIcon,
|
|
6097
|
+
{
|
|
6098
|
+
variant: "subtle",
|
|
6099
|
+
color: "red",
|
|
6100
|
+
size: "md",
|
|
6101
|
+
onClick: () => removeIdentifier(i),
|
|
6102
|
+
title: t("Remove", "Remove"),
|
|
6103
|
+
"aria-label": t("Remove", "Remove"),
|
|
6104
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Trash2, { size: "1.4vh" })
|
|
6105
|
+
}
|
|
6106
|
+
)
|
|
6107
|
+
] }, i)),
|
|
6108
|
+
addingPlayer && /* @__PURE__ */ jsxRuntime.jsxs(core.Flex, { align: "center", gap: "xs", children: [
|
|
6109
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
6110
|
+
PlayerSelect,
|
|
6111
|
+
{
|
|
6112
|
+
value: null,
|
|
6113
|
+
onChange: (p) => commitIdentifier(p?.citizenId ?? ""),
|
|
6114
|
+
includeOffline,
|
|
6115
|
+
placeholder: t("AccessPickPlayer", "Pick a player\u2026"),
|
|
6116
|
+
style: { flex: 1 },
|
|
6117
|
+
size: "xs"
|
|
6118
|
+
}
|
|
6119
|
+
),
|
|
6120
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
6121
|
+
core.ActionIcon,
|
|
6122
|
+
{
|
|
6123
|
+
variant: "subtle",
|
|
6124
|
+
color: "red",
|
|
6125
|
+
size: "md",
|
|
6126
|
+
onClick: () => setAddingPlayer(false),
|
|
6127
|
+
title: t("Remove", "Remove"),
|
|
6128
|
+
"aria-label": t("Remove", "Remove"),
|
|
6129
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Trash2, { size: "1.4vh" })
|
|
6130
|
+
}
|
|
6131
|
+
)
|
|
6132
|
+
] })
|
|
6133
|
+
] }),
|
|
6134
|
+
!addingPlayer && /* @__PURE__ */ jsxRuntime.jsx(AddRowButton, { label: t("AccessAddPlayer", "Add player"), onClick: () => setAddingPlayer(true) })
|
|
6135
|
+
]
|
|
6136
|
+
}
|
|
6137
|
+
);
|
|
6138
|
+
}
|
|
6139
|
+
function AddRowButton({ label, onClick }) {
|
|
6140
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
6141
|
+
core.Flex,
|
|
6142
|
+
{
|
|
6143
|
+
align: "center",
|
|
6144
|
+
justify: "center",
|
|
6145
|
+
gap: "xs",
|
|
6146
|
+
onClick,
|
|
6147
|
+
role: "button",
|
|
6148
|
+
tabIndex: 0,
|
|
6149
|
+
onKeyDown: (e) => {
|
|
6150
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
6151
|
+
e.preventDefault();
|
|
6152
|
+
onClick();
|
|
6153
|
+
}
|
|
6154
|
+
},
|
|
6155
|
+
py: "xs",
|
|
6156
|
+
style: {
|
|
6157
|
+
cursor: "pointer",
|
|
6158
|
+
border: "0.1vh dashed rgba(255,255,255,0.12)",
|
|
6159
|
+
borderRadius: "0.4vh",
|
|
6160
|
+
transition: "background 0.15s, border-color 0.15s"
|
|
6161
|
+
},
|
|
6162
|
+
onMouseEnter: (e) => {
|
|
6163
|
+
e.currentTarget.style.background = "rgba(255,255,255,0.03)";
|
|
6164
|
+
e.currentTarget.style.borderColor = "rgba(255,255,255,0.2)";
|
|
6165
|
+
},
|
|
6166
|
+
onMouseLeave: (e) => {
|
|
6167
|
+
e.currentTarget.style.background = "transparent";
|
|
6168
|
+
e.currentTarget.style.borderColor = "rgba(255,255,255,0.12)";
|
|
6169
|
+
},
|
|
6170
|
+
children: [
|
|
6171
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Plus, { size: "1.4vh", color: "rgba(255,255,255,0.4)" }),
|
|
6172
|
+
/* @__PURE__ */ jsxRuntime.jsx(core.Text, { ff: "Akrobat Bold", size: "xxs", tt: "uppercase", lts: "0.06em", c: "rgba(255,255,255,0.4)", children: label })
|
|
6173
|
+
]
|
|
6174
|
+
}
|
|
6175
|
+
);
|
|
6176
|
+
}
|
|
6177
|
+
function AceGroupAddRow({
|
|
6178
|
+
placeholder,
|
|
6179
|
+
onCommit,
|
|
6180
|
+
onCancel,
|
|
6181
|
+
removeLabel
|
|
6182
|
+
}) {
|
|
6183
|
+
const [draft, setDraft] = react.useState("");
|
|
6184
|
+
const commit = () => {
|
|
6185
|
+
if (draft.trim()) onCommit(draft);
|
|
6186
|
+
else onCancel();
|
|
6187
|
+
};
|
|
6188
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(core.Flex, { align: "center", gap: "xs", children: [
|
|
6189
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
6190
|
+
core.TextInput,
|
|
6191
|
+
{
|
|
6192
|
+
value: draft,
|
|
6193
|
+
onChange: (e) => setDraft(e.currentTarget.value),
|
|
6194
|
+
onKeyDown: (e) => {
|
|
6195
|
+
if (e.key === "Enter") {
|
|
6196
|
+
e.preventDefault();
|
|
6197
|
+
commit();
|
|
6198
|
+
} else if (e.key === "Escape") {
|
|
6199
|
+
e.preventDefault();
|
|
6200
|
+
onCancel();
|
|
6201
|
+
}
|
|
6202
|
+
},
|
|
6203
|
+
onBlur: commit,
|
|
6204
|
+
placeholder,
|
|
6205
|
+
size: "xs",
|
|
6206
|
+
autoFocus: true,
|
|
6207
|
+
style: { flex: 1 }
|
|
6208
|
+
}
|
|
6209
|
+
),
|
|
6210
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
6211
|
+
core.ActionIcon,
|
|
6212
|
+
{
|
|
6213
|
+
variant: "subtle",
|
|
6214
|
+
color: "red",
|
|
6215
|
+
size: "md",
|
|
6216
|
+
onClick: onCancel,
|
|
6217
|
+
title: removeLabel,
|
|
6218
|
+
"aria-label": removeLabel,
|
|
6219
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Trash2, { size: "1.4vh" })
|
|
6220
|
+
}
|
|
6221
|
+
)
|
|
6222
|
+
] });
|
|
6223
|
+
}
|
|
5743
6224
|
|
|
5744
6225
|
// src/utils/gtaAnimPostFx.ts
|
|
5745
6226
|
var GTA_ANIM_POST_FX_GROUP_ORDER = [
|
|
@@ -6329,7 +6810,7 @@ function ScenarioSelect({
|
|
|
6329
6810
|
}
|
|
6330
6811
|
);
|
|
6331
6812
|
}
|
|
6332
|
-
var
|
|
6813
|
+
var t2 = (key, fallback) => {
|
|
6333
6814
|
const v = locale(key);
|
|
6334
6815
|
return v === key ? fallback : v;
|
|
6335
6816
|
};
|
|
@@ -6397,7 +6878,7 @@ function DiscordRoleSelect(props) {
|
|
|
6397
6878
|
return map;
|
|
6398
6879
|
}, [roles]);
|
|
6399
6880
|
if (errorCode === "NotConfigured") {
|
|
6400
|
-
const notConfiguredPlaceholder =
|
|
6881
|
+
const notConfiguredPlaceholder = t2(
|
|
6401
6882
|
"DiscordNotConfigured",
|
|
6402
6883
|
"Not configured \u2014 set in /dirk_lib"
|
|
6403
6884
|
);
|
|
@@ -6465,9 +6946,9 @@ function DiscordRoleSelect(props) {
|
|
|
6465
6946
|
e.stopPropagation();
|
|
6466
6947
|
refresh();
|
|
6467
6948
|
},
|
|
6468
|
-
title:
|
|
6949
|
+
title: t2("Refresh", "Refresh"),
|
|
6469
6950
|
loading,
|
|
6470
|
-
"aria-label":
|
|
6951
|
+
"aria-label": t2("Refresh", "Refresh"),
|
|
6471
6952
|
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.RefreshCw, { size: "1.2vh" })
|
|
6472
6953
|
}
|
|
6473
6954
|
);
|
|
@@ -6477,7 +6958,7 @@ function DiscordRoleSelect(props) {
|
|
|
6477
6958
|
{
|
|
6478
6959
|
label,
|
|
6479
6960
|
size,
|
|
6480
|
-
placeholder: placeholder ||
|
|
6961
|
+
placeholder: placeholder || t2("PickRoles", "Pick roles..."),
|
|
6481
6962
|
data,
|
|
6482
6963
|
value: props.value || [],
|
|
6483
6964
|
onChange: (ids) => props.onChange(ids),
|
|
@@ -6489,7 +6970,7 @@ function DiscordRoleSelect(props) {
|
|
|
6489
6970
|
rightSectionPointerEvents: "all",
|
|
6490
6971
|
style,
|
|
6491
6972
|
styles,
|
|
6492
|
-
nothingFoundMessage: errorCode ? `${
|
|
6973
|
+
nothingFoundMessage: errorCode ? `${t2("Error", "Error")} (${errorCode})` : t2("NoRoles", "No roles found")
|
|
6493
6974
|
}
|
|
6494
6975
|
);
|
|
6495
6976
|
}
|
|
@@ -6498,7 +6979,7 @@ function DiscordRoleSelect(props) {
|
|
|
6498
6979
|
{
|
|
6499
6980
|
label,
|
|
6500
6981
|
size,
|
|
6501
|
-
placeholder: placeholder ||
|
|
6982
|
+
placeholder: placeholder || t2("PickRole", "Pick a role..."),
|
|
6502
6983
|
data,
|
|
6503
6984
|
value: props.value || null,
|
|
6504
6985
|
onChange: (id) => props.onChange(id),
|
|
@@ -6510,11 +6991,11 @@ function DiscordRoleSelect(props) {
|
|
|
6510
6991
|
rightSectionPointerEvents: "all",
|
|
6511
6992
|
style,
|
|
6512
6993
|
styles,
|
|
6513
|
-
nothingFoundMessage: errorCode ? `${
|
|
6994
|
+
nothingFoundMessage: errorCode ? `${t2("Error", "Error")} (${errorCode})` : t2("NoRoles", "No roles found")
|
|
6514
6995
|
}
|
|
6515
6996
|
);
|
|
6516
6997
|
}
|
|
6517
|
-
var
|
|
6998
|
+
var t3 = (key, fallback) => {
|
|
6518
6999
|
const v = locale(key);
|
|
6519
7000
|
return v === key ? fallback : v;
|
|
6520
7001
|
};
|
|
@@ -6535,7 +7016,7 @@ function FrameworkHint({ framework }) {
|
|
|
6535
7016
|
const theme = core.useMantineTheme();
|
|
6536
7017
|
if (!framework) return null;
|
|
6537
7018
|
const hintKey = `AccountFrameworkHint_${framework}`;
|
|
6538
|
-
const text =
|
|
7019
|
+
const text = t3(hintKey, FRAMEWORK_HINTS[framework] || FRAMEWORK_HINTS.unknown);
|
|
6539
7020
|
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
6540
7021
|
core.Flex,
|
|
6541
7022
|
{
|
|
@@ -6619,9 +7100,9 @@ function AccountSelect(props) {
|
|
|
6619
7100
|
e.stopPropagation();
|
|
6620
7101
|
refresh();
|
|
6621
7102
|
},
|
|
6622
|
-
title:
|
|
7103
|
+
title: t3("Refresh", "Refresh"),
|
|
6623
7104
|
loading,
|
|
6624
|
-
"aria-label":
|
|
7105
|
+
"aria-label": t3("Refresh", "Refresh"),
|
|
6625
7106
|
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.RefreshCw, { size: "1.2vh" })
|
|
6626
7107
|
}
|
|
6627
7108
|
);
|
|
@@ -6635,14 +7116,14 @@ function AccountSelect(props) {
|
|
|
6635
7116
|
rightSection: refreshButton,
|
|
6636
7117
|
rightSectionPointerEvents: "all",
|
|
6637
7118
|
style,
|
|
6638
|
-
nothingFoundMessage:
|
|
7119
|
+
nothingFoundMessage: t3("NoAccounts", "No accounts available")
|
|
6639
7120
|
};
|
|
6640
7121
|
return /* @__PURE__ */ jsxRuntime.jsxs(core.Flex, { direction: "column", gap: "0.3vh", style, children: [
|
|
6641
7122
|
props.multi ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
6642
7123
|
core.MultiSelect,
|
|
6643
7124
|
{
|
|
6644
7125
|
...sharedSelectProps,
|
|
6645
|
-
placeholder: placeholder ||
|
|
7126
|
+
placeholder: placeholder || t3("PickAccounts", "Pick accounts (or type custom)..."),
|
|
6646
7127
|
value: props.value || [],
|
|
6647
7128
|
onChange: (names) => props.onChange(names),
|
|
6648
7129
|
renderOption,
|
|
@@ -6654,7 +7135,7 @@ function AccountSelect(props) {
|
|
|
6654
7135
|
core.Select,
|
|
6655
7136
|
{
|
|
6656
7137
|
...sharedSelectProps,
|
|
6657
|
-
placeholder: placeholder ||
|
|
7138
|
+
placeholder: placeholder || t3("PickAccount", "Pick an account..."),
|
|
6658
7139
|
value: props.value || null,
|
|
6659
7140
|
onChange: (name) => props.onChange(name),
|
|
6660
7141
|
renderOption,
|
|
@@ -6946,175 +7427,6 @@ function WorldPositionGotoButton2({
|
|
|
6946
7427
|
}
|
|
6947
7428
|
) });
|
|
6948
7429
|
}
|
|
6949
|
-
function usePlayers(opts = {}) {
|
|
6950
|
-
const {
|
|
6951
|
-
includeOffline = false,
|
|
6952
|
-
search = "",
|
|
6953
|
-
limit = 50,
|
|
6954
|
-
staleTimeMs,
|
|
6955
|
-
refetchIntervalMs
|
|
6956
|
-
} = opts;
|
|
6957
|
-
const query = reactQuery.useQuery({
|
|
6958
|
-
queryKey: includeOffline ? ["dirk:players", "search", search.trim().toLowerCase(), limit] : ["dirk:players", "online"],
|
|
6959
|
-
queryFn: async () => {
|
|
6960
|
-
const toolId = includeOffline ? "searchPlayers" : "getOnlinePlayers";
|
|
6961
|
-
const payload = includeOffline ? { id: toolId, value: { search: search.trim(), limit } } : { id: toolId };
|
|
6962
|
-
const result = await fetchNui(
|
|
6963
|
-
"ADMIN_TOOL_QUERY",
|
|
6964
|
-
payload,
|
|
6965
|
-
// Browser-dev fallback. Returns a couple of mock players so the
|
|
6966
|
-
// dev shell isn't blank.
|
|
6967
|
-
includeOffline ? [
|
|
6968
|
-
{ id: 1, citizenId: "ABC12345", name: "Dev User", charName: "John Doe", online: true },
|
|
6969
|
-
{ id: null, citizenId: "DEF67890", name: "", charName: "Jane Offline", online: false }
|
|
6970
|
-
] : [
|
|
6971
|
-
{ id: 1, citizenId: "ABC12345", name: "Dev User", charName: "John Doe", online: true }
|
|
6972
|
-
]
|
|
6973
|
-
);
|
|
6974
|
-
return Array.isArray(result) ? result : [];
|
|
6975
|
-
},
|
|
6976
|
-
staleTime: staleTimeMs ?? (includeOffline ? 3e4 : 5e3),
|
|
6977
|
-
gcTime: 5 * 6e4,
|
|
6978
|
-
refetchInterval: refetchIntervalMs ?? false,
|
|
6979
|
-
refetchOnWindowFocus: false,
|
|
6980
|
-
placeholderData: includeOffline ? reactQuery.keepPreviousData : void 0
|
|
6981
|
-
});
|
|
6982
|
-
return {
|
|
6983
|
-
players: query.data ?? [],
|
|
6984
|
-
isLoading: query.isLoading,
|
|
6985
|
-
isFetching: query.isFetching,
|
|
6986
|
-
error: query.error ?? null,
|
|
6987
|
-
refresh: () => query.refetch()
|
|
6988
|
-
};
|
|
6989
|
-
}
|
|
6990
|
-
var DEBOUNCE_MS = 300;
|
|
6991
|
-
var ONLINE_COLOR = "#3FA83F";
|
|
6992
|
-
var OFFLINE_COLOR = "#E54141";
|
|
6993
|
-
function PlayerSelect({
|
|
6994
|
-
value,
|
|
6995
|
-
onChange,
|
|
6996
|
-
includeOffline = false,
|
|
6997
|
-
limit = 50,
|
|
6998
|
-
searchable: searchableProp,
|
|
6999
|
-
placeholder,
|
|
7000
|
-
nothingFoundMessage: nothingFoundMessageProp,
|
|
7001
|
-
loadingLabel = "Loading\u2026",
|
|
7002
|
-
onlineLabel = "Online",
|
|
7003
|
-
offlineLabel = "Offline",
|
|
7004
|
-
refreshLabel = "Refresh player list",
|
|
7005
|
-
...rest
|
|
7006
|
-
}) {
|
|
7007
|
-
const theme = core.useMantineTheme();
|
|
7008
|
-
const color = theme.colors[theme.primaryColor][5];
|
|
7009
|
-
const [searchInput, setSearchInput] = react.useState("");
|
|
7010
|
-
const [debouncedSearch, setDebouncedSearch] = react.useState("");
|
|
7011
|
-
const { players, isFetching, refresh } = usePlayers({
|
|
7012
|
-
includeOffline,
|
|
7013
|
-
search: includeOffline ? debouncedSearch : void 0,
|
|
7014
|
-
limit
|
|
7015
|
-
});
|
|
7016
|
-
const sortedPlayers = react.useMemo(
|
|
7017
|
-
() => [...players].sort((a, b) => {
|
|
7018
|
-
if (a.online !== b.online) return a.online ? -1 : 1;
|
|
7019
|
-
return a.charName.localeCompare(b.charName);
|
|
7020
|
-
}),
|
|
7021
|
-
[players]
|
|
7022
|
-
);
|
|
7023
|
-
const playerByCitizen = react.useMemo(() => {
|
|
7024
|
-
const m = /* @__PURE__ */ new Map();
|
|
7025
|
-
for (const p of sortedPlayers) m.set(p.citizenId, p);
|
|
7026
|
-
return m;
|
|
7027
|
-
}, [sortedPlayers]);
|
|
7028
|
-
const data = react.useMemo(() => {
|
|
7029
|
-
const items = sortedPlayers.map((p) => ({
|
|
7030
|
-
value: p.citizenId,
|
|
7031
|
-
label: formatLabel(p)
|
|
7032
|
-
}));
|
|
7033
|
-
if (value && !items.some((i) => i.value === value)) {
|
|
7034
|
-
items.unshift({ value, label: value });
|
|
7035
|
-
}
|
|
7036
|
-
return items;
|
|
7037
|
-
}, [sortedPlayers, value]);
|
|
7038
|
-
const selectedPlayer = value ? playerByCitizen.get(value) ?? null : null;
|
|
7039
|
-
const selectedLabel = selectedPlayer ? formatLabel(selectedPlayer) : null;
|
|
7040
|
-
react.useEffect(() => {
|
|
7041
|
-
if (!includeOffline) return;
|
|
7042
|
-
if (selectedLabel && searchInput === selectedLabel) return;
|
|
7043
|
-
const t3 = setTimeout(() => setDebouncedSearch(searchInput), DEBOUNCE_MS);
|
|
7044
|
-
return () => clearTimeout(t3);
|
|
7045
|
-
}, [searchInput, includeOffline, selectedLabel]);
|
|
7046
|
-
const renderOption = ({ option }) => {
|
|
7047
|
-
const p = playerByCitizen.get(option.value);
|
|
7048
|
-
if (!p) return option.label;
|
|
7049
|
-
return /* @__PURE__ */ jsxRuntime.jsxs(core.Group, { justify: "space-between", wrap: "nowrap", gap: "xs", style: { width: "100%" }, children: [
|
|
7050
|
-
/* @__PURE__ */ jsxRuntime.jsx(core.Text, { size: "xs", truncate: true, style: { flex: 1 }, children: formatLabel(p) }),
|
|
7051
|
-
/* @__PURE__ */ jsxRuntime.jsx(StatusDot, { online: p.online, onlineLabel, offlineLabel })
|
|
7052
|
-
] });
|
|
7053
|
-
};
|
|
7054
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
7055
|
-
core.Select,
|
|
7056
|
-
{
|
|
7057
|
-
...rest,
|
|
7058
|
-
data,
|
|
7059
|
-
value: value ?? null,
|
|
7060
|
-
onChange: (v) => {
|
|
7061
|
-
if (!v) return onChange(null);
|
|
7062
|
-
const player = playerByCitizen.get(v) ?? null;
|
|
7063
|
-
onChange(player);
|
|
7064
|
-
},
|
|
7065
|
-
searchable: searchableProp ?? true,
|
|
7066
|
-
searchValue: includeOffline ? searchInput : void 0,
|
|
7067
|
-
onSearchChange: includeOffline ? setSearchInput : void 0,
|
|
7068
|
-
placeholder,
|
|
7069
|
-
nothingFoundMessage: isFetching ? loadingLabel : nothingFoundMessageProp ?? "No players found",
|
|
7070
|
-
maxDropdownHeight: 300,
|
|
7071
|
-
renderOption,
|
|
7072
|
-
leftSection: selectedPlayer ? /* @__PURE__ */ jsxRuntime.jsx(StatusDot, { online: selectedPlayer.online, onlineLabel, offlineLabel }) : void 0,
|
|
7073
|
-
rightSectionWidth: "3.5vh",
|
|
7074
|
-
rightSectionPointerEvents: "all",
|
|
7075
|
-
rightSection: /* @__PURE__ */ jsxRuntime.jsx(
|
|
7076
|
-
core.ActionIcon,
|
|
7077
|
-
{
|
|
7078
|
-
variant: "subtle",
|
|
7079
|
-
size: "sm",
|
|
7080
|
-
onClick: (e) => {
|
|
7081
|
-
e.stopPropagation();
|
|
7082
|
-
refresh();
|
|
7083
|
-
},
|
|
7084
|
-
"aria-label": refreshLabel,
|
|
7085
|
-
title: refreshLabel,
|
|
7086
|
-
style: { marginRight: "0.6vh" },
|
|
7087
|
-
children: isFetching ? /* @__PURE__ */ jsxRuntime.jsx(core.Loader, { size: "1.2vh", color }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.RefreshCw, { size: "1.4vh", color })
|
|
7088
|
-
}
|
|
7089
|
-
)
|
|
7090
|
-
}
|
|
7091
|
-
);
|
|
7092
|
-
}
|
|
7093
|
-
function StatusDot({ online, onlineLabel, offlineLabel }) {
|
|
7094
|
-
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
7095
|
-
"span",
|
|
7096
|
-
{
|
|
7097
|
-
title: online ? onlineLabel : offlineLabel,
|
|
7098
|
-
style: {
|
|
7099
|
-
display: "inline-block",
|
|
7100
|
-
width: "0.9vh",
|
|
7101
|
-
height: "0.9vh",
|
|
7102
|
-
borderRadius: "50%",
|
|
7103
|
-
backgroundColor: online ? ONLINE_COLOR : OFFLINE_COLOR,
|
|
7104
|
-
boxShadow: `0 0 0.4vh ${online ? ONLINE_COLOR : OFFLINE_COLOR}`,
|
|
7105
|
-
flexShrink: 0
|
|
7106
|
-
}
|
|
7107
|
-
}
|
|
7108
|
-
);
|
|
7109
|
-
}
|
|
7110
|
-
function formatLabel(p) {
|
|
7111
|
-
const parts = [p.charName || p.citizenId];
|
|
7112
|
-
if (p.online) {
|
|
7113
|
-
if (p.name) parts.push(p.name);
|
|
7114
|
-
if (p.id != null) parts.push(`#${p.id}`);
|
|
7115
|
-
}
|
|
7116
|
-
return parts.join(" \xB7 ");
|
|
7117
|
-
}
|
|
7118
7430
|
function usePickDoor() {
|
|
7119
7431
|
const begin = useAdminToolStore((s) => s.begin);
|
|
7120
7432
|
return async () => {
|
|
@@ -7391,6 +7703,7 @@ function BlipMarker({
|
|
|
7391
7703
|
);
|
|
7392
7704
|
}
|
|
7393
7705
|
|
|
7706
|
+
exports.AccessOverrideSection = AccessOverrideSection;
|
|
7394
7707
|
exports.AccountSelect = AccountSelect;
|
|
7395
7708
|
exports.AdminPageTitle = AdminPageTitle;
|
|
7396
7709
|
exports.AnimPostFxSelect = AnimPostFxSelect;
|