// dynamic-help-system.js - Context-aware help system for tournament manager /** * HELP CONTENT DATABASE * Organized by page and context with progressive disclosure */ const HELP_CONTENT = { // Setup Page Help setup: { title: "Tournament Setup", overview: "Start a tournament, or pick one up where you left off. The tournament you're running is at the top, with its next step; starting a new one is a quiet line under it. The panel to act on is framed: the tournament while it is New or Active; once it is completed, the New tournament form moves to the top, open and framed, with Create tournament as the main button (the same when nothing is loaded).", sections: { current: { title: "Current Tournament", content: `

The loaded tournament, with its status (New, Active or Completed; grey, orange or green) and a short fact beside it: that the bracket isn't drawn, how many matches are completed, or who won. Under the name: players, bracket size, and the matches being played now.

Next step changes with the status:

Export tournament: Save the tournament as a JSON file, for backup or to move it to another computer.

Backup to server: Shown when the Tournament Manager runs on a server. Uploads the tournament, or a tournament file.

Reset tournament: Clears all bracket progress and keeps the players. You type the tournament's name to confirm. Not offered once a tournament is completed: its results are final (to correct one, Developer Console → Toggle Read-Only, then undo the match).

⚠️ Warning: Reset permanently deletes all match results and standings.

` }, creation: { title: "New Tournament", content: `

Click + New tournament to open the form. Once the tournament is completed (or when nothing is loaded), the form is always open at the top, under Start a new tournament.

  1. Enter a name (e.g., "Thursday Cup #42")
  2. Check the date (defaults to today)
  3. Click Create tournament. It becomes the current tournament at the top, and its next step is Register players

Import a tournament file: Load a tournament from an exported JSON file. It is next to + New tournament.

` }, recent: { title: "Tournaments", content: `

Every tournament on this computer, newest first, with date, format, players, status and Analytics.

