@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.
package/dist/index.mjs CHANGED
@@ -31237,9 +31237,26 @@ function standardSeeding(participants) {
31237
31237
  const sorted = [...participants].sort(
31238
31238
  (a, b) => (a.seed ?? 999) - (b.seed ?? 999)
31239
31239
  );
31240
+ const n = sorted.length;
31240
31241
  const pairs = [];
31241
- for (let i = 0; i < sorted.length / 2; i++) {
31242
- pairs.push([sorted[i], sorted[sorted.length - 1 - i]]);
31242
+ if (n < 2 || (n & n - 1) !== 0) {
31243
+ for (let i = 0; i + 1 < n; i += 2) {
31244
+ pairs.push([sorted[i], sorted[i + 1]]);
31245
+ }
31246
+ return pairs;
31247
+ }
31248
+ let order = [1, 2];
31249
+ while (order.length < n) {
31250
+ const total = order.length * 2 + 1;
31251
+ const next = [];
31252
+ for (const r of order) {
31253
+ next.push(r);
31254
+ next.push(total - r);
31255
+ }
31256
+ order = next;
31257
+ }
31258
+ for (let i = 0; i < order.length; i += 2) {
31259
+ pairs.push([sorted[order[i] - 1], sorted[order[i + 1] - 1]]);
31243
31260
  }
31244
31261
  return pairs;
31245
31262
  }
@@ -31593,6 +31610,7 @@ function BracketGenerator({
31593
31610
  theme: theme5 = "blue",
31594
31611
  darkMode,
31595
31612
  seedingStrategy = "standard",
31613
+ resumeMatches,
31596
31614
  onMatchComplete,
31597
31615
  onChampion,
31598
31616
  onStateChange,
@@ -31671,6 +31689,47 @@ function BracketGenerator({
31671
31689
  );
31672
31690
  const [miniMapHidden, setMiniMapHidden] = useState71(false);
31673
31691
  const miniMapDragRef = useRef40(false);
31692
+ const resumeRef = useRef40(resumeMatches);
31693
+ const hydrateFromResume = useCallback45(() => {
31694
+ const resume = resumeRef.current;
31695
+ if (!resume?.length) return null;
31696
+ const n = effectiveParticipants.length;
31697
+ if (n < 2 || (n & n - 1) !== 0) return null;
31698
+ const rounds = Math.log2(n);
31699
+ if (resume.length !== n - 1) return null;
31700
+ const byId = new Map(effectiveParticipants.map((p) => [p.id, p]));
31701
+ const resolve = (id) => {
31702
+ if (!id) return null;
31703
+ const found = byId.get(id);
31704
+ if (found) return found;
31705
+ if (id.startsWith("__bye_")) {
31706
+ return { id, name: "BYE", seed: 999, isBye: true };
31707
+ }
31708
+ return void 0;
31709
+ };
31710
+ const matches = [];
31711
+ for (const rm of resume) {
31712
+ if (rm.round < 0 || rm.round >= rounds) return null;
31713
+ const a = resolve(rm.aId);
31714
+ const b = resolve(rm.bId);
31715
+ const winner = resolve(rm.winnerId);
31716
+ if (a === void 0 || b === void 0 || winner === void 0)
31717
+ return null;
31718
+ matches.push({
31719
+ id: rm.id,
31720
+ round: rm.round,
31721
+ slot: rm.slot,
31722
+ a,
31723
+ b,
31724
+ scoreA: rm.scoreA,
31725
+ scoreB: rm.scoreB,
31726
+ winner
31727
+ });
31728
+ }
31729
+ matches.sort((x, y) => x.round - y.round || x.slot - y.slot);
31730
+ const finalMatch = matches.find((m) => m.round === rounds - 1);
31731
+ return { matches, rounds, champion: finalMatch?.winner ?? null };
31732
+ }, [effectiveParticipants]);
31674
31733
  const seedBracket = useCallback45(() => {
31675
31734
  const { matches, rounds } = buildBracket(
31676
31735
  effectiveParticipants,
@@ -31781,12 +31840,47 @@ function BracketGenerator({
31781
31840
  onStateChange?.(next);
31782
31841
  prevBracketStateRef.current = next;
31783
31842
  }, [bracketState, onMatchComplete, onChampion, onStateChange]);
31843
+ const structuralKey = useMemo29(
31844
+ () => `${seedingStrategy}|${effectiveParticipants.map((p) => `${p.id}:${p.seed ?? ""}`).join(",")}`,
31845
+ [effectiveParticipants, seedingStrategy]
31846
+ );
31847
+ const seededKeyRef = useRef40(null);
31848
+ useEffect55(() => {
31849
+ if (seededKeyRef.current === structuralKey) {
31850
+ setBracketState((prev) => {
31851
+ if (!prev.matches.length) return prev;
31852
+ const byId = new Map(effectiveParticipants.map((p) => [p.id, p]));
31853
+ const swap = (p) => p && !p.isBye ? byId.get(p.id) ?? p : p;
31854
+ let changed = false;
31855
+ const matches = prev.matches.map((m) => {
31856
+ const a = swap(m.a);
31857
+ const b = swap(m.b);
31858
+ const winner = swap(m.winner);
31859
+ if (a === m.a && b === m.b && winner === m.winner) return m;
31860
+ changed = true;
31861
+ return { ...m, a, b, winner };
31862
+ });
31863
+ if (!changed) return prev;
31864
+ return { ...prev, matches, champion: swap(prev.champion) };
31865
+ });
31866
+ return;
31867
+ }
31868
+ seededKeyRef.current = structuralKey;
31869
+ if (autoRef.current) clearTimeout(autoRef.current);
31870
+ setSimulating(false);
31871
+ const resumed = hydrateFromResume();
31872
+ resumeRef.current = void 0;
31873
+ if (resumed) {
31874
+ setBracketState(resumed);
31875
+ } else {
31876
+ seedBracket();
31877
+ }
31878
+ }, [structuralKey, effectiveParticipants, hydrateFromResume, seedBracket]);
31784
31879
  useEffect55(() => {
31785
- seedBracket();
31786
31880
  return () => {
31787
31881
  if (autoRef.current) clearTimeout(autoRef.current);
31788
31882
  };
31789
- }, [seedBracket]);
31883
+ }, []);
31790
31884
  useLayoutEffect2(() => {
31791
31885
  if (contentSize.w > 0) {
31792
31886
  const timer = setTimeout(fit, 50);
@@ -40569,6 +40663,27 @@ function generateSchedule(teams, roundLabel) {
40569
40663
  }
40570
40664
  return rounds;
40571
40665
  }
40666
+ function hydrateResumeRounds(resume, teams) {
40667
+ if (!resume?.length || teams.length < 2) return [];
40668
+ const expected = teams.length % 2 === 0 ? teams.length - 1 : teams.length;
40669
+ if (resume.length !== expected) return [];
40670
+ const roster = new Set(teams.map((t) => t.toLowerCase()));
40671
+ for (const round of resume) {
40672
+ for (const m of round.matchups) {
40673
+ for (const side of [m.home, m.away]) {
40674
+ if (side !== BYE_TOKEN && !roster.has(side.toLowerCase())) return [];
40675
+ }
40676
+ }
40677
+ }
40678
+ return resume.map((round) => ({
40679
+ label: round.label,
40680
+ matchups: round.matchups.map((m) => ({
40681
+ home: m.home,
40682
+ away: m.away,
40683
+ isBye: m.home === BYE_TOKEN || m.away === BYE_TOKEN
40684
+ }))
40685
+ }));
40686
+ }
40572
40687
  var DEFAULT_LABELS4 = {
40573
40688
  inputPlaceholder: "Team or player name (comma-separated for batch)",
40574
40689
  addButton: "Add",
@@ -40589,6 +40704,7 @@ function RoundRobinScheduler({
40589
40704
  darkMode,
40590
40705
  minTeams = 3,
40591
40706
  defaultTeams = [],
40707
+ resumeRounds,
40592
40708
  onTeamsChange,
40593
40709
  onGenerate,
40594
40710
  labels,
@@ -40602,7 +40718,9 @@ function RoundRobinScheduler({
40602
40718
  const L = { ...DEFAULT_LABELS4, ...labels };
40603
40719
  const [teams, setTeams] = useState80(defaultTeams);
40604
40720
  const [input, setInput] = useState80("");
40605
- const [rounds, setRounds] = useState80([]);
40721
+ const [rounds, setRounds] = useState80(
40722
+ () => hydrateResumeRounds(resumeRounds, defaultTeams)
40723
+ );
40606
40724
  const inputRef = useRef49(null);
40607
40725
  useEffect64(() => {
40608
40726
  onTeamsChange?.(teams);
@@ -41128,6 +41246,8 @@ function Scoreboard({
41128
41246
  maxTeams = 8,
41129
41247
  scoreButtons = [-1, 1, 5, 10],
41130
41248
  defaultTeams,
41249
+ initialRound = 1,
41250
+ initialHistory,
41131
41251
  onTeamsChange,
41132
41252
  onRoundAdvance,
41133
41253
  onNewGame,
@@ -41143,8 +41263,12 @@ function Scoreboard({
41143
41263
  const [teams, setTeams] = useState81(
41144
41264
  () => defaultTeams && defaultTeams.length >= minTeams ? defaultTeams.slice(0, maxTeams) : createDefaultTeams(seedCount, L.teamNamePrefix)
41145
41265
  );
41146
- const [round, setRound] = useState81(1);
41147
- const [history, setHistory] = useState81([]);
41266
+ const [round, setRound] = useState81(
41267
+ () => Number.isFinite(initialRound) ? Math.max(1, Math.floor(initialRound)) : 1
41268
+ );
41269
+ const [history, setHistory] = useState81(
41270
+ () => initialHistory && initialHistory.length > 0 ? initialHistory.slice() : []
41271
+ );
41148
41272
  const [historyOpen, setHistoryOpen] = useState81(false);
41149
41273
  const rootRef = useRef50(null);
41150
41274
  const [containerWidth, setContainerWidth] = useState81(0);