@stamprally/admin-ui 0.12.0 → 0.14.0
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/README.md +4 -0
- package/dist/index.cjs +169 -44
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +170 -45
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
+
"use client";
|
|
1
2
|
import { resolveLocalizedText, updateLocalizedField, DEFAULT_SHEET_THEME, safeParseAdminConfig } from '@stamprally/core';
|
|
2
|
-
import { useRef, useState, useCallback } from 'react';
|
|
3
|
+
import { useRef, useState, useEffect, useCallback } from 'react';
|
|
3
4
|
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
|
|
4
5
|
|
|
5
|
-
// src/index.tsx
|
|
6
6
|
function nextEntityId(prefix, ids) {
|
|
7
7
|
let index = ids.size + 1;
|
|
8
8
|
let candidate = `${prefix}-${index}`;
|
|
@@ -24,12 +24,22 @@ function localizedValue(value, locale, nextValue) {
|
|
|
24
24
|
}
|
|
25
25
|
function useAdminRallyEditor(initialConfig, options = {}) {
|
|
26
26
|
const initialRef = useRef(initialConfig);
|
|
27
|
+
const previousInitialRef = useRef(initialConfig);
|
|
27
28
|
const [history, setHistory] = useState({
|
|
28
29
|
past: [],
|
|
29
30
|
present: initialConfig,
|
|
30
31
|
future: []
|
|
31
32
|
});
|
|
32
33
|
const config = history.present;
|
|
34
|
+
useEffect(() => {
|
|
35
|
+
if (previousInitialRef.current === initialConfig) return;
|
|
36
|
+
previousInitialRef.current = initialConfig;
|
|
37
|
+
const isDirty = JSON.stringify(history.present) !== JSON.stringify(initialRef.current);
|
|
38
|
+
if (!isDirty) {
|
|
39
|
+
initialRef.current = initialConfig;
|
|
40
|
+
setHistory({ past: [], present: initialConfig, future: [] });
|
|
41
|
+
}
|
|
42
|
+
}, [history.present, initialConfig]);
|
|
33
43
|
const commit = useCallback(
|
|
34
44
|
(value) => {
|
|
35
45
|
setHistory((current) => {
|
|
@@ -66,6 +76,7 @@ function useAdminRallyEditor(initialConfig, options = {}) {
|
|
|
66
76
|
spots: current.spots.filter((spot) => spot.id !== spotId).map((spot, index) => ({ ...spot, orderIndex: index }))
|
|
67
77
|
}));
|
|
68
78
|
const reorderSpots = (fromIndex, toIndex) => commit((current) => ({ ...current, spots: moveTo(current.spots, fromIndex, toIndex) }));
|
|
79
|
+
const reorderRewards = (fromIndex, toIndex) => commit((current) => ({ ...current, rewards: moveTo(current.rewards, fromIndex, toIndex) }));
|
|
69
80
|
const duplicateSpot = (spotId) => commit((current) => {
|
|
70
81
|
const source = current.spots.find((spot) => spot.id === spotId);
|
|
71
82
|
if (source === void 0) return current;
|
|
@@ -164,6 +175,7 @@ function useAdminRallyEditor(initialConfig, options = {}) {
|
|
|
164
175
|
});
|
|
165
176
|
const resetConfig = (newConfig) => {
|
|
166
177
|
initialRef.current = newConfig;
|
|
178
|
+
previousInitialRef.current = newConfig;
|
|
167
179
|
setHistory({ past: [], present: newConfig, future: [] });
|
|
168
180
|
options.onChange?.(newConfig);
|
|
169
181
|
};
|
|
@@ -176,6 +188,7 @@ function useAdminRallyEditor(initialConfig, options = {}) {
|
|
|
176
188
|
addSpot,
|
|
177
189
|
removeSpot,
|
|
178
190
|
reorderSpots: (fromIndex, toIndex) => reorderSpots(fromIndex, toIndex),
|
|
191
|
+
reorderRewards,
|
|
179
192
|
duplicateSpot,
|
|
180
193
|
addReward,
|
|
181
194
|
removeReward,
|
|
@@ -196,9 +209,13 @@ function useSpotEditor(spotId, options = {}) {
|
|
|
196
209
|
const [config, setConfig] = useState(
|
|
197
210
|
options.config ?? options.initialConfig
|
|
198
211
|
);
|
|
199
|
-
const commit = (
|
|
200
|
-
setConfig(
|
|
201
|
-
|
|
212
|
+
const commit = (updateConfig) => {
|
|
213
|
+
setConfig((current) => {
|
|
214
|
+
if (current === void 0) return current;
|
|
215
|
+
const next = updateConfig(current);
|
|
216
|
+
options.onChange?.(next);
|
|
217
|
+
return next;
|
|
218
|
+
});
|
|
202
219
|
};
|
|
203
220
|
const spot = config?.spots.find((item) => item.id === spotId);
|
|
204
221
|
return {
|
|
@@ -207,14 +224,17 @@ function useSpotEditor(spotId, options = {}) {
|
|
|
207
224
|
setConfig,
|
|
208
225
|
update: (patch) => {
|
|
209
226
|
if (config === void 0 || spot === void 0) return;
|
|
210
|
-
commit({
|
|
211
|
-
...
|
|
212
|
-
spots:
|
|
213
|
-
});
|
|
227
|
+
commit((current) => ({
|
|
228
|
+
...current,
|
|
229
|
+
spots: current.spots.map((item) => item.id === spotId ? { ...item, ...patch } : item)
|
|
230
|
+
}));
|
|
214
231
|
},
|
|
215
232
|
remove: () => {
|
|
216
233
|
if (config === void 0 || spot === void 0) return;
|
|
217
|
-
commit(
|
|
234
|
+
commit((current) => ({
|
|
235
|
+
...current,
|
|
236
|
+
spots: current.spots.filter((item) => item.id !== spotId)
|
|
237
|
+
}));
|
|
218
238
|
}
|
|
219
239
|
};
|
|
220
240
|
}
|
|
@@ -223,9 +243,13 @@ function useRewardEditor(rewardId, options = {}) {
|
|
|
223
243
|
options.config ?? options.initialConfig
|
|
224
244
|
);
|
|
225
245
|
const reward = config?.rewards.find((item) => item.id === rewardId);
|
|
226
|
-
const commit = (
|
|
227
|
-
setConfig(
|
|
228
|
-
|
|
246
|
+
const commit = (updateConfig) => {
|
|
247
|
+
setConfig((current) => {
|
|
248
|
+
if (current === void 0) return current;
|
|
249
|
+
const next = updateConfig(current);
|
|
250
|
+
options.onChange?.(next);
|
|
251
|
+
return next;
|
|
252
|
+
});
|
|
229
253
|
};
|
|
230
254
|
return {
|
|
231
255
|
config,
|
|
@@ -233,16 +257,19 @@ function useRewardEditor(rewardId, options = {}) {
|
|
|
233
257
|
setConfig,
|
|
234
258
|
update: (patch) => {
|
|
235
259
|
if (config === void 0 || reward === void 0) return;
|
|
236
|
-
commit({
|
|
237
|
-
...
|
|
238
|
-
rewards:
|
|
260
|
+
commit((current) => ({
|
|
261
|
+
...current,
|
|
262
|
+
rewards: current.rewards.map(
|
|
239
263
|
(item) => item.id === rewardId ? { ...item, ...patch } : item
|
|
240
264
|
)
|
|
241
|
-
});
|
|
265
|
+
}));
|
|
242
266
|
},
|
|
243
267
|
remove: () => {
|
|
244
268
|
if (config === void 0 || reward === void 0) return;
|
|
245
|
-
commit(
|
|
269
|
+
commit((current) => ({
|
|
270
|
+
...current,
|
|
271
|
+
rewards: current.rewards.filter((item) => item.id !== rewardId)
|
|
272
|
+
}));
|
|
246
273
|
}
|
|
247
274
|
};
|
|
248
275
|
}
|
|
@@ -1112,21 +1139,40 @@ function AdminRallyEditor({ config, onChange, locale, dictionary }) {
|
|
|
1112
1139
|
children: field("addReward", "Add reward")
|
|
1113
1140
|
}
|
|
1114
1141
|
),
|
|
1115
|
-
/* @__PURE__ */ jsx("div", { children: config.rewards.map((reward) => /* @__PURE__ */
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1142
|
+
/* @__PURE__ */ jsx("div", { children: config.rewards.map((reward, index) => /* @__PURE__ */ jsxs("div", { children: [
|
|
1143
|
+
/* @__PURE__ */ jsx(
|
|
1144
|
+
RewardItemForm,
|
|
1145
|
+
{
|
|
1146
|
+
reward,
|
|
1147
|
+
locale: activeLocale,
|
|
1148
|
+
...dictionary === void 0 ? {} : { dictionary },
|
|
1149
|
+
onChange: (next) => update({
|
|
1150
|
+
rewards: config.rewards.map(
|
|
1151
|
+
(current) => current.id === reward.id ? next : current
|
|
1152
|
+
)
|
|
1153
|
+
}),
|
|
1154
|
+
onRemove: () => update({ rewards: config.rewards.filter((current) => current.id !== reward.id) })
|
|
1155
|
+
}
|
|
1156
|
+
),
|
|
1157
|
+
/* @__PURE__ */ jsx(
|
|
1158
|
+
"button",
|
|
1159
|
+
{
|
|
1160
|
+
type: "button",
|
|
1161
|
+
disabled: index === 0,
|
|
1162
|
+
onClick: () => update({ rewards: move(config.rewards, index, -1) }),
|
|
1163
|
+
children: field("moveUp", "Move up")
|
|
1164
|
+
}
|
|
1165
|
+
),
|
|
1166
|
+
/* @__PURE__ */ jsx(
|
|
1167
|
+
"button",
|
|
1168
|
+
{
|
|
1169
|
+
type: "button",
|
|
1170
|
+
disabled: index === config.rewards.length - 1,
|
|
1171
|
+
onClick: () => update({ rewards: move(config.rewards, index, 1) }),
|
|
1172
|
+
children: field("moveDown", "Move down")
|
|
1173
|
+
}
|
|
1174
|
+
)
|
|
1175
|
+
] }, reward.id)) })
|
|
1130
1176
|
] });
|
|
1131
1177
|
}
|
|
1132
1178
|
var RallyEditor = AdminRallyEditor;
|
|
@@ -1137,18 +1183,97 @@ function GeneralSettingsForm({
|
|
|
1137
1183
|
dictionary
|
|
1138
1184
|
}) {
|
|
1139
1185
|
const activeLocale = locale ?? "en";
|
|
1140
|
-
return /* @__PURE__ */ jsxs("
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1186
|
+
return /* @__PURE__ */ jsxs("section", { "aria-label": text(dictionary, activeLocale, "generalSettings", "General settings"), children: [
|
|
1187
|
+
/* @__PURE__ */ jsxs("label", { children: [
|
|
1188
|
+
text(dictionary, activeLocale, "title", "Rally title"),
|
|
1189
|
+
/* @__PURE__ */ jsx(
|
|
1190
|
+
"input",
|
|
1191
|
+
{
|
|
1192
|
+
value: resolveLocalizedText(config.title, activeLocale),
|
|
1193
|
+
onChange: (event) => onChange({
|
|
1194
|
+
...config,
|
|
1195
|
+
title: updateLocalizedField(config.title, activeLocale, event.target.value)
|
|
1196
|
+
})
|
|
1197
|
+
}
|
|
1198
|
+
)
|
|
1199
|
+
] }),
|
|
1200
|
+
/* @__PURE__ */ jsxs("label", { children: [
|
|
1201
|
+
text(dictionary, activeLocale, "inventoryLimit", "Global inventory limit"),
|
|
1202
|
+
/* @__PURE__ */ jsx(
|
|
1203
|
+
"input",
|
|
1204
|
+
{
|
|
1205
|
+
type: "number",
|
|
1206
|
+
min: 0,
|
|
1207
|
+
placeholder: text(dictionary, activeLocale, "inventoryLimitPlaceholder", "Unlimited"),
|
|
1208
|
+
title: text(
|
|
1209
|
+
dictionary,
|
|
1210
|
+
activeLocale,
|
|
1211
|
+
"inventoryOverrideHelp",
|
|
1212
|
+
"Individual reward limits override this global limit."
|
|
1213
|
+
),
|
|
1214
|
+
value: config.inventory?.global ?? "",
|
|
1215
|
+
onChange: (event) => {
|
|
1216
|
+
const value = event.target.value;
|
|
1217
|
+
const inventory = { ...config.inventory ?? {} };
|
|
1218
|
+
if (value === "") delete inventory.global;
|
|
1219
|
+
else inventory.global = Number(value);
|
|
1220
|
+
onChange({ ...config, inventory });
|
|
1221
|
+
}
|
|
1222
|
+
}
|
|
1223
|
+
)
|
|
1224
|
+
] }),
|
|
1225
|
+
/* @__PURE__ */ jsxs("label", { children: [
|
|
1226
|
+
text(dictionary, activeLocale, "inventoryMode", "Inventory aggregation"),
|
|
1227
|
+
/* @__PURE__ */ jsxs(
|
|
1228
|
+
"select",
|
|
1229
|
+
{
|
|
1230
|
+
value: config.inventoryMode ?? "shared",
|
|
1231
|
+
title: text(
|
|
1232
|
+
dictionary,
|
|
1233
|
+
activeLocale,
|
|
1234
|
+
"inventoryModeHelp",
|
|
1235
|
+
"Individual reward limits override this setting."
|
|
1236
|
+
),
|
|
1237
|
+
onChange: (event) => onChange({
|
|
1238
|
+
...config,
|
|
1239
|
+
inventoryMode: event.target.value
|
|
1240
|
+
}),
|
|
1241
|
+
children: [
|
|
1242
|
+
/* @__PURE__ */ jsx("option", { value: "shared", children: text(dictionary, activeLocale, "inventoryShared", "Shared") }),
|
|
1243
|
+
/* @__PURE__ */ jsx("option", { value: "per_reward", children: text(dictionary, activeLocale, "inventoryPerReward", "Per reward") })
|
|
1244
|
+
]
|
|
1245
|
+
}
|
|
1246
|
+
)
|
|
1247
|
+
] }),
|
|
1248
|
+
/* @__PURE__ */ jsxs("label", { children: [
|
|
1249
|
+
text(dictionary, activeLocale, "staffPasscode", "Global staff passcode"),
|
|
1250
|
+
/* @__PURE__ */ jsx(
|
|
1251
|
+
"input",
|
|
1252
|
+
{
|
|
1253
|
+
type: "password",
|
|
1254
|
+
placeholder: text(
|
|
1255
|
+
dictionary,
|
|
1256
|
+
activeLocale,
|
|
1257
|
+
"staffPasscodePlaceholder",
|
|
1258
|
+
"Used unless a reward has its own passcode"
|
|
1259
|
+
),
|
|
1260
|
+
title: text(
|
|
1261
|
+
dictionary,
|
|
1262
|
+
activeLocale,
|
|
1263
|
+
"staffPasscodeHelp",
|
|
1264
|
+
"Individual spot or reward settings override this global passcode."
|
|
1265
|
+
),
|
|
1266
|
+
value: config.staffPasscode ?? "",
|
|
1267
|
+
onChange: (event) => {
|
|
1268
|
+
const value = event.target.value;
|
|
1269
|
+
if (value === "") {
|
|
1270
|
+
const { staffPasscode: _staffPasscode, ...withoutPasscode } = config;
|
|
1271
|
+
onChange(withoutPasscode);
|
|
1272
|
+
} else onChange({ ...config, staffPasscode: value });
|
|
1273
|
+
}
|
|
1274
|
+
}
|
|
1275
|
+
)
|
|
1276
|
+
] })
|
|
1152
1277
|
] });
|
|
1153
1278
|
}
|
|
1154
1279
|
function JsonConfigIO({ config, onImport, locale, dictionary }) {
|