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
package/dist/components/index.js
CHANGED
|
@@ -2,9 +2,10 @@ import { Flex, Text, Image, TextInput, Select, Box, useMantineTheme, Tooltip, al
|
|
|
2
2
|
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
3
3
|
import { createContext, memo, useRef, useEffect, useContext, useState, useCallback, useMemo } from 'react';
|
|
4
4
|
import { create, useStore, createStore } from 'zustand';
|
|
5
|
+
import { z } from 'zod';
|
|
5
6
|
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
|
6
7
|
import { motion, AnimatePresence, useMotionValue } from 'framer-motion';
|
|
7
|
-
import { Info, X, AlertTriangle, Trash2, RefreshCw, ChevronDown, Check, Copy, MapPin, Crosshair, EyeOff, Eye, RotateCcw, FlaskConical, ChevronUp, Palette,
|
|
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';
|
|
8
9
|
import clickSoundUrl from '../click_sound-PNCRRTM4.mp3';
|
|
9
10
|
import hoverSoundUrl from '../hover_sound-NBUA222C.mp3';
|
|
10
11
|
import { notifications } from '@mantine/notifications';
|
|
@@ -1257,11 +1258,11 @@ var colorNames = {
|
|
|
1257
1258
|
Yellow: { r: 255, g: 255, b: 0 },
|
|
1258
1259
|
YellowGreen: { r: 154, g: 205, b: 50 }
|
|
1259
1260
|
};
|
|
1260
|
-
function colorWithAlpha(color,
|
|
1261
|
+
function colorWithAlpha(color, alpha18) {
|
|
1261
1262
|
const lowerCasedColor = color.toLowerCase();
|
|
1262
1263
|
if (colorNames[lowerCasedColor]) {
|
|
1263
1264
|
const rgb = colorNames[lowerCasedColor];
|
|
1264
|
-
return `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, ${
|
|
1265
|
+
return `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, ${alpha18})`;
|
|
1265
1266
|
}
|
|
1266
1267
|
if (/^#([A-Fa-f0-9]{6})$/.test(color)) {
|
|
1267
1268
|
const hex = color.slice(1);
|
|
@@ -1269,12 +1270,12 @@ function colorWithAlpha(color, alpha17) {
|
|
|
1269
1270
|
const r = bigint >> 16 & 255;
|
|
1270
1271
|
const g = bigint >> 8 & 255;
|
|
1271
1272
|
const b = bigint & 255;
|
|
1272
|
-
return `rgba(${r}, ${g}, ${b}, ${
|
|
1273
|
+
return `rgba(${r}, ${g}, ${b}, ${alpha18})`;
|
|
1273
1274
|
}
|
|
1274
1275
|
if (/^rgb\((\d{1,3}), (\d{1,3}), (\d{1,3})\)$/.test(color)) {
|
|
1275
1276
|
const result = color.match(/^rgb\((\d{1,3}), (\d{1,3}), (\d{1,3})\)$/);
|
|
1276
1277
|
if (result) {
|
|
1277
|
-
return `rgba(${result[1]}, ${result[2]}, ${result[3]}, ${
|
|
1278
|
+
return `rgba(${result[1]}, ${result[2]}, ${result[3]}, ${alpha18})`;
|
|
1278
1279
|
}
|
|
1279
1280
|
}
|
|
1280
1281
|
return color;
|
|
@@ -1282,6 +1283,32 @@ function colorWithAlpha(color, alpha17) {
|
|
|
1282
1283
|
|
|
1283
1284
|
// src/utils/misc.ts
|
|
1284
1285
|
var isEnvBrowser = () => !window.invokeNative;
|
|
1286
|
+
var DEFAULT_PALETTE = [
|
|
1287
|
+
"#f0f4ff",
|
|
1288
|
+
"#d9e3ff",
|
|
1289
|
+
"#bfcfff",
|
|
1290
|
+
"#a6bbff",
|
|
1291
|
+
"#8ca7ff",
|
|
1292
|
+
"#7393ff",
|
|
1293
|
+
"#5a7fff",
|
|
1294
|
+
"#406bff",
|
|
1295
|
+
"#2547ff",
|
|
1296
|
+
"#0b33ff"
|
|
1297
|
+
];
|
|
1298
|
+
z.object({
|
|
1299
|
+
useOverride: z.boolean(),
|
|
1300
|
+
primaryColor: z.string(),
|
|
1301
|
+
primaryShade: z.number(),
|
|
1302
|
+
customTheme: z.array(z.string())
|
|
1303
|
+
});
|
|
1304
|
+
var defaultThemeOverride = {
|
|
1305
|
+
useOverride: false,
|
|
1306
|
+
primaryColor: "dirk",
|
|
1307
|
+
primaryShade: 5,
|
|
1308
|
+
customTheme: DEFAULT_PALETTE
|
|
1309
|
+
};
|
|
1310
|
+
|
|
1311
|
+
// src/utils/useSettings.ts
|
|
1285
1312
|
var useSettings = create(() => ({
|
|
1286
1313
|
currency: "$",
|
|
1287
1314
|
game: "fivem",
|
|
@@ -1289,18 +1316,7 @@ var useSettings = create(() => ({
|
|
|
1289
1316
|
primaryShade: 9,
|
|
1290
1317
|
itemImgPath: "",
|
|
1291
1318
|
resourceVersion: "dev",
|
|
1292
|
-
customTheme:
|
|
1293
|
-
"#f0f4ff",
|
|
1294
|
-
"#d9e3ff",
|
|
1295
|
-
"#bfcfff",
|
|
1296
|
-
"#a6bbff",
|
|
1297
|
-
"#8ca7ff",
|
|
1298
|
-
"#7393ff",
|
|
1299
|
-
"#5a7fff",
|
|
1300
|
-
"#406bff",
|
|
1301
|
-
"#2547ff",
|
|
1302
|
-
"#0b33ff"
|
|
1303
|
-
]
|
|
1319
|
+
customTheme: DEFAULT_PALETTE
|
|
1304
1320
|
}));
|
|
1305
1321
|
|
|
1306
1322
|
// src/utils/fetchNui.ts
|
|
@@ -3154,11 +3170,11 @@ function ConfirmModal({
|
|
|
3154
3170
|
placeholder: confirmText,
|
|
3155
3171
|
value: typed,
|
|
3156
3172
|
onChange: (e) => setTyped(e.currentTarget.value),
|
|
3157
|
-
styles: (
|
|
3173
|
+
styles: (t4) => ({
|
|
3158
3174
|
input: {
|
|
3159
|
-
backgroundColor: alpha(
|
|
3175
|
+
backgroundColor: alpha(t4.colors.dark[7], 0.5),
|
|
3160
3176
|
border: `0.1vh solid ${alpha(
|
|
3161
|
-
typed === confirmText ? "#ef4444" :
|
|
3177
|
+
typed === confirmText ? "#ef4444" : t4.colors.dark[5],
|
|
3162
3178
|
0.5
|
|
3163
3179
|
)}`,
|
|
3164
3180
|
color: "rgba(255,255,255,0.85)",
|
|
@@ -3803,7 +3819,7 @@ function useAdminState(key, initial) {
|
|
|
3803
3819
|
useEffect(() => {
|
|
3804
3820
|
const set = listeners.get(key) ?? /* @__PURE__ */ new Set();
|
|
3805
3821
|
listeners.set(key, set);
|
|
3806
|
-
const handler = () => setTick((
|
|
3822
|
+
const handler = () => setTick((t4) => t4 + 1);
|
|
3807
3823
|
set.add(handler);
|
|
3808
3824
|
return () => {
|
|
3809
3825
|
set.delete(handler);
|
|
@@ -4457,7 +4473,7 @@ function ConfigPanel(props) {
|
|
|
4457
4473
|
if (isSaving) return;
|
|
4458
4474
|
setIsSaving(true);
|
|
4459
4475
|
try {
|
|
4460
|
-
const result = await updateConfig(form.values);
|
|
4476
|
+
const result = await updateConfig(form.values, form.changedFields);
|
|
4461
4477
|
if (result?.success) {
|
|
4462
4478
|
form.reinitialize(cloneConfig(form.values));
|
|
4463
4479
|
dirkQueryClient.invalidateQueries({ queryKey: ["scriptConfigHistory"] });
|
|
@@ -5491,24 +5507,6 @@ var MANTINE_COLOR_OPTIONS = [
|
|
|
5491
5507
|
"yellow",
|
|
5492
5508
|
"orange"
|
|
5493
5509
|
].map((value) => ({ value, label: value }));
|
|
5494
|
-
var DEFAULT_PALETTE = [
|
|
5495
|
-
"#f0f4ff",
|
|
5496
|
-
"#d9e3ff",
|
|
5497
|
-
"#bfcfff",
|
|
5498
|
-
"#a6bbff",
|
|
5499
|
-
"#8ca7ff",
|
|
5500
|
-
"#7393ff",
|
|
5501
|
-
"#5a7fff",
|
|
5502
|
-
"#406bff",
|
|
5503
|
-
"#2547ff",
|
|
5504
|
-
"#0b33ff"
|
|
5505
|
-
];
|
|
5506
|
-
var DEFAULT_VALUE = {
|
|
5507
|
-
useOverride: false,
|
|
5508
|
-
primaryColor: "dirk",
|
|
5509
|
-
primaryShade: 5,
|
|
5510
|
-
customTheme: DEFAULT_PALETTE
|
|
5511
|
-
};
|
|
5512
5510
|
function GroupLabel({ label }) {
|
|
5513
5511
|
return /* @__PURE__ */ jsxs(Flex, { align: "center", gap: "xs", mt: "xxs", children: [
|
|
5514
5512
|
/* @__PURE__ */ jsx(Text, { ff: "Akrobat Bold", size: "xxs", tt: "uppercase", lts: "0.07em", c: "rgba(255,255,255,0.2)", children: label }),
|
|
@@ -5523,10 +5521,10 @@ function ThemeOverrideSection({
|
|
|
5523
5521
|
const color = mantineTheme.colors[mantineTheme.primaryColor][5];
|
|
5524
5522
|
const raw = useFormField(schemaKey);
|
|
5525
5523
|
const value = {
|
|
5526
|
-
useOverride: raw?.useOverride ??
|
|
5527
|
-
primaryColor: raw?.primaryColor ??
|
|
5528
|
-
primaryShade: raw?.primaryShade ??
|
|
5529
|
-
customTheme: Array.isArray(raw?.customTheme) && raw.customTheme.length === 10 ? raw.customTheme :
|
|
5524
|
+
useOverride: raw?.useOverride ?? defaultThemeOverride.useOverride,
|
|
5525
|
+
primaryColor: raw?.primaryColor ?? defaultThemeOverride.primaryColor,
|
|
5526
|
+
primaryShade: raw?.primaryShade ?? defaultThemeOverride.primaryShade,
|
|
5527
|
+
customTheme: Array.isArray(raw?.customTheme) && raw.customTheme.length === 10 ? raw.customTheme : defaultThemeOverride.customTheme
|
|
5530
5528
|
};
|
|
5531
5529
|
const { setValue } = useFormActions();
|
|
5532
5530
|
const set = (key, val) => setValue(schemaKey, { ...value, [key]: val });
|
|
@@ -5733,6 +5731,489 @@ function SwatchTile({
|
|
|
5733
5731
|
) })
|
|
5734
5732
|
] });
|
|
5735
5733
|
}
|
|
5734
|
+
function usePlayers(opts = {}) {
|
|
5735
|
+
const {
|
|
5736
|
+
includeOffline = false,
|
|
5737
|
+
search = "",
|
|
5738
|
+
limit = 50,
|
|
5739
|
+
staleTimeMs,
|
|
5740
|
+
refetchIntervalMs
|
|
5741
|
+
} = opts;
|
|
5742
|
+
const query = useQuery({
|
|
5743
|
+
queryKey: includeOffline ? ["dirk:players", "search", search.trim().toLowerCase(), limit] : ["dirk:players", "online"],
|
|
5744
|
+
queryFn: async () => {
|
|
5745
|
+
const toolId = includeOffline ? "searchPlayers" : "getOnlinePlayers";
|
|
5746
|
+
const payload = includeOffline ? { id: toolId, value: { search: search.trim(), limit } } : { id: toolId };
|
|
5747
|
+
const result = await fetchNui(
|
|
5748
|
+
"ADMIN_TOOL_QUERY",
|
|
5749
|
+
payload,
|
|
5750
|
+
// Browser-dev fallback. Returns a couple of mock players so the
|
|
5751
|
+
// dev shell isn't blank.
|
|
5752
|
+
includeOffline ? [
|
|
5753
|
+
{ id: 1, citizenId: "ABC12345", name: "Dev User", charName: "John Doe", online: true },
|
|
5754
|
+
{ id: null, citizenId: "DEF67890", name: "", charName: "Jane Offline", online: false }
|
|
5755
|
+
] : [
|
|
5756
|
+
{ id: 1, citizenId: "ABC12345", name: "Dev User", charName: "John Doe", online: true }
|
|
5757
|
+
]
|
|
5758
|
+
);
|
|
5759
|
+
return Array.isArray(result) ? result : [];
|
|
5760
|
+
},
|
|
5761
|
+
staleTime: staleTimeMs ?? (includeOffline ? 3e4 : 5e3),
|
|
5762
|
+
gcTime: 5 * 6e4,
|
|
5763
|
+
refetchInterval: refetchIntervalMs ?? false,
|
|
5764
|
+
refetchOnWindowFocus: false,
|
|
5765
|
+
placeholderData: includeOffline ? keepPreviousData : void 0
|
|
5766
|
+
});
|
|
5767
|
+
return {
|
|
5768
|
+
players: query.data ?? [],
|
|
5769
|
+
isLoading: query.isLoading,
|
|
5770
|
+
isFetching: query.isFetching,
|
|
5771
|
+
error: query.error ?? null,
|
|
5772
|
+
refresh: () => query.refetch()
|
|
5773
|
+
};
|
|
5774
|
+
}
|
|
5775
|
+
var DEBOUNCE_MS = 300;
|
|
5776
|
+
var ONLINE_COLOR = "#3FA83F";
|
|
5777
|
+
var OFFLINE_COLOR = "#E54141";
|
|
5778
|
+
function PlayerSelect({
|
|
5779
|
+
value,
|
|
5780
|
+
onChange,
|
|
5781
|
+
includeOffline = false,
|
|
5782
|
+
limit = 50,
|
|
5783
|
+
searchable: searchableProp,
|
|
5784
|
+
placeholder,
|
|
5785
|
+
nothingFoundMessage: nothingFoundMessageProp,
|
|
5786
|
+
loadingLabel = "Loading\u2026",
|
|
5787
|
+
onlineLabel = "Online",
|
|
5788
|
+
offlineLabel = "Offline",
|
|
5789
|
+
refreshLabel = "Refresh player list",
|
|
5790
|
+
...rest
|
|
5791
|
+
}) {
|
|
5792
|
+
const theme = useMantineTheme();
|
|
5793
|
+
const color = theme.colors[theme.primaryColor][5];
|
|
5794
|
+
const [searchInput, setSearchInput] = useState("");
|
|
5795
|
+
const [debouncedSearch, setDebouncedSearch] = useState("");
|
|
5796
|
+
const { players, isFetching, refresh } = usePlayers({
|
|
5797
|
+
includeOffline,
|
|
5798
|
+
search: includeOffline ? debouncedSearch : void 0,
|
|
5799
|
+
limit
|
|
5800
|
+
});
|
|
5801
|
+
const sortedPlayers = useMemo(
|
|
5802
|
+
() => [...players].sort((a, b) => {
|
|
5803
|
+
if (a.online !== b.online) return a.online ? -1 : 1;
|
|
5804
|
+
return a.charName.localeCompare(b.charName);
|
|
5805
|
+
}),
|
|
5806
|
+
[players]
|
|
5807
|
+
);
|
|
5808
|
+
const playerByCitizen = useMemo(() => {
|
|
5809
|
+
const m = /* @__PURE__ */ new Map();
|
|
5810
|
+
for (const p of sortedPlayers) m.set(p.citizenId, p);
|
|
5811
|
+
return m;
|
|
5812
|
+
}, [sortedPlayers]);
|
|
5813
|
+
const data = useMemo(() => {
|
|
5814
|
+
const items = sortedPlayers.map((p) => ({
|
|
5815
|
+
value: p.citizenId,
|
|
5816
|
+
label: formatLabel(p)
|
|
5817
|
+
}));
|
|
5818
|
+
if (value && !items.some((i) => i.value === value)) {
|
|
5819
|
+
items.unshift({ value, label: value });
|
|
5820
|
+
}
|
|
5821
|
+
return items;
|
|
5822
|
+
}, [sortedPlayers, value]);
|
|
5823
|
+
const selectedPlayer = value ? playerByCitizen.get(value) ?? null : null;
|
|
5824
|
+
const selectedLabel = selectedPlayer ? formatLabel(selectedPlayer) : null;
|
|
5825
|
+
useEffect(() => {
|
|
5826
|
+
if (!includeOffline) return;
|
|
5827
|
+
if (selectedLabel && searchInput === selectedLabel) return;
|
|
5828
|
+
const t4 = setTimeout(() => setDebouncedSearch(searchInput), DEBOUNCE_MS);
|
|
5829
|
+
return () => clearTimeout(t4);
|
|
5830
|
+
}, [searchInput, includeOffline, selectedLabel]);
|
|
5831
|
+
const renderOption = ({ option }) => {
|
|
5832
|
+
const p = playerByCitizen.get(option.value);
|
|
5833
|
+
if (!p) return option.label;
|
|
5834
|
+
return /* @__PURE__ */ jsxs(Group, { justify: "space-between", wrap: "nowrap", gap: "xs", style: { width: "100%" }, children: [
|
|
5835
|
+
/* @__PURE__ */ jsx(Text, { size: "xs", truncate: true, style: { flex: 1 }, children: formatLabel(p) }),
|
|
5836
|
+
/* @__PURE__ */ jsx(StatusDot, { online: p.online, onlineLabel, offlineLabel })
|
|
5837
|
+
] });
|
|
5838
|
+
};
|
|
5839
|
+
return /* @__PURE__ */ jsx(
|
|
5840
|
+
Select,
|
|
5841
|
+
{
|
|
5842
|
+
...rest,
|
|
5843
|
+
data,
|
|
5844
|
+
value: value ?? null,
|
|
5845
|
+
onChange: (v) => {
|
|
5846
|
+
if (!v) return onChange(null);
|
|
5847
|
+
const player = playerByCitizen.get(v) ?? null;
|
|
5848
|
+
onChange(player);
|
|
5849
|
+
},
|
|
5850
|
+
searchable: searchableProp ?? true,
|
|
5851
|
+
searchValue: includeOffline ? searchInput : void 0,
|
|
5852
|
+
onSearchChange: includeOffline ? setSearchInput : void 0,
|
|
5853
|
+
placeholder,
|
|
5854
|
+
nothingFoundMessage: isFetching ? loadingLabel : nothingFoundMessageProp ?? "No players found",
|
|
5855
|
+
maxDropdownHeight: 300,
|
|
5856
|
+
renderOption,
|
|
5857
|
+
leftSection: selectedPlayer ? /* @__PURE__ */ jsx(StatusDot, { online: selectedPlayer.online, onlineLabel, offlineLabel }) : void 0,
|
|
5858
|
+
rightSectionWidth: "3.5vh",
|
|
5859
|
+
rightSectionPointerEvents: "all",
|
|
5860
|
+
rightSection: /* @__PURE__ */ jsx(
|
|
5861
|
+
ActionIcon,
|
|
5862
|
+
{
|
|
5863
|
+
variant: "subtle",
|
|
5864
|
+
size: "sm",
|
|
5865
|
+
onClick: (e) => {
|
|
5866
|
+
e.stopPropagation();
|
|
5867
|
+
refresh();
|
|
5868
|
+
},
|
|
5869
|
+
"aria-label": refreshLabel,
|
|
5870
|
+
title: refreshLabel,
|
|
5871
|
+
style: { marginRight: "0.6vh" },
|
|
5872
|
+
children: isFetching ? /* @__PURE__ */ jsx(Loader, { size: "1.2vh", color }) : /* @__PURE__ */ jsx(RefreshCw, { size: "1.4vh", color })
|
|
5873
|
+
}
|
|
5874
|
+
)
|
|
5875
|
+
}
|
|
5876
|
+
);
|
|
5877
|
+
}
|
|
5878
|
+
function StatusDot({ online, onlineLabel, offlineLabel }) {
|
|
5879
|
+
return /* @__PURE__ */ jsx(
|
|
5880
|
+
"span",
|
|
5881
|
+
{
|
|
5882
|
+
title: online ? onlineLabel : offlineLabel,
|
|
5883
|
+
style: {
|
|
5884
|
+
display: "inline-block",
|
|
5885
|
+
width: "0.9vh",
|
|
5886
|
+
height: "0.9vh",
|
|
5887
|
+
borderRadius: "50%",
|
|
5888
|
+
backgroundColor: online ? ONLINE_COLOR : OFFLINE_COLOR,
|
|
5889
|
+
boxShadow: `0 0 0.4vh ${online ? ONLINE_COLOR : OFFLINE_COLOR}`,
|
|
5890
|
+
flexShrink: 0
|
|
5891
|
+
}
|
|
5892
|
+
}
|
|
5893
|
+
);
|
|
5894
|
+
}
|
|
5895
|
+
function formatLabel(p) {
|
|
5896
|
+
const parts = [p.charName || p.citizenId];
|
|
5897
|
+
if (p.online) {
|
|
5898
|
+
if (p.name) parts.push(p.name);
|
|
5899
|
+
if (p.id != null) parts.push(`#${p.id}`);
|
|
5900
|
+
}
|
|
5901
|
+
return parts.join(" \xB7 ");
|
|
5902
|
+
}
|
|
5903
|
+
var COMMON_ACE_GROUPS = ["group.admin", "group.mod", "group.superadmin"];
|
|
5904
|
+
var t = (key, fallback) => {
|
|
5905
|
+
const v = locale(key);
|
|
5906
|
+
return v === key ? fallback : v;
|
|
5907
|
+
};
|
|
5908
|
+
var DEFAULT_VALUE = {
|
|
5909
|
+
groups: [],
|
|
5910
|
+
identifiers: []
|
|
5911
|
+
};
|
|
5912
|
+
function SectionLabel({ icon: Icon, label }) {
|
|
5913
|
+
return /* @__PURE__ */ jsxs(Flex, { align: "center", gap: "xs", mt: "xs", children: [
|
|
5914
|
+
/* @__PURE__ */ jsx(Icon, { size: "1.4vh", color: "rgba(255,255,255,0.3)" }),
|
|
5915
|
+
/* @__PURE__ */ jsx(Text, { ff: "Akrobat Bold", size: "xxs", tt: "uppercase", lts: "0.07em", c: "rgba(255,255,255,0.2)", children: label }),
|
|
5916
|
+
/* @__PURE__ */ jsx("div", { style: { flex: 1, height: "0.05vh", background: "rgba(255,255,255,0.06)" } })
|
|
5917
|
+
] });
|
|
5918
|
+
}
|
|
5919
|
+
function EmptyHint({ text }) {
|
|
5920
|
+
return /* @__PURE__ */ jsx(Text, { ff: "Akrobat Bold", size: "xxs", c: "rgba(255,255,255,0.25)", ta: "center", py: "xs", children: text });
|
|
5921
|
+
}
|
|
5922
|
+
function AccessOverrideSection({
|
|
5923
|
+
schemaKey = "access",
|
|
5924
|
+
title,
|
|
5925
|
+
description,
|
|
5926
|
+
includeOffline = true
|
|
5927
|
+
}) {
|
|
5928
|
+
const mantineTheme = useMantineTheme();
|
|
5929
|
+
const color = mantineTheme.colors[mantineTheme.primaryColor][5];
|
|
5930
|
+
const raw = useFormField(schemaKey);
|
|
5931
|
+
const value = {
|
|
5932
|
+
groups: Array.isArray(raw?.groups) ? raw.groups : DEFAULT_VALUE.groups,
|
|
5933
|
+
identifiers: Array.isArray(raw?.identifiers) ? raw.identifiers : DEFAULT_VALUE.identifiers
|
|
5934
|
+
};
|
|
5935
|
+
const { setValue } = useFormActions();
|
|
5936
|
+
const set = (key, val) => setValue(schemaKey, { ...value, [key]: val });
|
|
5937
|
+
const [addingGroup, setAddingGroup] = useState(false);
|
|
5938
|
+
const [addingPlayer, setAddingPlayer] = useState(false);
|
|
5939
|
+
const commitGroup = (name) => {
|
|
5940
|
+
const g = name.trim();
|
|
5941
|
+
if (!g) return;
|
|
5942
|
+
if (!value.groups.includes(g)) set("groups", [...value.groups, g]);
|
|
5943
|
+
setAddingGroup(false);
|
|
5944
|
+
};
|
|
5945
|
+
const setGroup = (index, name) => {
|
|
5946
|
+
const next = [...value.groups];
|
|
5947
|
+
next[index] = name;
|
|
5948
|
+
set("groups", next);
|
|
5949
|
+
};
|
|
5950
|
+
const quickAddGroup = (name) => {
|
|
5951
|
+
if (value.groups.includes(name)) return;
|
|
5952
|
+
set("groups", [...value.groups, name]);
|
|
5953
|
+
};
|
|
5954
|
+
const removeGroup = (index) => set("groups", value.groups.filter((_, i) => i !== index));
|
|
5955
|
+
const commitIdentifier = (id) => {
|
|
5956
|
+
if (!id) return;
|
|
5957
|
+
set("identifiers", [...value.identifiers, id]);
|
|
5958
|
+
setAddingPlayer(false);
|
|
5959
|
+
};
|
|
5960
|
+
const setIdentifier = (index, id) => {
|
|
5961
|
+
const next = [...value.identifiers];
|
|
5962
|
+
next[index] = id;
|
|
5963
|
+
set("identifiers", next.filter((x) => x !== ""));
|
|
5964
|
+
};
|
|
5965
|
+
const removeIdentifier = (index) => set("identifiers", value.identifiers.filter((_, i) => i !== index));
|
|
5966
|
+
return /* @__PURE__ */ jsxs(
|
|
5967
|
+
Flex,
|
|
5968
|
+
{
|
|
5969
|
+
direction: "column",
|
|
5970
|
+
gap: "xs",
|
|
5971
|
+
p: "sm",
|
|
5972
|
+
style: { flex: 1, minHeight: 0, overflowY: "auto" },
|
|
5973
|
+
children: [
|
|
5974
|
+
/* @__PURE__ */ jsx(
|
|
5975
|
+
AdminPageTitle,
|
|
5976
|
+
{
|
|
5977
|
+
icon: ShieldCheck,
|
|
5978
|
+
title: title || t("Access", "Access"),
|
|
5979
|
+
color
|
|
5980
|
+
}
|
|
5981
|
+
),
|
|
5982
|
+
/* @__PURE__ */ jsx(Text, { ff: "Akrobat Bold", size: "xxs", c: "rgba(255,255,255,0.4)", children: description || t(
|
|
5983
|
+
"AccessOverrideDesc",
|
|
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."
|
|
5985
|
+
) }),
|
|
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
|
+
}) }),
|
|
6031
|
+
/* @__PURE__ */ jsxs(Flex, { direction: "column", gap: "xxs", children: [
|
|
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
|
+
),
|
|
6038
|
+
value.groups.map((name, i) => /* @__PURE__ */ jsxs(Flex, { align: "center", gap: "xs", children: [
|
|
6039
|
+
/* @__PURE__ */ jsx(
|
|
6040
|
+
TextInput,
|
|
6041
|
+
{
|
|
6042
|
+
value: name,
|
|
6043
|
+
onChange: (e) => setGroup(i, e.currentTarget.value),
|
|
6044
|
+
placeholder: t("AccessGroupPlaceholder", "ACE group or permission, e.g. group.admin"),
|
|
6045
|
+
size: "xs",
|
|
6046
|
+
style: { flex: 1 }
|
|
6047
|
+
}
|
|
6048
|
+
),
|
|
6049
|
+
/* @__PURE__ */ jsx(
|
|
6050
|
+
ActionIcon,
|
|
6051
|
+
{
|
|
6052
|
+
variant: "subtle",
|
|
6053
|
+
color: "red",
|
|
6054
|
+
size: "md",
|
|
6055
|
+
onClick: () => removeGroup(i),
|
|
6056
|
+
title: t("Remove", "Remove"),
|
|
6057
|
+
"aria-label": t("Remove", "Remove"),
|
|
6058
|
+
children: /* @__PURE__ */ jsx(Trash2, { size: "1.4vh" })
|
|
6059
|
+
}
|
|
6060
|
+
)
|
|
6061
|
+
] }, i)),
|
|
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
|
+
)
|
|
6071
|
+
] }),
|
|
6072
|
+
!addingGroup && /* @__PURE__ */ jsx(AddRowButton, { label: t("AccessAddGroup", "Add group"), onClick: () => setAddingGroup(true) }),
|
|
6073
|
+
/* @__PURE__ */ jsx(SectionLabel, { icon: UserRound, label: t("AccessPlayers", "Players") }),
|
|
6074
|
+
/* @__PURE__ */ jsxs(Flex, { direction: "column", gap: "xxs", children: [
|
|
6075
|
+
value.identifiers.length === 0 && !addingPlayer && /* @__PURE__ */ jsx(EmptyHint, { text: t("AccessNoPlayers", "No players added.") }),
|
|
6076
|
+
value.identifiers.map((id, i) => /* @__PURE__ */ jsxs(Flex, { align: "center", gap: "xs", children: [
|
|
6077
|
+
/* @__PURE__ */ jsx(
|
|
6078
|
+
PlayerSelect,
|
|
6079
|
+
{
|
|
6080
|
+
value: id || null,
|
|
6081
|
+
onChange: (p) => setIdentifier(i, p?.citizenId ?? ""),
|
|
6082
|
+
includeOffline,
|
|
6083
|
+
placeholder: t("AccessPickPlayer", "Pick a player\u2026"),
|
|
6084
|
+
style: { flex: 1 },
|
|
6085
|
+
size: "xs"
|
|
6086
|
+
}
|
|
6087
|
+
),
|
|
6088
|
+
/* @__PURE__ */ jsx(
|
|
6089
|
+
ActionIcon,
|
|
6090
|
+
{
|
|
6091
|
+
variant: "subtle",
|
|
6092
|
+
color: "red",
|
|
6093
|
+
size: "md",
|
|
6094
|
+
onClick: () => removeIdentifier(i),
|
|
6095
|
+
title: t("Remove", "Remove"),
|
|
6096
|
+
"aria-label": t("Remove", "Remove"),
|
|
6097
|
+
children: /* @__PURE__ */ jsx(Trash2, { size: "1.4vh" })
|
|
6098
|
+
}
|
|
6099
|
+
)
|
|
6100
|
+
] }, i)),
|
|
6101
|
+
addingPlayer && /* @__PURE__ */ jsxs(Flex, { align: "center", gap: "xs", children: [
|
|
6102
|
+
/* @__PURE__ */ jsx(
|
|
6103
|
+
PlayerSelect,
|
|
6104
|
+
{
|
|
6105
|
+
value: null,
|
|
6106
|
+
onChange: (p) => commitIdentifier(p?.citizenId ?? ""),
|
|
6107
|
+
includeOffline,
|
|
6108
|
+
placeholder: t("AccessPickPlayer", "Pick a player\u2026"),
|
|
6109
|
+
style: { flex: 1 },
|
|
6110
|
+
size: "xs"
|
|
6111
|
+
}
|
|
6112
|
+
),
|
|
6113
|
+
/* @__PURE__ */ jsx(
|
|
6114
|
+
ActionIcon,
|
|
6115
|
+
{
|
|
6116
|
+
variant: "subtle",
|
|
6117
|
+
color: "red",
|
|
6118
|
+
size: "md",
|
|
6119
|
+
onClick: () => setAddingPlayer(false),
|
|
6120
|
+
title: t("Remove", "Remove"),
|
|
6121
|
+
"aria-label": t("Remove", "Remove"),
|
|
6122
|
+
children: /* @__PURE__ */ jsx(Trash2, { size: "1.4vh" })
|
|
6123
|
+
}
|
|
6124
|
+
)
|
|
6125
|
+
] })
|
|
6126
|
+
] }),
|
|
6127
|
+
!addingPlayer && /* @__PURE__ */ jsx(AddRowButton, { label: t("AccessAddPlayer", "Add player"), onClick: () => setAddingPlayer(true) })
|
|
6128
|
+
]
|
|
6129
|
+
}
|
|
6130
|
+
);
|
|
6131
|
+
}
|
|
6132
|
+
function AddRowButton({ label, onClick }) {
|
|
6133
|
+
return /* @__PURE__ */ jsxs(
|
|
6134
|
+
Flex,
|
|
6135
|
+
{
|
|
6136
|
+
align: "center",
|
|
6137
|
+
justify: "center",
|
|
6138
|
+
gap: "xs",
|
|
6139
|
+
onClick,
|
|
6140
|
+
role: "button",
|
|
6141
|
+
tabIndex: 0,
|
|
6142
|
+
onKeyDown: (e) => {
|
|
6143
|
+
if (e.key === "Enter" || e.key === " ") {
|
|
6144
|
+
e.preventDefault();
|
|
6145
|
+
onClick();
|
|
6146
|
+
}
|
|
6147
|
+
},
|
|
6148
|
+
py: "xs",
|
|
6149
|
+
style: {
|
|
6150
|
+
cursor: "pointer",
|
|
6151
|
+
border: "0.1vh dashed rgba(255,255,255,0.12)",
|
|
6152
|
+
borderRadius: "0.4vh",
|
|
6153
|
+
transition: "background 0.15s, border-color 0.15s"
|
|
6154
|
+
},
|
|
6155
|
+
onMouseEnter: (e) => {
|
|
6156
|
+
e.currentTarget.style.background = "rgba(255,255,255,0.03)";
|
|
6157
|
+
e.currentTarget.style.borderColor = "rgba(255,255,255,0.2)";
|
|
6158
|
+
},
|
|
6159
|
+
onMouseLeave: (e) => {
|
|
6160
|
+
e.currentTarget.style.background = "transparent";
|
|
6161
|
+
e.currentTarget.style.borderColor = "rgba(255,255,255,0.12)";
|
|
6162
|
+
},
|
|
6163
|
+
children: [
|
|
6164
|
+
/* @__PURE__ */ jsx(Plus, { size: "1.4vh", color: "rgba(255,255,255,0.4)" }),
|
|
6165
|
+
/* @__PURE__ */ jsx(Text, { ff: "Akrobat Bold", size: "xxs", tt: "uppercase", lts: "0.06em", c: "rgba(255,255,255,0.4)", children: label })
|
|
6166
|
+
]
|
|
6167
|
+
}
|
|
6168
|
+
);
|
|
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
|
+
}
|
|
5736
6217
|
|
|
5737
6218
|
// src/utils/gtaAnimPostFx.ts
|
|
5738
6219
|
var GTA_ANIM_POST_FX_GROUP_ORDER = [
|
|
@@ -6322,7 +6803,7 @@ function ScenarioSelect({
|
|
|
6322
6803
|
}
|
|
6323
6804
|
);
|
|
6324
6805
|
}
|
|
6325
|
-
var
|
|
6806
|
+
var t2 = (key, fallback) => {
|
|
6326
6807
|
const v = locale(key);
|
|
6327
6808
|
return v === key ? fallback : v;
|
|
6328
6809
|
};
|
|
@@ -6390,7 +6871,7 @@ function DiscordRoleSelect(props) {
|
|
|
6390
6871
|
return map;
|
|
6391
6872
|
}, [roles]);
|
|
6392
6873
|
if (errorCode === "NotConfigured") {
|
|
6393
|
-
const notConfiguredPlaceholder =
|
|
6874
|
+
const notConfiguredPlaceholder = t2(
|
|
6394
6875
|
"DiscordNotConfigured",
|
|
6395
6876
|
"Not configured \u2014 set in /dirk_lib"
|
|
6396
6877
|
);
|
|
@@ -6458,9 +6939,9 @@ function DiscordRoleSelect(props) {
|
|
|
6458
6939
|
e.stopPropagation();
|
|
6459
6940
|
refresh();
|
|
6460
6941
|
},
|
|
6461
|
-
title:
|
|
6942
|
+
title: t2("Refresh", "Refresh"),
|
|
6462
6943
|
loading,
|
|
6463
|
-
"aria-label":
|
|
6944
|
+
"aria-label": t2("Refresh", "Refresh"),
|
|
6464
6945
|
children: /* @__PURE__ */ jsx(RefreshCw, { size: "1.2vh" })
|
|
6465
6946
|
}
|
|
6466
6947
|
);
|
|
@@ -6470,7 +6951,7 @@ function DiscordRoleSelect(props) {
|
|
|
6470
6951
|
{
|
|
6471
6952
|
label,
|
|
6472
6953
|
size,
|
|
6473
|
-
placeholder: placeholder ||
|
|
6954
|
+
placeholder: placeholder || t2("PickRoles", "Pick roles..."),
|
|
6474
6955
|
data,
|
|
6475
6956
|
value: props.value || [],
|
|
6476
6957
|
onChange: (ids) => props.onChange(ids),
|
|
@@ -6482,7 +6963,7 @@ function DiscordRoleSelect(props) {
|
|
|
6482
6963
|
rightSectionPointerEvents: "all",
|
|
6483
6964
|
style,
|
|
6484
6965
|
styles,
|
|
6485
|
-
nothingFoundMessage: errorCode ? `${
|
|
6966
|
+
nothingFoundMessage: errorCode ? `${t2("Error", "Error")} (${errorCode})` : t2("NoRoles", "No roles found")
|
|
6486
6967
|
}
|
|
6487
6968
|
);
|
|
6488
6969
|
}
|
|
@@ -6491,7 +6972,7 @@ function DiscordRoleSelect(props) {
|
|
|
6491
6972
|
{
|
|
6492
6973
|
label,
|
|
6493
6974
|
size,
|
|
6494
|
-
placeholder: placeholder ||
|
|
6975
|
+
placeholder: placeholder || t2("PickRole", "Pick a role..."),
|
|
6495
6976
|
data,
|
|
6496
6977
|
value: props.value || null,
|
|
6497
6978
|
onChange: (id) => props.onChange(id),
|
|
@@ -6503,11 +6984,11 @@ function DiscordRoleSelect(props) {
|
|
|
6503
6984
|
rightSectionPointerEvents: "all",
|
|
6504
6985
|
style,
|
|
6505
6986
|
styles,
|
|
6506
|
-
nothingFoundMessage: errorCode ? `${
|
|
6987
|
+
nothingFoundMessage: errorCode ? `${t2("Error", "Error")} (${errorCode})` : t2("NoRoles", "No roles found")
|
|
6507
6988
|
}
|
|
6508
6989
|
);
|
|
6509
6990
|
}
|
|
6510
|
-
var
|
|
6991
|
+
var t3 = (key, fallback) => {
|
|
6511
6992
|
const v = locale(key);
|
|
6512
6993
|
return v === key ? fallback : v;
|
|
6513
6994
|
};
|
|
@@ -6528,7 +7009,7 @@ function FrameworkHint({ framework }) {
|
|
|
6528
7009
|
const theme = useMantineTheme();
|
|
6529
7010
|
if (!framework) return null;
|
|
6530
7011
|
const hintKey = `AccountFrameworkHint_${framework}`;
|
|
6531
|
-
const text =
|
|
7012
|
+
const text = t3(hintKey, FRAMEWORK_HINTS[framework] || FRAMEWORK_HINTS.unknown);
|
|
6532
7013
|
return /* @__PURE__ */ jsxs(
|
|
6533
7014
|
Flex,
|
|
6534
7015
|
{
|
|
@@ -6612,9 +7093,9 @@ function AccountSelect(props) {
|
|
|
6612
7093
|
e.stopPropagation();
|
|
6613
7094
|
refresh();
|
|
6614
7095
|
},
|
|
6615
|
-
title:
|
|
7096
|
+
title: t3("Refresh", "Refresh"),
|
|
6616
7097
|
loading,
|
|
6617
|
-
"aria-label":
|
|
7098
|
+
"aria-label": t3("Refresh", "Refresh"),
|
|
6618
7099
|
children: /* @__PURE__ */ jsx(RefreshCw, { size: "1.2vh" })
|
|
6619
7100
|
}
|
|
6620
7101
|
);
|
|
@@ -6628,14 +7109,14 @@ function AccountSelect(props) {
|
|
|
6628
7109
|
rightSection: refreshButton,
|
|
6629
7110
|
rightSectionPointerEvents: "all",
|
|
6630
7111
|
style,
|
|
6631
|
-
nothingFoundMessage:
|
|
7112
|
+
nothingFoundMessage: t3("NoAccounts", "No accounts available")
|
|
6632
7113
|
};
|
|
6633
7114
|
return /* @__PURE__ */ jsxs(Flex, { direction: "column", gap: "0.3vh", style, children: [
|
|
6634
7115
|
props.multi ? /* @__PURE__ */ jsx(
|
|
6635
7116
|
MultiSelect,
|
|
6636
7117
|
{
|
|
6637
7118
|
...sharedSelectProps,
|
|
6638
|
-
placeholder: placeholder ||
|
|
7119
|
+
placeholder: placeholder || t3("PickAccounts", "Pick accounts (or type custom)..."),
|
|
6639
7120
|
value: props.value || [],
|
|
6640
7121
|
onChange: (names) => props.onChange(names),
|
|
6641
7122
|
renderOption,
|
|
@@ -6647,7 +7128,7 @@ function AccountSelect(props) {
|
|
|
6647
7128
|
Select,
|
|
6648
7129
|
{
|
|
6649
7130
|
...sharedSelectProps,
|
|
6650
|
-
placeholder: placeholder ||
|
|
7131
|
+
placeholder: placeholder || t3("PickAccount", "Pick an account..."),
|
|
6651
7132
|
value: props.value || null,
|
|
6652
7133
|
onChange: (name) => props.onChange(name),
|
|
6653
7134
|
renderOption,
|
|
@@ -6939,175 +7420,6 @@ function WorldPositionGotoButton2({
|
|
|
6939
7420
|
}
|
|
6940
7421
|
) });
|
|
6941
7422
|
}
|
|
6942
|
-
function usePlayers(opts = {}) {
|
|
6943
|
-
const {
|
|
6944
|
-
includeOffline = false,
|
|
6945
|
-
search = "",
|
|
6946
|
-
limit = 50,
|
|
6947
|
-
staleTimeMs,
|
|
6948
|
-
refetchIntervalMs
|
|
6949
|
-
} = opts;
|
|
6950
|
-
const query = useQuery({
|
|
6951
|
-
queryKey: includeOffline ? ["dirk:players", "search", search.trim().toLowerCase(), limit] : ["dirk:players", "online"],
|
|
6952
|
-
queryFn: async () => {
|
|
6953
|
-
const toolId = includeOffline ? "searchPlayers" : "getOnlinePlayers";
|
|
6954
|
-
const payload = includeOffline ? { id: toolId, value: { search: search.trim(), limit } } : { id: toolId };
|
|
6955
|
-
const result = await fetchNui(
|
|
6956
|
-
"ADMIN_TOOL_QUERY",
|
|
6957
|
-
payload,
|
|
6958
|
-
// Browser-dev fallback. Returns a couple of mock players so the
|
|
6959
|
-
// dev shell isn't blank.
|
|
6960
|
-
includeOffline ? [
|
|
6961
|
-
{ id: 1, citizenId: "ABC12345", name: "Dev User", charName: "John Doe", online: true },
|
|
6962
|
-
{ id: null, citizenId: "DEF67890", name: "", charName: "Jane Offline", online: false }
|
|
6963
|
-
] : [
|
|
6964
|
-
{ id: 1, citizenId: "ABC12345", name: "Dev User", charName: "John Doe", online: true }
|
|
6965
|
-
]
|
|
6966
|
-
);
|
|
6967
|
-
return Array.isArray(result) ? result : [];
|
|
6968
|
-
},
|
|
6969
|
-
staleTime: staleTimeMs ?? (includeOffline ? 3e4 : 5e3),
|
|
6970
|
-
gcTime: 5 * 6e4,
|
|
6971
|
-
refetchInterval: refetchIntervalMs ?? false,
|
|
6972
|
-
refetchOnWindowFocus: false,
|
|
6973
|
-
placeholderData: includeOffline ? keepPreviousData : void 0
|
|
6974
|
-
});
|
|
6975
|
-
return {
|
|
6976
|
-
players: query.data ?? [],
|
|
6977
|
-
isLoading: query.isLoading,
|
|
6978
|
-
isFetching: query.isFetching,
|
|
6979
|
-
error: query.error ?? null,
|
|
6980
|
-
refresh: () => query.refetch()
|
|
6981
|
-
};
|
|
6982
|
-
}
|
|
6983
|
-
var DEBOUNCE_MS = 300;
|
|
6984
|
-
var ONLINE_COLOR = "#3FA83F";
|
|
6985
|
-
var OFFLINE_COLOR = "#E54141";
|
|
6986
|
-
function PlayerSelect({
|
|
6987
|
-
value,
|
|
6988
|
-
onChange,
|
|
6989
|
-
includeOffline = false,
|
|
6990
|
-
limit = 50,
|
|
6991
|
-
searchable: searchableProp,
|
|
6992
|
-
placeholder,
|
|
6993
|
-
nothingFoundMessage: nothingFoundMessageProp,
|
|
6994
|
-
loadingLabel = "Loading\u2026",
|
|
6995
|
-
onlineLabel = "Online",
|
|
6996
|
-
offlineLabel = "Offline",
|
|
6997
|
-
refreshLabel = "Refresh player list",
|
|
6998
|
-
...rest
|
|
6999
|
-
}) {
|
|
7000
|
-
const theme = useMantineTheme();
|
|
7001
|
-
const color = theme.colors[theme.primaryColor][5];
|
|
7002
|
-
const [searchInput, setSearchInput] = useState("");
|
|
7003
|
-
const [debouncedSearch, setDebouncedSearch] = useState("");
|
|
7004
|
-
const { players, isFetching, refresh } = usePlayers({
|
|
7005
|
-
includeOffline,
|
|
7006
|
-
search: includeOffline ? debouncedSearch : void 0,
|
|
7007
|
-
limit
|
|
7008
|
-
});
|
|
7009
|
-
const sortedPlayers = useMemo(
|
|
7010
|
-
() => [...players].sort((a, b) => {
|
|
7011
|
-
if (a.online !== b.online) return a.online ? -1 : 1;
|
|
7012
|
-
return a.charName.localeCompare(b.charName);
|
|
7013
|
-
}),
|
|
7014
|
-
[players]
|
|
7015
|
-
);
|
|
7016
|
-
const playerByCitizen = useMemo(() => {
|
|
7017
|
-
const m = /* @__PURE__ */ new Map();
|
|
7018
|
-
for (const p of sortedPlayers) m.set(p.citizenId, p);
|
|
7019
|
-
return m;
|
|
7020
|
-
}, [sortedPlayers]);
|
|
7021
|
-
const data = useMemo(() => {
|
|
7022
|
-
const items = sortedPlayers.map((p) => ({
|
|
7023
|
-
value: p.citizenId,
|
|
7024
|
-
label: formatLabel(p)
|
|
7025
|
-
}));
|
|
7026
|
-
if (value && !items.some((i) => i.value === value)) {
|
|
7027
|
-
items.unshift({ value, label: value });
|
|
7028
|
-
}
|
|
7029
|
-
return items;
|
|
7030
|
-
}, [sortedPlayers, value]);
|
|
7031
|
-
const selectedPlayer = value ? playerByCitizen.get(value) ?? null : null;
|
|
7032
|
-
const selectedLabel = selectedPlayer ? formatLabel(selectedPlayer) : null;
|
|
7033
|
-
useEffect(() => {
|
|
7034
|
-
if (!includeOffline) return;
|
|
7035
|
-
if (selectedLabel && searchInput === selectedLabel) return;
|
|
7036
|
-
const t3 = setTimeout(() => setDebouncedSearch(searchInput), DEBOUNCE_MS);
|
|
7037
|
-
return () => clearTimeout(t3);
|
|
7038
|
-
}, [searchInput, includeOffline, selectedLabel]);
|
|
7039
|
-
const renderOption = ({ option }) => {
|
|
7040
|
-
const p = playerByCitizen.get(option.value);
|
|
7041
|
-
if (!p) return option.label;
|
|
7042
|
-
return /* @__PURE__ */ jsxs(Group, { justify: "space-between", wrap: "nowrap", gap: "xs", style: { width: "100%" }, children: [
|
|
7043
|
-
/* @__PURE__ */ jsx(Text, { size: "xs", truncate: true, style: { flex: 1 }, children: formatLabel(p) }),
|
|
7044
|
-
/* @__PURE__ */ jsx(StatusDot, { online: p.online, onlineLabel, offlineLabel })
|
|
7045
|
-
] });
|
|
7046
|
-
};
|
|
7047
|
-
return /* @__PURE__ */ jsx(
|
|
7048
|
-
Select,
|
|
7049
|
-
{
|
|
7050
|
-
...rest,
|
|
7051
|
-
data,
|
|
7052
|
-
value: value ?? null,
|
|
7053
|
-
onChange: (v) => {
|
|
7054
|
-
if (!v) return onChange(null);
|
|
7055
|
-
const player = playerByCitizen.get(v) ?? null;
|
|
7056
|
-
onChange(player);
|
|
7057
|
-
},
|
|
7058
|
-
searchable: searchableProp ?? true,
|
|
7059
|
-
searchValue: includeOffline ? searchInput : void 0,
|
|
7060
|
-
onSearchChange: includeOffline ? setSearchInput : void 0,
|
|
7061
|
-
placeholder,
|
|
7062
|
-
nothingFoundMessage: isFetching ? loadingLabel : nothingFoundMessageProp ?? "No players found",
|
|
7063
|
-
maxDropdownHeight: 300,
|
|
7064
|
-
renderOption,
|
|
7065
|
-
leftSection: selectedPlayer ? /* @__PURE__ */ jsx(StatusDot, { online: selectedPlayer.online, onlineLabel, offlineLabel }) : void 0,
|
|
7066
|
-
rightSectionWidth: "3.5vh",
|
|
7067
|
-
rightSectionPointerEvents: "all",
|
|
7068
|
-
rightSection: /* @__PURE__ */ jsx(
|
|
7069
|
-
ActionIcon,
|
|
7070
|
-
{
|
|
7071
|
-
variant: "subtle",
|
|
7072
|
-
size: "sm",
|
|
7073
|
-
onClick: (e) => {
|
|
7074
|
-
e.stopPropagation();
|
|
7075
|
-
refresh();
|
|
7076
|
-
},
|
|
7077
|
-
"aria-label": refreshLabel,
|
|
7078
|
-
title: refreshLabel,
|
|
7079
|
-
style: { marginRight: "0.6vh" },
|
|
7080
|
-
children: isFetching ? /* @__PURE__ */ jsx(Loader, { size: "1.2vh", color }) : /* @__PURE__ */ jsx(RefreshCw, { size: "1.4vh", color })
|
|
7081
|
-
}
|
|
7082
|
-
)
|
|
7083
|
-
}
|
|
7084
|
-
);
|
|
7085
|
-
}
|
|
7086
|
-
function StatusDot({ online, onlineLabel, offlineLabel }) {
|
|
7087
|
-
return /* @__PURE__ */ jsx(
|
|
7088
|
-
"span",
|
|
7089
|
-
{
|
|
7090
|
-
title: online ? onlineLabel : offlineLabel,
|
|
7091
|
-
style: {
|
|
7092
|
-
display: "inline-block",
|
|
7093
|
-
width: "0.9vh",
|
|
7094
|
-
height: "0.9vh",
|
|
7095
|
-
borderRadius: "50%",
|
|
7096
|
-
backgroundColor: online ? ONLINE_COLOR : OFFLINE_COLOR,
|
|
7097
|
-
boxShadow: `0 0 0.4vh ${online ? ONLINE_COLOR : OFFLINE_COLOR}`,
|
|
7098
|
-
flexShrink: 0
|
|
7099
|
-
}
|
|
7100
|
-
}
|
|
7101
|
-
);
|
|
7102
|
-
}
|
|
7103
|
-
function formatLabel(p) {
|
|
7104
|
-
const parts = [p.charName || p.citizenId];
|
|
7105
|
-
if (p.online) {
|
|
7106
|
-
if (p.name) parts.push(p.name);
|
|
7107
|
-
if (p.id != null) parts.push(`#${p.id}`);
|
|
7108
|
-
}
|
|
7109
|
-
return parts.join(" \xB7 ");
|
|
7110
|
-
}
|
|
7111
7423
|
function usePickDoor() {
|
|
7112
7424
|
const begin = useAdminToolStore((s) => s.begin);
|
|
7113
7425
|
return async () => {
|
|
@@ -7384,6 +7696,6 @@ function BlipMarker({
|
|
|
7384
7696
|
);
|
|
7385
7697
|
}
|
|
7386
7698
|
|
|
7387
|
-
export { AccountSelect, AdminPageTitle, AnimPostFxSelect, AsyncSaveButton, BlipColorSelect, BlipDisplaySelect, BlipIconSelect, BlipMarker, BorderedIcon, ConfigPanel, ConfirmModal, ControlMultiSelect, ControlSelect, Counter, DiscordRoleSelect, DoorPickerButton, FiveMKeyBindInput, FloatingParticles, GroupName, GroupRank, GroupSelect, InfoBox, InputContainer, InstructionPanel, LevelBanner, LevelPanel, Map2 as Map, MapLayer, MissingItemsBanner, Modal, ModalContext, ModalProvider, MotionFlex, MotionIcon, MotionImage, MotionText, NavBar, NavigationContext, NavigationProvider, PlayerSelect, PositionPicker, PromptModal, ScenarioSelect, SegmentedControl, SegmentedProgress, SelectItem, SwitchPanel, TestBed, ThemeOverrideSection, Title, Vector4DeleteButton, Vector4Display, WorldPositionGotoButton, WorldPositionPicker, WorldPositionSetButton, ZoomControls, blipUrl, blipUrlForSprite, getBlipColor, getBlipEntry, useAdminToolStore, useMissingItemsAudit, useModal, useModalActions, useNavigation, useNavigationStore, usePickDoor };
|
|
7699
|
+
export { AccessOverrideSection, AccountSelect, AdminPageTitle, AnimPostFxSelect, AsyncSaveButton, BlipColorSelect, BlipDisplaySelect, BlipIconSelect, BlipMarker, BorderedIcon, ConfigPanel, ConfirmModal, ControlMultiSelect, ControlSelect, Counter, DiscordRoleSelect, DoorPickerButton, FiveMKeyBindInput, FloatingParticles, GroupName, GroupRank, GroupSelect, InfoBox, InputContainer, InstructionPanel, LevelBanner, LevelPanel, Map2 as Map, MapLayer, MissingItemsBanner, Modal, ModalContext, ModalProvider, MotionFlex, MotionIcon, MotionImage, MotionText, NavBar, NavigationContext, NavigationProvider, PlayerSelect, PositionPicker, PromptModal, ScenarioSelect, SegmentedControl, SegmentedProgress, SelectItem, SwitchPanel, TestBed, ThemeOverrideSection, Title, Vector4DeleteButton, Vector4Display, WorldPositionGotoButton, WorldPositionPicker, WorldPositionSetButton, ZoomControls, blipUrl, blipUrlForSprite, getBlipColor, getBlipEntry, useAdminToolStore, useMissingItemsAudit, useModal, useModalActions, useNavigation, useNavigationStore, usePickDoor };
|
|
7388
7700
|
//# sourceMappingURL=index.js.map
|
|
7389
7701
|
//# sourceMappingURL=index.js.map
|