@digilogiclabs/saas-factory-ui 2.0.0 → 2.2.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.
@@ -31140,9 +31140,26 @@ function standardSeeding(participants) {
31140
31140
  const sorted = [...participants].sort(
31141
31141
  (a, b) => (a.seed ?? 999) - (b.seed ?? 999)
31142
31142
  );
31143
+ const n = sorted.length;
31143
31144
  const pairs = [];
31144
- for (let i = 0; i < sorted.length / 2; i++) {
31145
- pairs.push([sorted[i], sorted[sorted.length - 1 - i]]);
31145
+ if (n < 2 || (n & n - 1) !== 0) {
31146
+ for (let i = 0; i + 1 < n; i += 2) {
31147
+ pairs.push([sorted[i], sorted[i + 1]]);
31148
+ }
31149
+ return pairs;
31150
+ }
31151
+ let order = [1, 2];
31152
+ while (order.length < n) {
31153
+ const total = order.length * 2 + 1;
31154
+ const next = [];
31155
+ for (const r of order) {
31156
+ next.push(r);
31157
+ next.push(total - r);
31158
+ }
31159
+ order = next;
31160
+ }
31161
+ for (let i = 0; i < order.length; i += 2) {
31162
+ pairs.push([sorted[order[i] - 1], sorted[order[i + 1] - 1]]);
31146
31163
  }
31147
31164
  return pairs;
31148
31165
  }
@@ -31496,6 +31513,7 @@ function BracketGenerator({
31496
31513
  theme: theme5 = "blue",
31497
31514
  darkMode,
31498
31515
  seedingStrategy = "standard",
31516
+ resumeMatches,
31499
31517
  onMatchComplete,
31500
31518
  onChampion,
31501
31519
  onStateChange,
@@ -31574,6 +31592,47 @@ function BracketGenerator({
31574
31592
  );
31575
31593
  const [miniMapHidden, setMiniMapHidden] = useState68(false);
31576
31594
  const miniMapDragRef = useRef41(false);
31595
+ const resumeRef = useRef41(resumeMatches);
31596
+ const hydrateFromResume = useCallback42(() => {
31597
+ const resume = resumeRef.current;
31598
+ if (!resume?.length) return null;
31599
+ const n = effectiveParticipants.length;
31600
+ if (n < 2 || (n & n - 1) !== 0) return null;
31601
+ const rounds = Math.log2(n);
31602
+ if (resume.length !== n - 1) return null;
31603
+ const byId = new Map(effectiveParticipants.map((p) => [p.id, p]));
31604
+ const resolve = (id) => {
31605
+ if (!id) return null;
31606
+ const found = byId.get(id);
31607
+ if (found) return found;
31608
+ if (id.startsWith("__bye_")) {
31609
+ return { id, name: "BYE", seed: 999, isBye: true };
31610
+ }
31611
+ return void 0;
31612
+ };
31613
+ const matches = [];
31614
+ for (const rm of resume) {
31615
+ if (rm.round < 0 || rm.round >= rounds) return null;
31616
+ const a = resolve(rm.aId);
31617
+ const b = resolve(rm.bId);
31618
+ const winner = resolve(rm.winnerId);
31619
+ if (a === void 0 || b === void 0 || winner === void 0)
31620
+ return null;
31621
+ matches.push({
31622
+ id: rm.id,
31623
+ round: rm.round,
31624
+ slot: rm.slot,
31625
+ a,
31626
+ b,
31627
+ scoreA: rm.scoreA,
31628
+ scoreB: rm.scoreB,
31629
+ winner
31630
+ });
31631
+ }
31632
+ matches.sort((x, y) => x.round - y.round || x.slot - y.slot);
31633
+ const finalMatch = matches.find((m) => m.round === rounds - 1);
31634
+ return { matches, rounds, champion: finalMatch?.winner ?? null };
31635
+ }, [effectiveParticipants]);
31577
31636
  const seedBracket = useCallback42(() => {
31578
31637
  const { matches, rounds } = buildBracket(
31579
31638
  effectiveParticipants,
@@ -31684,12 +31743,47 @@ function BracketGenerator({
31684
31743
  onStateChange?.(next);
31685
31744
  prevBracketStateRef.current = next;
31686
31745
  }, [bracketState, onMatchComplete, onChampion, onStateChange]);
31746
+ const structuralKey = useMemo27(
31747
+ () => `${seedingStrategy}|${effectiveParticipants.map((p) => `${p.id}:${p.seed ?? ""}`).join(",")}`,
31748
+ [effectiveParticipants, seedingStrategy]
31749
+ );
31750
+ const seededKeyRef = useRef41(null);
31751
+ useEffect53(() => {
31752
+ if (seededKeyRef.current === structuralKey) {
31753
+ setBracketState((prev) => {
31754
+ if (!prev.matches.length) return prev;
31755
+ const byId = new Map(effectiveParticipants.map((p) => [p.id, p]));
31756
+ const swap = (p) => p && !p.isBye ? byId.get(p.id) ?? p : p;
31757
+ let changed = false;
31758
+ const matches = prev.matches.map((m) => {
31759
+ const a = swap(m.a);
31760
+ const b = swap(m.b);
31761
+ const winner = swap(m.winner);
31762
+ if (a === m.a && b === m.b && winner === m.winner) return m;
31763
+ changed = true;
31764
+ return { ...m, a, b, winner };
31765
+ });
31766
+ if (!changed) return prev;
31767
+ return { ...prev, matches, champion: swap(prev.champion) };
31768
+ });
31769
+ return;
31770
+ }
31771
+ seededKeyRef.current = structuralKey;
31772
+ if (autoRef.current) clearTimeout(autoRef.current);
31773
+ setSimulating(false);
31774
+ const resumed = hydrateFromResume();
31775
+ resumeRef.current = void 0;
31776
+ if (resumed) {
31777
+ setBracketState(resumed);
31778
+ } else {
31779
+ seedBracket();
31780
+ }
31781
+ }, [structuralKey, effectiveParticipants, hydrateFromResume, seedBracket]);
31687
31782
  useEffect53(() => {
31688
- seedBracket();
31689
31783
  return () => {
31690
31784
  if (autoRef.current) clearTimeout(autoRef.current);
31691
31785
  };
31692
- }, [seedBracket]);
31786
+ }, []);
31693
31787
  useLayoutEffect2(() => {
31694
31788
  if (contentSize.w > 0) {
31695
31789
  const timer = setTimeout(fit, 50);
@@ -40267,6 +40361,27 @@ function generateSchedule(teams, roundLabel) {
40267
40361
  }
40268
40362
  return rounds;
40269
40363
  }
40364
+ function hydrateResumeRounds(resume, teams) {
40365
+ if (!resume?.length || teams.length < 2) return [];
40366
+ const expected = teams.length % 2 === 0 ? teams.length - 1 : teams.length;
40367
+ if (resume.length !== expected) return [];
40368
+ const roster = new Set(teams.map((t) => t.toLowerCase()));
40369
+ for (const round of resume) {
40370
+ for (const m of round.matchups) {
40371
+ for (const side of [m.home, m.away]) {
40372
+ if (side !== BYE_TOKEN && !roster.has(side.toLowerCase())) return [];
40373
+ }
40374
+ }
40375
+ }
40376
+ return resume.map((round) => ({
40377
+ label: round.label,
40378
+ matchups: round.matchups.map((m) => ({
40379
+ home: m.home,
40380
+ away: m.away,
40381
+ isBye: m.home === BYE_TOKEN || m.away === BYE_TOKEN
40382
+ }))
40383
+ }));
40384
+ }
40270
40385
  var DEFAULT_LABELS4 = {
40271
40386
  inputPlaceholder: "Team or player name (comma-separated for batch)",
40272
40387
  addButton: "Add",
@@ -40287,6 +40402,7 @@ function RoundRobinScheduler({
40287
40402
  darkMode,
40288
40403
  minTeams = 3,
40289
40404
  defaultTeams = [],
40405
+ resumeRounds,
40290
40406
  onTeamsChange,
40291
40407
  onGenerate,
40292
40408
  labels,
@@ -40300,7 +40416,9 @@ function RoundRobinScheduler({
40300
40416
  const L = { ...DEFAULT_LABELS4, ...labels };
40301
40417
  const [teams, setTeams] = useState77(defaultTeams);
40302
40418
  const [input, setInput] = useState77("");
40303
- const [rounds, setRounds] = useState77([]);
40419
+ const [rounds, setRounds] = useState77(
40420
+ () => hydrateResumeRounds(resumeRounds, defaultTeams)
40421
+ );
40304
40422
  const inputRef = useRef50(null);
40305
40423
  useEffect62(() => {
40306
40424
  onTeamsChange?.(teams);
@@ -40826,6 +40944,8 @@ function Scoreboard({
40826
40944
  maxTeams = 8,
40827
40945
  scoreButtons = [-1, 1, 5, 10],
40828
40946
  defaultTeams,
40947
+ initialRound = 1,
40948
+ initialHistory,
40829
40949
  onTeamsChange,
40830
40950
  onRoundAdvance,
40831
40951
  onNewGame,
@@ -40841,8 +40961,12 @@ function Scoreboard({
40841
40961
  const [teams, setTeams] = useState78(
40842
40962
  () => defaultTeams && defaultTeams.length >= minTeams ? defaultTeams.slice(0, maxTeams) : createDefaultTeams(seedCount, L.teamNamePrefix)
40843
40963
  );
40844
- const [round, setRound] = useState78(1);
40845
- const [history, setHistory] = useState78([]);
40964
+ const [round, setRound] = useState78(
40965
+ () => Number.isFinite(initialRound) ? Math.max(1, Math.floor(initialRound)) : 1
40966
+ );
40967
+ const [history, setHistory] = useState78(
40968
+ () => initialHistory && initialHistory.length > 0 ? initialHistory.slice() : []
40969
+ );
40846
40970
  const [historyOpen, setHistoryOpen] = useState78(false);
40847
40971
  const rootRef = useRef51(null);
40848
40972
  const [containerWidth, setContainerWidth] = useState78(0);