Server: When the Tournament Manager runs on a server, a Server tab lists the tournaments shared there, with Import (or Re-import if it's already on this computer).

Storage: How full this computer's browser storage is. Click it for details.

` }, results: { title: "Match History", content: `

The current tournament's completed matches, latest first.

` } } }, // Registration Page Help registration: { title: "Player Registration", overview: "Pick tonight's players from the club's player database and mark who has paid. Once the bracket is drawn, the page shows the Leaderboard.", sections: { adding: { title: "Adding Players", content: `
  1. Type in Add from the database: it searches short, first, last and previous names
  2. Click a player, or press Enter to add the first match
  3. Not in the database yet? + New player (or Enter when no one matches) creates them and adds them
  4. Click a player in Players to switch between Paid and Unpaid; × removes an unpaid player

Next step shows the player, paid and unpaid counts, and what's needed before the draw. Open bracket works once there are at least 4 paid players. Only paid players go into the bracket.

Payment QR code: Add payment.png to the images/ folder (Swish, bank details, etc.) and it is shown next to the players. Without it, the panel is hidden.

` }, database: { title: "The Player Database", content: `

Every player has a permanent ID, a short name (what the bracket, Match Controls, the Chalker and the Leaderboard show; it must be unique) and a first and last name. Analytics follows the ID, so a player's results stay together whatever they are called.

The database is kept on this computer and travels with every tournament export and upload, so a shared Analytics instance shows the current names.

` }, statistics: { title: "Player Statistics", content: `

Once the bracket is drawn, click a player in the Leaderboard to record or correct:

Statistics count towards the points.

Player arrived late? Under the player list once the bracket is drawn. It explains how to register a late arrival from the Developer Console.

` }, results: { title: "Leaderboard", content: `

Export CSV: The Leaderboard as a spreadsheet, for records or league management.

Export JSON: Results and match history, for importing into other systems.

Rankings update as matches are completed.

` } } }, // Tournament Page Help tournament: { title: "Tournament Management", overview: "Run the tournament bracket, manage matches, and track live progress.", sections: { bracket: { title: "Tournament Setup & Bracket Generation", content: `

Setting Up Your Tournament:

  1. Open the Match Controls tab in the header
  2. Before the draw it shows the players: click a name to mark them paid or unpaid, click × on an unpaid player to remove them, or add a player (found in the player database, or created). Everyone must be paid before the draw: the draw buttons say how many are still unpaid
  3. If seeding is on (Global Settings → Tournaments → Seeding), tick Seed the draw by ranking to keep the best players apart. The panel shows which earlier tournaments the ranking comes from, and who would be seeded
  4. When ready, pick a format under Pick a format (each card says what it means for tonight's players; the picked one shows how it works, roughly how many matches, and its options), then click Draw. It opens on the format of your last tournament

Tournament Formats:

Bracket Sizes:

Qualifiers (33 to 48 players): the players above 32 make it in through qualifier matches (Q1, Q2 …) before round 1, one for each player above 32: 40 players play 8 qualifiers, 48 play 16. The players in them are drawn at random (with seeding, never the seeds; with All, the lowest-ranked), and each winner takes the place in round 1 that shows "Winner Q3". A qualifier only decides who gets to play: the loser is not qualified (placed 33rd–48th), and nothing in a qualifier counts, for the winner either (no achievements, no matches or legs in the statistics, never in Analytics). Its length is the format's regular rounds. Double elimination with qualifiers is drawn with the finals in the middle.

Cup and Plate: pick the Cup and Plate card before you draw (from 5 players). The bracket is then the Cup, and every round 1 loser goes on to the Plate, a second knockout of half the size, at a fixed place: the losers of round 1 match 1 and 2 meet in the Plate's first match, and so on, so a Plate match can start as soon as its two round 1 matches are played. A bye's place in the Plate is a walkover. The Plate is drawn back to back with the Cup, the two finals facing each other. Its places follow the shared places (with 8 players the Plate's finalists are 5th–6th, its bronze pair 7th–8th), and the finished view names the Plate winner. Match lengths and bronze finals as single elimination.

Swiss: pick the Swiss card, with Rounds (by players: 3 for 5 to 8, 4 for 9 to 16, 5 for 17 to 32) and Finish (semifinals and final for the top four, or the table decides). Round 1 is random (with seeding: the top half against the bottom half); every later round is drawn by itself when the last match of the round before is entered, pairing the table 1st v 2nd, 3rd v 4th, skipping anyone already played. An odd number gives one bye a round (a win), never twice to the same player. The table: wins, opponents' wins, leg difference, legs won. Undoing a result of the round just finished takes back the next round, as long as none of its matches has started.

💡 Tip: Match Controls adapts based on tournament state - use it for both setup and active tournament management!

` }, navigation: { title: "Bracket Navigation & Quick Access", content: `

Header:

Moving around:

Selecting a match:

Finals Right | Middle: the finals at the right edge, or in the middle: between the frontside and backside in double elimination; in single elimination, the two halves of the bracket face a final in the middle, with the bronze final under it. Also in Global Settings → Bracket.

` }, matches: { title: "Match Management", content: `

Match Colours:

Running Matches: matches are run from Match Controls: start, lane, referee, winner and Chalker handover. The bracket shows where things stand.

Chalker QR: click QR on a LIVE match in Match Controls. The Chalker scans it to receive player names, format, and lane/referee automatically.

` }, completion: { title: "Completing & Correcting Matches", content: `

Selecting Winners:

  1. In Match Controls, click the winner's name (Wins) on the match's lane tile
  2. Enter leg scores (optional but recommended)
  3. Click "Confirm Winner"

Scan Results QR: If the match was scored on the Chalker, click Scan Results QR in the confirmation dialog to import the result and player achievements directly from the Chalker's QR code — no manual entry needed.

Correcting Match Results:

💡 Tip: Undo match only appears when it is safe to use.

` }, lanes: { title: "Lane/Referee Management", content: `

Assigning Lanes/Referees:

In the bracket: a live match shows its lane (L1, L2 …); hover it to see the referee.

QR Integration: When a lane or referee is assigned, it is automatically included in the Chalker assignment QR code. The Chalker displays the lane in its info bar and the referee name throughout the match.

💡 Tip: Assign lanes to organize physical dartboard usage and avoid conflicts.

` }, groups: { title: "Round Robin", content: `

Two ways to play (Global Settings → Round Robin): Groups and cups or One group. A tournament keeps the setting it was drawn with.

One group: everybody plays everybody in a fixed order, each match with a planned referee from the group; the table decides the placings when the last match is played. 3 to 8 players.

Groups and cups: players are drawn into groups of up to four, or five or six as set in Largest group (as few groups as fit, an even number with Top two; by ranking when seeding is ticked, otherwise at random). Everybody plays everybody in their group. Then the A cup and the B cup: single elimination with a bronze final, drawn with the final in the middle. To the A cup: Top half (the default): everyone ranked across the groups and split into two cups of the same size, or the top two of each group (the rest play the B cup).

The group stage:

Draw the cups: when the last group match is played, Match Controls shows both cups' seeded fields: group winners first, then runners-up, and so on, ranked across the groups by win rate, then leg difference per match (with Top half the A cup takes the first half of that order; with top two, the B cup has the thirds, then the fourths, and so on). Switch off Play the B cup to play without it (whether it starts on is a Global Setting); then the A cup takes the top two of each group, also with Top half, and the rest are placed after it by their group results. Nothing is drawn until you click Draw the cups.

The cups: A cup and B cup side by side in Match Controls, on the same lanes. Round 1's referees are planned (players with a bye, then the players from the bottom of the round, then the first losers), later rounds take losers as they come. Cups on the bracket page shows both.

Undo: group results can be undone until the cups are drawn. Undo the cup draw (Match Controls) is there until a cup match has been started or played; it opens the group stage again. Cup matches undo as in any bracket.

Placings: everyone is placed. A cup: 1st and 2nd from the final, 3rd and 4th from the bronze final. B cup: its final pair 5th–6th, its bronze pair 7th–8th. Then the A cup's other losers (the latest round first), then the B cup's, each round ordered by group results, then anyone who played no cup. Places run on as shared places (9th–12th, 13th–16th …), so places a small B cup can't fill go to the A cup's quarterfinal losers; without a B cup they take 5th–8th. Everyone gets the points for taking part.

Waits: a match waits (Match Controls says why) while one of its players is the referee of an earlier match in the group or cup, or while its planned referee isn't known yet (the loser of a match still to be played). Choose another referee to start it anyway. A referee chosen for a match that hasn't started is only a plan: it blocks no one until that match is live.

Match length: group matches and the cups have their own settings (Global Settings → Match length → Groups and cups): Cup rounds (every round before the semifinals), Cup semifinal, Cup bronze final and Cup final, the same for the A and the B cup.

` }, matchControls: { title: "Match Controls Interface", content: `

Accessing Match Controls:

While the tournament runs:

When the tournament is finished: the podium with Most 180s, Shortest leg and Highest out under it, and a plaque with the club, tournament and date; then 4th, 5th–6th and 7th–8th (the places that still score placement points) and the night's awards, one per row (Most points, Most matches won, Backside run, Best average, Most tons, Lollipops, Busiest lane), the night in numbers, and Tournament Analytics.

Real-time Updates: Interface refreshes automatically after each action.

` } } }, // Config Page Help config: { title: "Global Settings", overview: "Settings for this computer, used by every tournament. Change what you need, then click Save changes in the bar at the bottom. On a shared Analytics instance the page is called Custom Settings and shows the Points only: members try other point values there, kept in their own browser, and see them in Analytics with Custom.", sections: { application: { title: "Saving and the Page Layout", content: `

Sections: Club, Tournaments, Match day, Bracket, Chalker, Server & backup and Developer. Click a name in the list on the left to jump to it.

Saving:

Club: the club name is shown in the page title and at the top of the bracket. For a logo, put a square logo.png, logo.jpg, logo.jpeg or logo.svg in the images folder.

Match day:

Bracket: Finals position, Right or Middle (double elimination). The same setting as the Finals toggle in the bracket header.

Developer: Developer Console adds a Console tab to the Tournament Bracket page, with diagnostics, validation checks, lane usage and transaction tools.

` }, roundRobin: { title: "Round Robin", content: `

Round Robin (Tournaments) shows while the format is ticked under Formats to offer.

A tournament keeps the settings it was drawn with; a change here applies to the next draw.

` }, seeding: { title: "Seeding", content: `

Seeding (Tournaments) lets the draw keep the best players apart, in every format. Off: the draw is always random. Available: the option is offered, and you tick it. On: it is ticked to start with.

Seeded players is how many of the best players are seeded, as a share of the bracket: 1/8, 1/4 or 1/2 (2, 4 or 8 players in a 16-player bracket). All seeds everyone with a ranking, so the top seed meets the bottom seed. Seeded players can't meet in round 1, and everyone else is drawn at random. You can change it for each draw.

Byes go to the best seeds. Players with no ranking are never seeded, and only get a bye if there are more byes than seeds.

Round Robin seeds every ranked player into the groups in snake order (the best in group A, the second in B, …, then back again), or into the one group's order; Seeded players doesn't apply. The cups are seeded from the group tables.

The ranking is the Leaderboard's points over earlier tournaments with the same name, from this browser. The name word is taken from the tournament's name ("Måndagscup", "Måndagscup week 43" and "NewTon Måndagscup" are the same cup), and tournaments with "Final" in the name never count. It looks at the current half-year, or the previous one for the first match of a season. In Match Controls you can change the word or the period, and tick tournaments by hand.

💡 New players are never seeded; Match Controls names them. With no earlier tournaments in this browser, or none ticked, the draw is random. To seed from the club's history, restore a backup on this computer first.

` }, formats: { title: "Tournament Formats", content: `

Formats to offer (Tournaments) controls which formats can be picked in Match Controls' Pick a format when starting a tournament. A format not offered is still shown there, greyed out. Cup and Plate is one of them.

Swiss (Tournaments, while Swiss is offered): Rounds (by players, or a fixed 3 to 6) and Finish (semifinals and final for the top four, or the table decides): how the draw starts; both can be changed for each draw.

Untick the ones your club never plays so they can't be picked by mistake. At least one format always stays available.

💡 Existing tournaments are unaffected: a tournament already created in a hidden format still opens, renders and exports as normal.

` }, matches: { title: "Match Length", content: `

Match length (Tournaments) sets best-of legs per round, with − and + (Bo1 to Bo21).

Reset to defaults fills in the standard lengths; click Save changes to keep them. New lengths apply to matches that haven't started.

` }, points: { title: "Points", content: `

Points (Tournaments):

Reset to defaults fills in the standard values; click Save changes to keep them.

` }, handover: { title: "Chalker Settings", content: `

Chalker settings are sent to the Chalker when a match starts:

Handover, how a match reaches the Chalker:

` }, server: { title: "Server & Backup", content: `

Only when running the Docker image. These settings do nothing if you opened the app as a file.

💡 If Shared Tournaments stays empty on a Linux server, the container could not write to its tournament folder. Its startup log says so and gives the command to fix it.

` } } }, // Analytics Page Help history: { title: "Analytics", overview: "Explore your tournament history — standings across a season, player records, and every completed match. Analytics only ever reads your results; it never changes them.", sections: { views: { title: "The Four Views", content: `

Buttons along the top switch between them. All four show the same set of tournaments — whichever ones the Lens currently selects.

` }, lens: { title: "The Lens — Choosing What You Are Looking At", content: `

The Lens decides which tournaments the numbers are drawn from. Everything else follows it — Dashboard, Leaderboard and Players all reflect whatever the Lens currently selects.

It sits at the top of every view, under the view buttons:

To leave out single tournaments, click Choose tournaments and untick them in the Register's tournament list.

💡 To the left of the Lens it says how many tournaments are counted, and why — so if a number looks wrong, check there first. It is the usual explanation. The bar turns orange when you are not seeing everything.

Analytics always opens on the current half-year (the previous one until the new half-year has its first tournament). What you change lasts until you leave the page.

` }, points: { title: "How Points Are Counted", content: `

The Points buttons sit beside the view buttons, and change every figure on screen as soon as you touch them.

Achievement points — 180s, high outs, short legs, tons — always count.

💡 None of this alters a stored result. It changes how the totals are worked out for display, nothing more.

` }, leaderboard: { title: "Leaderboard", content: `

One row per player across the selected tournaments. Click any column heading to sort by it. In points order, a line marks the top 16.

Export CSV and Export JSON save the table as it currently stands — same Lens, same point settings — for a spreadsheet or a club website.

` }, players: { title: "Players", content: `

The player list shows everyone who appears in the selected tournaments, with tournaments played and their win/loss record. Tick one player to see their profile, or up to six to compare them; the box at the top ticks the six best. The page opens on the best player in the lens (or those sharing the top points).

The profile has the player's Leaderboard figures, how often they finished in each place, and every tournament they played, with their placing and points. Click a tournament to open it.

Charts show how the player has done over the tournaments in the Lens: their last 10 finishes, then six cards (Position in the standings, Points, Finishes, Average, Matches, Highlights). Click a card for its chart; hover or tap a tournament for the figures. + Compare with adds up to five more players, The field adds the median of everyone who played (Points and Average), and Full screen gives the chart the whole screen. Ticking several players in the list compares them the same way.

Players are matched by name across tournaments, so someone entered as "Dave" in one and "dave " in another is treated as the same person. Genuinely different spellings are not — consistent names are worth the small effort at registration.

` }, tournaments: { title: "Tournament List", content: `

Each row shows a finalized tournament — date, format, players, matches, points and the winner. Unticked tournaments are left out of every view.

` }, matches: { title: "Match Detail", content: `

Click any tournament to see its matches. Click a match to open the detail view.

` }, register: { title: "Export & Import Register", content: `

Export Register: Download the full match history as a JSON file — useful for backups or moving to a new device.

Import Register: Merge a previously exported file into the current history. Existing records with the same match ID are overwritten; everything else is untouched.

` } } }, // Contextual Help for Common Scenarios scenarios: { title: "Common Scenarios", overview: "Guidance for getting started, and for the problems that come up most often.", sections: { firstTime: { title: "First Time Setup", content: `

Welcome to Tournament Manager!

Quick Start Guide:

  1. Setup: Create your first tournament with name and date
  2. Registration: Add players and mark them as paid
  3. Tournament: Generate bracket and start managing matches
  4. Config: Customize point values and settings (optional)

💡 Need help? Each page has specific guidance - click the help button (?) for detailed instructions.

` }, troubleshooting: { title: "Common Issues", content: `

Can't generate bracket:

Match won't start:

Points not calculating correctly:

` } } } }; /** * HELP SYSTEM STATE MANAGEMENT */ let helpState = { isVisible: false, currentPage: 'setup', currentSection: null, position: { x: 100, y: 100 } }; /** * INITIALIZE HELP SYSTEM * Call this after DOM is loaded */ function initializeHelpSystem() { console.log('🔧 Initializing dynamic help system...'); // Initialize help icons with tooltips initializeHelpIcons(); // Create help modal createHelpModal(); // Setup context detection setupContextDetection(); // Add keyboard shortcuts setupHelpKeyboardShortcuts(); console.log('✓ Help system initialized'); } /** * INITIALIZE HELP ICONS * Add click functionality to help info icons */ function initializeHelpIcons() { const helpIcons = document.querySelectorAll('.help-info-icon'); helpIcons.forEach(icon => { const pageId = icon.getAttribute('data-page'); if (!pageId) return; // Click icon to open help icon.addEventListener('click', (e) => { e.stopPropagation(); showHelp(pageId); }); }); } /** * CREATE HELP MODAL * Build the floating help interface */ function createHelpModal() { const modal = document.createElement('div'); modal.id = 'dynamicHelpModal'; modal.className = 'help-modal'; modal.style.cssText = ` display: none; position: fixed; top: 50px; right: 50px; width: 400px; max-height: 80vh; background: #ffffff; border: 2px solid #ff6b35; border-radius: 12px; box-shadow: 0 8px 30px rgba(0,0,0,0.3); z-index: 1000; overflow: hidden; resize: both; min-width: 350px; min-height: 200px; `; modal.innerHTML = `
Tournament Help
Press F1 for help • ESC to close
`; document.body.appendChild(modal); // Setup modal interactions setupHelpModalInteractions(modal); } /** * SETUP HELP MODAL INTERACTIONS */ function setupHelpModalInteractions(modal) { const header = modal.querySelector('.help-header'); const closeBtn = modal.querySelector('#helpClose'); const minimizeBtn = modal.querySelector('#helpMinimize'); const scenariosBtn = modal.querySelector('#helpShowScenarios'); // Make modal draggable let isDragging = false; let dragOffset = { x: 0, y: 0 }; header.addEventListener('mousedown', (e) => { isDragging = true; dragOffset.x = e.clientX - modal.offsetLeft; dragOffset.y = e.clientY - modal.offsetTop; modal.style.cursor = 'grabbing'; }); document.addEventListener('mousemove', (e) => { if (!isDragging) return; const newX = e.clientX - dragOffset.x; const newY = e.clientY - dragOffset.y; // Keep modal within viewport const maxX = window.innerWidth - modal.offsetWidth; const maxY = window.innerHeight - modal.offsetHeight; modal.style.left = Math.max(0, Math.min(newX, maxX)) + 'px'; modal.style.top = Math.max(0, Math.min(newY, maxY)) + 'px'; modal.style.right = 'auto'; // Remove right positioning }); document.addEventListener('mouseup', () => { isDragging = false; modal.style.cursor = 'auto'; }); // Close button closeBtn.addEventListener('click', hideHelp); // Minimize button minimizeBtn.addEventListener('click', () => { const content = modal.querySelector('.help-content'); if (content.style.display === 'none') { content.style.display = 'block'; minimizeBtn.textContent = '−'; modal.style.height = 'auto'; } else { content.style.display = 'none'; minimizeBtn.textContent = '+'; modal.style.height = '60px'; } }); // Scenarios button scenariosBtn.addEventListener('click', () => { showHelpSection('scenarios', 'troubleshooting'); }); } /** * Point the help system at a new page (6.4). * * Called from showPage() — the single place that moves the `.page.active` class — * so the help system learns about a navigation directly instead of inferring it. * This replaced a MutationObserver on the whole body subtree (childList + subtree + * class attributes), which woke on *every* DOM change in the app — each bracket * render, each clock tick — and ran a `querySelector('.page.active')` every time, * to detect an event the app already knows it is causing. * * Safe to call before the help system is initialized: it only updates state, and * updateHelpContext() does nothing while the help modal is closed. * * @param {string} pageId - id of the page now active (e.g. 'setup', 'tournament') * @returns {void} */ function setHelpPage(pageId) { if (!pageId || pageId === helpState.currentPage) return; helpState.currentPage = pageId; updateHelpContext(); } /** * SETUP CONTEXT DETECTION * Automatically detect user context and show relevant help */ function setupContextDetection() { // Page changes arrive via setHelpPage(), called from showPage() // Detect first-time user (the first-run guide is for the Tournament Manager, not Analytics) if (window.NEWTON_APP_MODE !== 'analytics' && !localStorage.getItem('helpSystemSeen')) { setTimeout(() => { showHelp('scenarios', 'firstTime'); localStorage.setItem('helpSystemSeen', 'true'); }, 2000); } } /** * SETUP KEYBOARD SHORTCUTS */ function setupHelpKeyboardShortcuts() { document.addEventListener('keydown', (e) => { // F1 - Show help for current page if (e.key === 'F1') { e.preventDefault(); if (helpState.isVisible) { hideHelp(); } else { showHelp(helpState.currentPage); } } // ESC - Close help if (e.key === 'Escape' && helpState.isVisible) { hideHelp(); } // Ctrl+H - Toggle help if (e.ctrlKey && e.key === 'h') { e.preventDefault(); toggleHelp(); } }); } /** * SHOW HELP * Display help for specific page and optionally specific section */ function showHelp(pageId = 'setup', sectionId = null) { const modal = document.getElementById('dynamicHelpModal'); if (!modal) return; helpState.isVisible = true; helpState.currentPage = pageId; helpState.currentSection = sectionId; // Update help content updateHelpContent(pageId, sectionId); // Show modal modal.style.display = 'block'; // Focus for keyboard navigation modal.focus(); console.log(`📖 Help shown for ${pageId}${sectionId ? ` > ${sectionId}` : ''}`); } /** * HIDE HELP */ function hideHelp() { const modal = document.getElementById('dynamicHelpModal'); if (!modal) return; modal.style.display = 'none'; helpState.isVisible = false; console.log('📖 Help hidden'); } /** * TOGGLE HELP */ function toggleHelp() { if (helpState.isVisible) { hideHelp(); } else { showHelp(helpState.currentPage); } } /** * UPDATE HELP CONTEXT * Called when page changes to update help relevance */ function updateHelpContext() { if (!helpState.isVisible) return; // Update help content for new page updateHelpContent(helpState.currentPage); } /** * UPDATE HELP CONTENT * Populate modal with relevant help information */ function updateHelpContent(pageId, sectionId = null) { const helpData = HELP_CONTENT[pageId]; if (!helpData) { console.warn(`No help content found for page: ${pageId}`); return; } const titleElement = document.getElementById('helpTitle'); const overviewElement = document.getElementById('helpOverview'); const sectionsElement = document.getElementById('helpSections'); // Update title if (titleElement) { titleElement.textContent = helpData.title; } // Update overview if (overviewElement) { overviewElement.innerHTML = `
Overview: ${helpData.overview}
`; } // Update sections if (sectionsElement && helpData.sections) { let sectionsHTML = ''; Object.entries(helpData.sections).forEach(([key, section]) => { const isExpanded = sectionId === key || sectionId === null; sectionsHTML += `

${section.title} ${isExpanded ? '−' : '+'}

${section.content}
`; }); sectionsElement.innerHTML = sectionsHTML; } // Add quick actions based on current page addQuickActions(pageId); } /** * ADD QUICK ACTIONS * Context-sensitive action buttons */ function addQuickActions(pageId) { const navigation = document.getElementById('helpNavigation'); if (!navigation) return; // Remove existing quick actions const existingActions = navigation.querySelector('.quick-actions'); if (existingActions) { existingActions.remove(); } const quickActions = document.createElement('div'); quickActions.className = 'quick-actions'; quickActions.style.cssText = 'display: flex; gap: 8px; align-items: center;'; let actionsHTML = ''; switch (pageId) { case 'setup': if (!tournament) { actionsHTML = ''; } break; case 'registration': if (tournament && players.filter(p => p.paid).length < 4) { actionsHTML = ''; } else if (tournament && !tournament.bracket) { actionsHTML = ''; } break; case 'tournament': if (tournament && !tournament.bracket) { actionsHTML = ''; } break; case 'config': actionsHTML = ''; break; } if (actionsHTML) { quickActions.innerHTML = actionsHTML; navigation.insertBefore(quickActions, navigation.firstChild); } } /** * TOGGLE HELP SECTION * Expand/collapse individual help sections */ function toggleHelpSection(sectionId) { const section = document.getElementById(`section-${sectionId}`); const toggle = document.getElementById(`toggle-${sectionId}`); if (!section || !toggle) return; if (section.style.display === 'none') { section.style.display = 'block'; toggle.textContent = '−'; } else { section.style.display = 'none'; toggle.textContent = '+'; } } /** * SHOW HELP SECTION * Direct navigation to specific help section */ function showHelpSection(pageId, sectionId) { showHelp(pageId, sectionId); } // Make functions globally available if (typeof window !== 'undefined') { // Core help functions window.initializeHelpSystem = initializeHelpSystem; window.showHelp = showHelp; window.hideHelp = hideHelp; window.toggleHelp = toggleHelp; window.toggleHelpSection = toggleHelpSection; window.showHelpSection = showHelpSection; console.log('✅ Dynamic help system functions registered globally'); }