@digilogiclabs/saas-factory-ui 2.7.1 → 2.8.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.
@@ -31798,32 +31798,26 @@ function BracketGenerator({
31798
31798
  const autoPlay = useCallback42(() => {
31799
31799
  if (simulating) {
31800
31800
  if (autoRef.current) clearTimeout(autoRef.current);
31801
+ autoRef.current = null;
31801
31802
  setSimulating(false);
31802
31803
  return;
31803
31804
  }
31804
31805
  setSimulating(true);
31805
- const tick = () => {
31806
- setBracketState((prev) => {
31807
- const next = prev.matches.find((m) => m.a && m.b && !m.winner);
31808
- if (!next) {
31809
- setSimulating(false);
31810
- return prev;
31811
- }
31812
- const completed = simulateMatch(next);
31813
- if (!completed) return prev;
31814
- let matches = prev.matches.map(
31815
- (m) => m.id === completed.id ? completed : m
31816
- );
31817
- matches = propagateWinner(matches, completed);
31818
- const finalMatch = matches.find((m) => m.round === prev.rounds - 1);
31819
- const champion = finalMatch?.winner || null;
31820
- if (!champion) autoRef.current = setTimeout(tick, simulateDelayMs);
31821
- else setSimulating(false);
31822
- return { ...prev, matches, champion };
31823
- });
31806
+ simulateNext();
31807
+ }, [simulating, simulateNext]);
31808
+ useEffect52(() => {
31809
+ if (!simulating) return;
31810
+ const hasNext = bracketState.matches.some((m) => m.a && m.b && !m.winner);
31811
+ if (bracketState.champion || !hasNext) {
31812
+ setSimulating(false);
31813
+ return;
31814
+ }
31815
+ autoRef.current = setTimeout(simulateNext, simulateDelayMs);
31816
+ return () => {
31817
+ if (autoRef.current) clearTimeout(autoRef.current);
31818
+ autoRef.current = null;
31824
31819
  };
31825
- tick();
31826
- }, [simulating, simulateDelayMs]);
31820
+ }, [simulating, bracketState, simulateDelayMs, simulateNext]);
31827
31821
  const advanceWinner = useCallback42(
31828
31822
  (matchId, winnerSlot) => {
31829
31823
  setBracketState((prev) => {
@@ -39470,8 +39464,8 @@ function RosterImport({
39470
39464
  maxNames,
39471
39465
  maxFileBytes,
39472
39466
  onClose: () => setOpen(false),
39473
- onImport: (names) => {
39474
- onImport(names);
39467
+ onImport: (names, info) => {
39468
+ onImport(names, info);
39475
39469
  setOpen(false);
39476
39470
  }
39477
39471
  }
@@ -39537,6 +39531,11 @@ function RosterImportModal({
39537
39531
  }),
39538
39532
  [table, column, skipHeader, maxNames]
39539
39533
  );
39534
+ const extractedTotal = useMemo34(
39535
+ () => extractRoster(table, { column, skipHeader }),
39536
+ [table, column, skipHeader]
39537
+ );
39538
+ const trimmedCount = Math.max(0, extractedTotal.length - extracted.length);
39540
39539
  const handleFile = useCallback49(
39541
39540
  async (e) => {
39542
39541
  setFileError(null);
@@ -39914,7 +39913,10 @@ function RosterImportModal({
39914
39913
  "button",
39915
39914
  {
39916
39915
  type: "button",
39917
- onClick: () => onImport(extracted),
39916
+ onClick: () => onImport(extracted, {
39917
+ trimmedCount,
39918
+ totalCount: extractedTotal.length
39919
+ }),
39918
39920
  disabled: !canImport,
39919
39921
  style: primaryBtn(!canImport),
39920
39922
  children: confirmLabel
@@ -40118,6 +40120,19 @@ function IconUsers({ size = 40 }) {
40118
40120
  }
40119
40121
  );
40120
40122
  }
40123
+ function mergeNames(prev, incoming, max) {
40124
+ const seen = new Set(prev.map((n) => n.toLowerCase()));
40125
+ const room = Math.max(0, max - prev.length);
40126
+ const deduped = [];
40127
+ for (const n of incoming) {
40128
+ const key = n.toLowerCase();
40129
+ if (seen.has(key)) continue;
40130
+ seen.add(key);
40131
+ deduped.push(n);
40132
+ if (deduped.length >= room) break;
40133
+ }
40134
+ return deduped.length > 0 ? [...prev, ...deduped] : prev;
40135
+ }
40121
40136
  function splitIntoTeams(names, count2) {
40122
40137
  const shuffled = shuffle(names);
40123
40138
  const teams = Array.from({ length: count2 }, () => []);
@@ -40191,29 +40206,14 @@ function TeamGenerator({
40191
40206
  const trimmed = input.trim();
40192
40207
  if (!trimmed) return;
40193
40208
  const newNames = trimmed.split(/[,\n]+/).map((n) => n.trim()).filter((n) => n.length > 0);
40194
- setNames((prev) => {
40195
- const room = Math.max(0, maxPlayers - prev.length);
40196
- return [...prev, ...newNames.slice(0, room)];
40197
- });
40209
+ setNames((prev) => mergeNames(prev, newNames, maxPlayers));
40198
40210
  setInput("");
40199
40211
  inputRef.current?.focus();
40200
40212
  }, [input, maxPlayers]);
40201
40213
  const importNames = useCallback50(
40202
40214
  (incoming) => {
40203
40215
  if (incoming.length === 0) return;
40204
- setNames((prev) => {
40205
- const seen = new Set(prev.map((n) => n.toLowerCase()));
40206
- const room = Math.max(0, maxPlayers - prev.length);
40207
- const deduped = [];
40208
- for (const n of incoming) {
40209
- const key = n.toLowerCase();
40210
- if (seen.has(key)) continue;
40211
- seen.add(key);
40212
- deduped.push(n);
40213
- if (deduped.length >= room) break;
40214
- }
40215
- return deduped.length > 0 ? [...prev, ...deduped] : prev;
40216
- });
40216
+ setNames((prev) => mergeNames(prev, incoming, maxPlayers));
40217
40217
  },
40218
40218
  [maxPlayers]
40219
40219
  );
@@ -40838,7 +40838,7 @@ function RoundRobinScheduler({
40838
40838
  useEffect62(() => {
40839
40839
  onTeamsChange?.(teams);
40840
40840
  }, [teams, onTeamsChange]);
40841
- const mergeNames = useCallback51(
40841
+ const mergeNames2 = useCallback51(
40842
40842
  (prev, incoming) => {
40843
40843
  const existing = new Set(prev.map((t) => t.toLowerCase()));
40844
40844
  const next = [...prev];
@@ -40864,20 +40864,20 @@ function RoundRobinScheduler({
40864
40864
  const names = raw.split(",").map((s) => s.trim()).filter((s) => s.length > 0);
40865
40865
  if (names.length === 0) return;
40866
40866
  if (!gateDiscard("add")) return;
40867
- setTeams((prev) => mergeNames(prev, names));
40867
+ setTeams((prev) => mergeNames2(prev, names));
40868
40868
  setInput("");
40869
40869
  setRounds([]);
40870
40870
  },
40871
- [mergeNames, gateDiscard]
40871
+ [mergeNames2, gateDiscard]
40872
40872
  );
40873
40873
  const importTeams = useCallback51(
40874
40874
  (incoming) => {
40875
40875
  if (incoming.length === 0) return;
40876
40876
  if (!gateDiscard("import")) return;
40877
- setTeams((prev) => mergeNames(prev, incoming));
40877
+ setTeams((prev) => mergeNames2(prev, incoming));
40878
40878
  setRounds([]);
40879
40879
  },
40880
- [mergeNames, gateDiscard]
40880
+ [mergeNames2, gateDiscard]
40881
40881
  );
40882
40882
  const handleKeyDown = useCallback51(
40883
40883
  (e) => {
@@ -41382,6 +41382,8 @@ function Scoreboard({
41382
41382
  onBeforeReset,
41383
41383
  onBeforeNewGame,
41384
41384
  nameMaxLength,
41385
+ scoreMin = -9999,
41386
+ scoreMax = 9999,
41385
41387
  labels,
41386
41388
  ariaLabel = "Scoreboard",
41387
41389
  className
@@ -41425,11 +41427,19 @@ function Scoreboard({
41425
41427
  useEffect63(() => {
41426
41428
  onTeamsChange?.(teams);
41427
41429
  }, [teams, onTeamsChange]);
41428
- const updateScore = useCallback52((index, delta) => {
41429
- setTeams(
41430
- (prev) => prev.map((t, i) => i === index ? { ...t, score: t.score + delta } : t)
41431
- );
41432
- }, []);
41430
+ const updateScore = useCallback52(
41431
+ (index, delta) => {
41432
+ setTeams(
41433
+ (prev) => prev.map(
41434
+ (t, i) => i === index ? {
41435
+ ...t,
41436
+ score: Math.min(scoreMax, Math.max(scoreMin, t.score + delta))
41437
+ } : t
41438
+ )
41439
+ );
41440
+ },
41441
+ [scoreMin, scoreMax]
41442
+ );
41433
41443
  const updateName = useCallback52((index, name) => {
41434
41444
  setTeams((prev) => prev.map((t, i) => i === index ? { ...t, name } : t));
41435
41445
  }, []);