-
Notifications
You must be signed in to change notification settings - Fork 11
Expand file tree
/
Copy patheslint-report.json
More file actions
1 lines (1 loc) · 219 KB
/
eslint-report.json
File metadata and controls
1 lines (1 loc) · 219 KB
1
[{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/assets/policies/schedulingPolicy.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/assets/policies/scoringPolicy.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/assets/policies/seedingPolicy.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/assets/specialCharacters/openClose.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/buttons/barButton.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/buttons/dropDownButton.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/controlBar/CompositeTable.stories.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/controlBar/Patterns.stories.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/controlBar/controlBar.stories.ts","messages":[{"ruleId":"sonarjs/no-duplicate-string","severity":1,"message":"Define a constant instead of duplicating this literal 8 times.","line":115,"column":17,"endLine":115,"endColumn":29},{"ruleId":"sonarjs/no-duplicate-string","severity":1,"message":"Define a constant instead of duplicating this literal 4 times.","line":178,"column":18,"endLine":178,"endColumn":35}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":2,"fixableErrorCount":0,"fixableWarningCount":0,"source":"/**\n * Control Bar Component Stories\n * \n * The Control Bar is a versatile component used throughout TMX for table controls.\n * It provides search fields, buttons, dropdown menus, and action overlays.\n * \n * ## Common Usage Pattern\n * The Control Bar is typically used with:\n * - A header showing count (e.g., \"Participants (42)\")\n * - The control bar with filters and actions\n * - A Tabulator table\n * \n * ## Locations\n * Items can be positioned in different locations:\n * - OVERLAY: Shown only when rows are selected\n * - LEFT: Left side of the control bar\n * - CENTER: Center of the control bar\n * - RIGHT: Right side of the control bar\n * - HEADER: In the header section above the control bar\n */\nimport type { Meta, StoryObj } from '@storybook/html';\nimport { controlBar } from './controlBar';\nimport { TabulatorFull as Tabulator } from 'tabulator-tables';\n\ninterface ControlBarArgs {\n withTable?: boolean;\n withSearch?: boolean;\n withFilters?: boolean;\n withActions?: boolean;\n withOverlay?: boolean;\n}\n\nconst meta: Meta<ControlBarArgs> = {\n title: 'Components/ControlBar',\n tags: ['autodocs'],\n argTypes: {\n withTable: {\n control: 'boolean',\n description: 'Include a sample table',\n defaultValue: false,\n },\n withSearch: {\n control: 'boolean',\n description: 'Include search input',\n defaultValue: true,\n },\n withFilters: {\n control: 'boolean',\n description: 'Include filter dropdowns',\n defaultValue: true,\n },\n withActions: {\n control: 'boolean',\n description: 'Include action buttons',\n defaultValue: true,\n },\n withOverlay: {\n control: 'boolean',\n description: 'Include overlay actions (shown on selection)',\n defaultValue: true,\n },\n },\n};\n\nexport default meta;\ntype Story = StoryObj<ControlBarArgs>;\n\n// Helper to create sample table data\nfunction createSampleData(count: number) {\n return Array.from({ length: count }, (_, i) => ({\n id: i + 1,\n name: `Person ${i + 1}`,\n email: `person${i + 1}@example.com`,\n status: i % 2 === 0 ? 'Active' : 'Inactive',\n }));\n}\n\n// Helper to create a sample Tabulator table\nfunction createSampleTable(element: HTMLElement) {\n const data = createSampleData(10);\n return new Tabulator(element, {\n data,\n height: '300px',\n layout: 'fitColumns',\n columns: [\n { title: 'Name', field: 'name', width: 200 },\n { title: 'Email', field: 'email', width: 250 },\n { title: 'Status', field: 'status', width: 100 },\n ],\n selectableRows: true,\n });\n}\n\n// Basic control bar with minimal configuration\nexport const Basic: Story = {\n render: () => {\n const container = document.createElement('div');\n container.className = 'section';\n \n const target = document.createElement('div');\n target.id = 'control-bar-basic';\n container.appendChild(target);\n\n const items = [\n {\n placeholder: 'Search...',\n location: 'left',\n search: true,\n clearSearch: () => console.log('Clear search'),\n onKeyUp: (e: KeyboardEvent) => console.log('Search:', (e.target as HTMLInputElement).value),\n },\n {\n label: 'Add Item',\n location: 'right',\n intent: 'is-primary',\n onClick: () => alert('Add item clicked'),\n },\n ];\n\n // Initialize control bar after DOM is attached\n requestAnimationFrame(() => {\n controlBar({ target, items });\n });\n\n return container;\n },\n};\n\n// Participants Page Configuration\nexport const ParticipantsPage: Story = {\n render: () => {\n const container = document.createElement('div');\n container.className = 'section';\n container.innerHTML = `\n <div class=\"tabHeader\" style=\"padding: 1rem; font-size: 1.2rem; font-weight: bold;\">\n Participants (10)\n </div>\n `;\n \n const target = document.createElement('div');\n target.id = 'control-bar-participants';\n container.appendChild(target);\n\n const tableContainer = document.createElement('div');\n tableContainer.id = 'sample-table';\n container.appendChild(tableContainer);\n\n requestAnimationFrame(() => {\n const table = createSampleTable(tableContainer);\n\n const items = [\n // Overlay items (shown when rows are selected)\n {\n placeholder: 'Search participants',\n location: 'overlay',\n search: true,\n clearSearch: () => console.log('Clear search'),\n onKeyUp: (e: KeyboardEvent) => console.log('Search:', (e.target as HTMLInputElement).value),\n },\n {\n label: 'Add to event',\n location: 'overlay',\n intent: 'is-none',\n options: [\n { label: 'Event A', onClick: () => alert('Added to Event A'), close: true },\n { label: 'Event B', onClick: () => alert('Added to Event B'), close: true },\n { divider: true },\n { label: '<p style=\"font-weight: bold\">Create new event</p>', onClick: () => alert('Create event'), close: true },\n ],\n },\n {\n label: 'Sign in',\n location: 'overlay',\n intent: 'is-primary',\n onClick: () => alert('Sign in selected'),\n },\n {\n label: 'Delete selected',\n location: 'overlay',\n intent: 'is-danger',\n onClick: () => alert('Delete selected'),\n },\n // Left side filters\n {\n placeholder: 'Search participants',\n location: 'left',\n search: true,\n clearSearch: () => table?.clearFilter(),\n onKeyUp: (e: KeyboardEvent) => {\n const value = (e.target as HTMLInputElement).value;\n if (value) {\n table?.setFilter('name', 'like', value);\n } else {\n table?.clearFilter();\n }\n },\n },\n {\n label: 'All Events',\n location: 'left',\n modifyLabel: true,\n selection: true,\n options: [\n { label: '<span style=\"font-weight: bold\">All Events</span>', onClick: () => table?.clearFilter(), close: true },\n { divider: true },\n { label: 'Event A', onClick: () => console.log('Filter Event A'), close: true },\n { label: 'Event B', onClick: () => console.log('Filter Event B'), close: true },\n ],\n },\n {\n label: 'All Genders',\n location: 'left',\n modifyLabel: true,\n selection: true,\n options: [\n { label: '<span style=\"font-weight: bold\">All Genders</span>', onClick: () => table?.clearFilter(), close: true },\n { divider: true },\n { label: 'Male', onClick: () => console.log('Filter Male'), close: true },\n { label: 'Female', onClick: () => console.log('Filter Female'), close: true },\n ],\n },\n // Right side actions\n {\n label: 'Individuals',\n location: 'right',\n intent: 'is-info',\n modifyLabel: true,\n selection: true,\n options: [\n { label: 'Individuals', onClick: () => console.log('View Individuals'), close: true },\n { label: 'Teams', onClick: () => console.log('View Teams'), close: true },\n { label: 'Groups', onClick: () => console.log('View Groups'), close: true },\n ],\n },\n {\n label: 'Actions',\n location: 'right',\n options: [\n { heading: 'Bulk Actions' },\n { label: 'Edit ratings', onClick: () => alert('Edit ratings'), close: true },\n { divider: true },\n { heading: 'Add participants' },\n { label: 'Generate mock participants', onClick: () => alert('Generate mock'), close: true },\n { label: 'Import from Google sheet', onClick: () => alert('Import sheet'), close: true },\n { label: 'New participant', onClick: () => alert('New participant'), close: true },\n ],\n },\n ];\n\n controlBar({ table, target, items, onSelection: (rows) => {\n console.log('Selected rows:', rows.length);\n }});\n });\n\n return container;\n },\n};\n\n// Events Page Configuration\nexport const EventsPage: Story = {\n render: () => {\n const container = document.createElement('div');\n container.className = 'section';\n \n const target = document.createElement('div');\n target.id = 'control-bar-events';\n container.appendChild(target);\n\n const tableContainer = document.createElement('div');\n tableContainer.id = 'sample-table-events';\n container.appendChild(tableContainer);\n\n requestAnimationFrame(() => {\n const table = createSampleTable(tableContainer);\n\n const items = [\n // Overlay (selection) actions\n {\n label: 'Delete selected',\n location: 'overlay',\n intent: 'is-danger',\n stateChange: true,\n onClick: () => alert('Delete selected events'),\n },\n // Left side search\n {\n placeholder: 'Search events',\n location: 'left',\n search: true,\n clearSearch: () => table?.clearFilter(),\n onKeyUp: (e: KeyboardEvent) => {\n const value = (e.target as HTMLInputElement).value;\n if (value) {\n table?.setFilter('name', 'like', value);\n } else {\n table?.clearFilter();\n }\n },\n },\n // Right side actions\n {\n label: 'Publish OOP',\n location: 'right',\n id: 'oopButton',\n onClick: () => {\n const button = document.getElementById('oopButton');\n const isPublished = button?.classList.contains('is-primary');\n button?.classList.toggle('is-primary');\n button!.innerText = isPublished ? 'Publish OOP' : 'Unpublish OOP';\n alert(isPublished ? 'Unpublished' : 'Published');\n },\n },\n {\n label: 'Add event',\n location: 'right',\n intent: 'is-primary',\n onClick: () => alert('Add event'),\n },\n ];\n\n controlBar({ table, target, items });\n });\n\n return container;\n },\n};\n\n// MatchUps Page Configuration\nexport const MatchUpsPage: Story = {\n render: () => {\n const container = document.createElement('div');\n container.className = 'section';\n \n const target = document.createElement('div');\n target.id = 'control-bar-matchups';\n container.appendChild(target);\n\n const tableContainer = document.createElement('div');\n tableContainer.id = 'sample-table-matchups';\n container.appendChild(tableContainer);\n\n requestAnimationFrame(() => {\n const table = createSampleTable(tableContainer);\n\n const items = [\n // Overlay\n {\n label: 'Schedule',\n location: 'overlay',\n stateChange: true,\n onClick: () => alert('Schedule selected'),\n },\n // Left side search and filters\n {\n placeholder: 'Search matches',\n location: 'left',\n search: true,\n clearSearch: () => table?.clearFilter(),\n onKeyUp: (e: KeyboardEvent) => {\n const value = (e.target as HTMLInputElement).value;\n if (value) {\n table?.setFilter('name', 'like', value);\n } else {\n table?.clearFilter();\n }\n },\n },\n {\n label: 'All Events',\n location: 'left',\n modifyLabel: true,\n selection: true,\n options: [\n { label: '<span style=\"font-weight: bold\">All Events</span>', onClick: () => table?.clearFilter(), close: true },\n { divider: true },\n { label: 'Event A', onClick: () => console.log('Filter Event A'), close: true },\n { label: 'Event B', onClick: () => console.log('Filter Event B'), close: true },\n ],\n },\n {\n label: 'All Statuses',\n location: 'left',\n modifyLabel: true,\n selection: true,\n options: [\n { label: '<span style=\"font-weight: bold\">All Statuses</span>', onClick: () => table?.clearFilter(), close: true },\n { divider: true },\n { label: 'Ready to score', onClick: () => console.log('Filter ready'), close: true },\n { label: 'Complete', onClick: () => console.log('Filter complete'), close: true },\n ],\n },\n {\n label: 'All Types',\n location: 'left',\n modifyLabel: true,\n selection: true,\n options: [\n { label: '<span style=\"font-weight: bold\">All Types</span>', onClick: () => table?.clearFilter(), close: true },\n { divider: true },\n { label: 'Singles', onClick: () => console.log('Filter singles'), close: true },\n { label: 'Doubles', onClick: () => console.log('Filter doubles'), close: true },\n { label: 'Team', onClick: () => console.log('Filter team'), close: true },\n ],\n },\n ];\n\n controlBar({ table, target, items });\n });\n\n return container;\n },\n};\n\n// Venues Page Configuration\nexport const VenuesPage: Story = {\n render: () => {\n const container = document.createElement('div');\n container.className = 'section';\n \n const target = document.createElement('div');\n target.id = 'control-bar-venues';\n container.appendChild(target);\n\n const tableContainer = document.createElement('div');\n tableContainer.id = 'sample-table-venues';\n container.appendChild(tableContainer);\n\n requestAnimationFrame(() => {\n const table = createSampleTable(tableContainer);\n\n const items = [\n {\n label: 'Delete selected',\n location: 'overlay',\n intent: 'is-danger',\n stateChange: true,\n onClick: () => alert('Delete selected venues'),\n },\n {\n label: 'Add venue',\n location: 'right',\n onClick: () => alert('Add venue'),\n },\n ];\n\n controlBar({ table, target, items });\n });\n\n return container;\n },\n};\n\n// Custom Configuration with Tabs\nexport const WithTabs: Story = {\n render: () => {\n const container = document.createElement('div');\n container.className = 'section';\n \n const target = document.createElement('div');\n target.id = 'control-bar-tabs';\n container.appendChild(target);\n\n requestAnimationFrame(() => {\n const items = [\n {\n tabs: [\n {\n label: 'View 1',\n active: true,\n onClick: () => alert('View 1 selected'),\n },\n {\n label: 'View 2',\n onClick: () => alert('View 2 selected'),\n },\n {\n label: 'View 3',\n onClick: () => alert('View 3 selected'),\n },\n ],\n location: 'left',\n },\n {\n label: 'Action',\n location: 'right',\n intent: 'is-primary',\n onClick: () => alert('Action clicked'),\n },\n ];\n\n controlBar({ target, items });\n });\n\n return container;\n },\n};\n\n// Configuration with Input Validation\nexport const WithValidation: Story = {\n render: () => {\n const container = document.createElement('div');\n container.className = 'section';\n \n const target = document.createElement('div');\n target.id = 'control-bar-validation';\n container.appendChild(target);\n\n requestAnimationFrame(() => {\n const items = [\n {\n placeholder: 'Enter email',\n location: 'left',\n validator: (value: string) => {\n const emailRegex = /^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/;\n return emailRegex.test(value) ? null : 'Invalid email format';\n },\n onKeyUp: (e: KeyboardEvent) => {\n console.log('Email input:', (e.target as HTMLInputElement).value);\n },\n },\n {\n placeholder: 'Enter number',\n location: 'left',\n validator: (value: string) => {\n return isNaN(Number(value)) ? 'Must be a number' : null;\n },\n },\n {\n label: 'Submit',\n location: 'right',\n intent: 'is-primary',\n onClick: () => alert('Submit clicked'),\n },\n ];\n\n controlBar({ target, items });\n });\n\n return container;\n },\n};\n\n// All Locations Demo\nexport const AllLocations: Story = {\n render: () => {\n const container = document.createElement('div');\n container.className = 'section';\n \n const headerDiv = document.createElement('div');\n headerDiv.className = 'panelHeader';\n headerDiv.style.padding = '1rem';\n headerDiv.style.fontWeight = 'bold';\n headerDiv.innerHTML = 'Header Location (click me)';\n container.appendChild(headerDiv);\n \n const target = document.createElement('div');\n target.id = 'control-bar-locations';\n container.appendChild(target);\n\n requestAnimationFrame(() => {\n const items = [\n {\n location: 'header',\n headerClick: () => alert('Header clicked!'),\n },\n {\n text: 'Overlay Item (select table rows)',\n location: 'overlay',\n onClick: () => alert('Overlay item clicked'),\n },\n {\n label: 'Left Button',\n location: 'left',\n onClick: () => alert('Left button'),\n },\n {\n label: 'Center Button',\n location: 'center',\n onClick: () => alert('Center button'),\n },\n {\n label: 'Right Button',\n location: 'right',\n intent: 'is-primary',\n onClick: () => alert('Right button'),\n },\n ];\n\n controlBar({ target, items });\n });\n\n return container;\n },\n};\n","usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/controlBar/controlBar.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/controlBar/toggleOverlay.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/drawer.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/drawers/addDraw/acceptedEntriesCount.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/drawers/addDraw/addDraw.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/drawers/addDraw/generateDraw.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/drawers/addDraw/getDrawFormItems.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/drawers/addDraw/getDrawFormRelationships.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/drawers/addDraw/getDrawTypeOptions.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/drawers/addDraw/submitDrawParams.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/drawers/avoidances/avoidanceRules.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/drawers/avoidances/editAvoidances.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/drawers/avoidances/getAttachedAvoidances.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/drawers/avoidances/getAvoidanceFormItems.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/drawers/editTournamentDrawer.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/elements/getTeamVs.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/forms/venue.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/framework/courtHiveLogo.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/framework/eventBlock.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/framework/footerBlock.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/framework/infoiBlock.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/framework/initScrollNav.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/framework/rootBlock.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/menus/keyActions.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/menus/mainMenu.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/menus/tmxNotes.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/modals/addAdHocMatchUps.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/modals/addAdHocRound.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/modals/addFlights/addFlights.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/modals/addRRplayoffs.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/modals/addStructures.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/modals/addToEvent.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/modals/baseModal/baseModal.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/modals/createTeamFromAttribute.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/modals/deleteAdHocMatchUps.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/modals/deleteEvents.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/modals/deleteFlights.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/modals/displaySettings/editDisplaySettings.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/modals/drawEntriesColumns/getDrawEntriesColumns.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/modals/drawEntriesColumns/seeding/drawEntriesSeedingSelector.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/modals/drawEntriesModal.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/modals/dropzoneModal.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/modals/editGroupNames.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/modals/editProvider.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/modals/editStructureNames.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/modals/enterLink.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/modals/exportTournamentRecord.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/modals/fetchTournamentDetails.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/modals/getLatLong.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/modals/invalidParticipantsModal.ts","messages":[{"ruleId":"sonarjs/no-duplicate-string","severity":1,"message":"Define a constant instead of duplicating this literal 3 times.","line":26,"column":19,"endLine":26,"endColumn":47},{"ruleId":"sonarjs/no-duplicate-string","severity":1,"message":"Define a constant instead of duplicating this literal 3 times.","line":27,"column":22,"endLine":27,"endColumn":53}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":2,"fixableErrorCount":0,"fixableWarningCount":0,"source":"/**\n * Invalid Participants Modal - displays participants that failed validation when adding to event\n * Shows table with participant names and reasons for validation failure\n */\nimport { TabulatorFull as Tabulator } from 'tabulator-tables';\nimport { tournamentEngine } from 'tods-competition-factory';\nimport { cModal } from 'courthive-components';\n\nexport interface InvalidParticipant {\n participantId: string;\n errors: Array<{\n error: string;\n [key: string]: any;\n }>;\n}\n\nexport interface InvalidParticipantsModalParams {\n invalidParticipants: InvalidParticipant[];\n}\n\n// Map error codes to human-readable messages with optional context\nfunction getErrorMessage(error: any): string {\n if (typeof error === 'string') {\n const errorMessages: Record<string, string> = {\n mismatchedGender: 'Gender does not match event requirements',\n invalidAge: 'Age outside category range',\n invalidRating: 'Rating outside category range',\n age: 'Age outside category range',\n rating: 'Rating outside category range',\n };\n return errorMessages[error] || error;\n }\n\n // If error has a reason field, use it directly (from categoryRejections)\n if (error.reason) {\n return error.reason;\n }\n\n // Handle error objects with additional context\n const errorCode = error.error;\n switch (errorCode) {\n case 'mismatchedGender':\n return `Gender mismatch: ${error.sex} participant in ${error.expectedGender} event`;\n case 'age':\n case 'invalidAge':\n return error.reason || 'Age outside category range';\n case 'rating':\n case 'invalidRating':\n return error.reason || 'Rating outside category range';\n default:\n return error.message || errorCode || 'Validation error';\n }\n}\n\nexport function invalidParticipantsModal({ invalidParticipants }: InvalidParticipantsModalParams): void {\n if (!invalidParticipants?.length) {\n console.error('No invalid participants provided');\n return;\n }\n\n // Check if we need to fetch participant names\n const needsNames = invalidParticipants.some((ip: any) => !ip.participantName);\n \n let participantMap: Map<string, any> | undefined;\n \n if (needsNames) {\n // Extract participant IDs that need names\n const participantIds = invalidParticipants\n .filter((ip: any) => !ip.participantName)\n .map((ip) => ip.participantId);\n\n // Fetch participant details from tournament engine\n const { participants } = tournamentEngine.getParticipants({\n participantFilters: { participantIds },\n withIndividualParticipants: true,\n }) ?? {};\n\n if (!participants?.length && participantIds.length) {\n console.error('Could not fetch participant details');\n return;\n }\n\n // Create a map of participantId to participant for quick lookup\n participantMap = new Map(participants?.map((p: any) => [p.participantId, p]) || []);\n }\n\n // Transform data for table display\n const tableData = invalidParticipants.map((invalid: any) => {\n // Use provided participantName or fetch from map\n let participantName = invalid.participantName;\n if (!participantName && participantMap) {\n const participant: any = participantMap.get(invalid.participantId);\n participantName = participant?.participantName || 'Unknown Participant';\n }\n\n // Collect all error reasons\n const reasons = invalid.errors.map((err: any) => getErrorMessage(err)).join('; ');\n\n return {\n participantId: invalid.participantId,\n participantName,\n reasons,\n };\n });\n\n // Create modal content container\n const content = document.createElement('div');\n content.style.width = '100%';\n\n // Table container\n const tableElement = document.createElement('div');\n tableElement.className = 'tmxTable';\n tableElement.style.width = '100%';\n content.appendChild(tableElement);\n\n // Define table columns\n const columns = [\n {\n title: 'Participant',\n field: 'participantName',\n widthGrow: 2,\n headerSort: false,\n },\n {\n title: 'Reason',\n field: 'reasons',\n widthGrow: 3,\n headerSort: false,\n },\n ];\n\n // Initialize Tabulator table\n const table = new Tabulator(tableElement, {\n columns,\n data: tableData,\n layout: 'fitColumns',\n height: Math.min(300 + tableData.length * 40, 600), // Dynamic height based on rows\n placeholder: 'No invalid participants',\n });\n\n // Clean up table when modal closes\n const onClose = () => {\n table?.destroy();\n };\n\n // Open modal\n cModal.open({\n title: 'Invalid Participants',\n content,\n buttons: [\n {\n label: 'OK',\n close: true,\n },\n ],\n config: {\n maxWidth: 800,\n },\n onClose,\n });\n}\n","usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/modals/inviteUser.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/modals/listPicker.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/modals/loadTournamentById.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/modals/loginModal.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/modals/mockParticipants.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/modals/modifyGroupName.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/modals/printDraw.ts","messages":[{"ruleId":"@typescript-eslint/no-unused-vars","severity":1,"message":"'error' is defined but never used.","line":133,"column":18,"messageId":"unusedVar","endLine":133,"endColumn":23},{"ruleId":"@typescript-eslint/no-unused-vars","severity":1,"message":"'error' is defined but never used.","line":152,"column":18,"messageId":"unusedVar","endLine":152,"endColumn":23}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":2,"fixableErrorCount":0,"fixableWarningCount":0,"source":"/**\n * Print Draw Modal\n * Provides options for generating PDF of draw sheet\n */\nimport { renderForm } from 'courthive-components';\nimport { openModal } from './baseModal/baseModal';\nimport { generateDrawPDF } from 'services/pdf/generators/drawGenerator';\nimport { tournamentEngine } from 'tods-competition-factory';\n\ninterface PrintDrawParams {\n drawId: string;\n eventId: string;\n structureId?: string;\n}\n\nexport function printDraw({ drawId, eventId, structureId }: PrintDrawParams): void {\n // Get tournament info using getTournamentInfo (TODS format)\n const tournamentInfoResult = tournamentEngine.getTournamentInfo();\n const tournament = tournamentInfoResult?.tournamentInfo;\n \n const eventResult = tournamentEngine.getEvent({ eventId });\n const event = eventResult?.event;\n \n // Get draw definition\n let drawDefinition;\n if (event?.drawDefinitions) {\n drawDefinition = event.drawDefinitions.find((dd: any) => dd.drawId === drawId);\n } else {\n // Try alternate method\n const drawResult = tournamentEngine.findDrawDefinition({ drawId });\n drawDefinition = drawResult?.drawDefinition;\n }\n\n if (!event || !drawDefinition) {\n return;\n }\n \n // Tournament is optional - use fallback values if not available\n\n // Get draw name (use structure name if structureId provided)\n let drawTitle = drawDefinition.drawName || event.eventName;\n if (structureId) {\n const structure = drawDefinition.structures?.find((s: any) => s.structureId === structureId);\n if (structure?.structureName) {\n drawTitle = structure.structureName;\n }\n }\n\n // Form state\n let printOptions = {\n drawTitle,\n includeSeeding: true,\n includeRankings: true,\n includeTimestamp: true,\n includeOrganizers: true,\n };\n\n const formItems = [\n {\n label: 'Draw Title',\n field: 'drawTitle',\n value: printOptions.drawTitle,\n placeholder: 'Enter draw title',\n },\n {\n text: 'Options',\n class: 'itemTitle',\n },\n {\n label: 'Include seeding',\n field: 'includeSeeding',\n checkbox: true,\n checked: printOptions.includeSeeding,\n },\n {\n label: 'Include rankings',\n field: 'includeRankings',\n checkbox: true,\n checked: printOptions.includeRankings,\n },\n {\n label: 'Include timestamp',\n field: 'includeTimestamp',\n checkbox: true,\n checked: printOptions.includeTimestamp,\n },\n {\n label: 'Include organizers',\n field: 'includeOrganizers',\n checkbox: true,\n checked: printOptions.includeOrganizers,\n },\n ];\n\n const content = document.createElement('div');\n content.style.padding = '1em';\n\n renderForm(content, formItems);\n\n // Update state when form changes\n content.addEventListener('change', (e: Event) => {\n const target = e.target as HTMLInputElement;\n const field = target.getAttribute('field');\n \n if (field) {\n if (target.type === 'checkbox') {\n (printOptions as any)[field] = target.checked;\n } else {\n (printOptions as any)[field] = target.value;\n }\n }\n });\n\n const buttons = [\n {\n label: 'Cancel',\n intent: 'none',\n close: true,\n },\n {\n label: 'View',\n intent: 'is-info',\n onClick: async () => {\n try {\n await generateDrawPDF({\n tournament,\n event,\n drawDefinition,\n structureId,\n options: printOptions,\n action: 'open',\n });\n } catch (error) {\n alert('Failed to generate PDF.');\n }\n },\n close: true,\n },\n {\n label: 'Download',\n intent: 'is-primary',\n onClick: async () => {\n try {\n await generateDrawPDF({\n tournament,\n event,\n drawDefinition,\n structureId,\n options: printOptions,\n action: 'download',\n });\n } catch (error) {\n alert('Failed to generate PDF.');\n }\n },\n close: true,\n },\n ];\n\n openModal({\n title: 'Print Draw',\n content,\n buttons,\n });\n}\n","usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/modals/printSchedule.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/modals/registrationModal.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/modals/removeApproved.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/modals/renameCourt.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/modals/resetDraws.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/modals/scheduleNotes.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/modals/scheduleSetMatchUpStatus.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/modals/scoringV2/index.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/modals/scoringV2/resolveComposition.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/modals/selectAndDeleteFlights.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/modals/selectIdiom.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/modals/selectItem.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/modals/selectParticipant.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/modals/selectProviderModal.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/modals/settingsModal.ts","messages":[{"ruleId":"sonarjs/no-duplicate-string","severity":1,"message":"Define a constant instead of duplicating this literal 6 times.","line":62,"column":16,"endLine":62,"endColumn":31}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":1,"fixableErrorCount":0,"fixableWarningCount":0,"source":"/**\n * Settings modal for active rating scale and local storage preferences.\n * Allows selection between WTN/UTR and toggling local tournament saves.\n */\nimport { saveSettings, loadSettings } from 'services/settings/settingsStorage';\nimport { renderForm, validators } from 'courthive-components';\nimport { setActiveScale } from 'settings/setActiveScale';\nimport { openModal } from './baseModal/baseModal';\nimport { env } from 'settings/env';\n\nimport { UTR, WTN } from 'constants/tmxConstants';\n\nexport function settingsModal(): void {\n let inputs: any;\n const currentSettings = loadSettings();\n\n const saveSettingsHandler = () => {\n const activeScale = inputs.wtn.checked ? WTN : UTR;\n env.saveLocal = inputs.saveLocal.checked;\n env.pdfPrinting = inputs.pdfPrinting?.checked || false;\n\n // Save scoring approach preference\n let scoringApproach: 'dynamicSets' | 'freeScore' | 'dialPad';\n if (inputs.dynamicSets.checked) {\n scoringApproach = 'dynamicSets';\n } else if (inputs.dialPad.checked) {\n scoringApproach = 'dialPad';\n } else if (inputs.freeScore.checked) {\n scoringApproach = 'freeScore';\n } else {\n // Default fallback\n scoringApproach = 'dynamicSets';\n }\n env.scoringApproach = scoringApproach;\n\n // Save minimum schedule grid rows\n const minCourtGridRowsValue = inputs.minCourtGridRows.value;\n if (validators.numericRange(1, 100)(minCourtGridRowsValue)) {\n env.schedule.minCourtGridRows = Number.parseInt(minCourtGridRowsValue, 10);\n }\n\n // Save participant assignment settings\n env.persistInputFields = inputs.persistInputFields?.checked ?? true;\n\n setActiveScale(activeScale);\n\n // Persist to localStorage\n saveSettings({\n activeScale,\n scoringApproach,\n saveLocal: env.saveLocal,\n smartComplements: inputs.smartComplements?.checked || false,\n pdfPrinting: env.pdfPrinting,\n minCourtGridRows: env.schedule.minCourtGridRows,\n persistInputFields: env.persistInputFields,\n });\n };\n const content = (elem: HTMLElement) =>\n (inputs = renderForm(elem, [\n {\n text: 'Active rating',\n class: 'section-title',\n },\n {\n options: [\n { text: 'WTN', field: 'wtn', checked: env.activeScale === WTN },\n { text: 'UTR', field: 'utr', checked: env.activeScale === UTR },\n ],\n onClick: (x: any) => console.log({ x }),\n field: 'activeRating',\n id: 'activeRating',\n radio: true,\n },\n {\n text: 'Scoring approach',\n class: 'section-title',\n },\n {\n options: [\n { text: 'Dynamic Sets', field: 'dynamicSets', checked: env.scoringApproach === 'dynamicSets' },\n { text: 'Dial Pad', field: 'dialPad', checked: env.scoringApproach === 'dialPad' },\n { text: 'Free Score', field: 'freeScore', checked: env.scoringApproach === 'freeScore' },\n ],\n onClick: (x: any) => console.log({ x }),\n field: 'scoringApproach',\n id: 'scoringApproach',\n radio: true,\n },\n {\n label: 'Smart complements (Dynamic Sets only)',\n checked: currentSettings?.smartComplements || false,\n field: 'smartComplements',\n id: 'smartComplements',\n checkbox: true,\n },\n {\n text: 'Storage',\n class: 'section-title',\n },\n {\n label: 'Save local copies',\n checked: env.saveLocal,\n field: 'saveLocal',\n id: 'saveLocal',\n checkbox: true,\n },\n {\n text: 'Scheduling',\n class: 'section-title',\n },\n {\n label: 'Minimum schedule grid rows',\n value: currentSettings?.minCourtGridRows ?? env.schedule.minCourtGridRows,\n field: 'minCourtGridRows',\n id: 'minCourtGridRows',\n validator: validators.numericRange(1, 100),\n error: 'Must be a number between 1 and 100',\n selectOnFocus: true,\n onInput: () => {\n const saveButton = document.getElementById('saveSettingsButton') as HTMLButtonElement;\n const value = inputs.minCourtGridRows.value;\n const valid = validators.numericRange(1, 100)(value);\n if (saveButton) saveButton.disabled = !valid;\n },\n },\n {\n text: 'Participant Assignment',\n class: 'section-title',\n },\n {\n label: 'Keep input fields after assignment (Persist mode)',\n checked: currentSettings?.persistInputFields ?? true, // Default true\n field: 'persistInputFields',\n id: 'persistInputFields',\n checkbox: true,\n },\n {\n text: 'Beta features',\n class: 'section-title',\n },\n {\n label: 'PDF printing',\n checked: env.pdfPrinting || false,\n field: 'pdfPrinting',\n id: 'pdfPrinting',\n checkbox: true,\n },\n ]));\n\n openModal({\n title: 'Settings',\n content,\n buttons: [\n { label: 'Cancel', intent: 'none', close: true },\n {\n id: 'saveSettingsButton',\n label: 'Save',\n intent: 'is-primary',\n onClick: saveSettingsHandler,\n close: true,\n },\n ],\n });\n\n // Initialize button state based on initial validation\n setTimeout(() => {\n const saveButton = document.getElementById('saveSettingsButton') as HTMLButtonElement;\n const value = inputs?.minCourtGridRows?.value ?? env.schedule.minCourtGridRows;\n const valid = validators.numericRange(1, 100)(value);\n if (saveButton) saveButton.disabled = !valid;\n }, 0);\n}\n\nexport function initSettingsIcon(id: string): void {\n const el = document.getElementById(id);\n if (el) el.addEventListener('click', settingsModal);\n}\n","usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/modals/timePicker.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/modals/tournamentActions.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/modals/tournamentImage.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/notices/failure.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/notices/success.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/overlays/editTieFormat.js/editTieFormat.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/overlays/editTieFormat.js/updateTieFormat.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/overlays/overlay.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/overlays/scorecard/scorecard.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/popovers/courtActions.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/popovers/drawActions.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/popovers/entryActions.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/popovers/eventActions.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/popovers/matchUpActions.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/popovers/participantActions.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/popovers/participantMatchUpActions.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/popovers/scheduleBlockedCellMenu.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/popovers/scheduleColumnHeader.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/popovers/scheduleEmptyCellMenu.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/popovers/scheduleSetMatchUpHeader.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/popovers/selectPositionAction.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/popovers/tipster.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/popovers/tournamentHeader.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/popovers/venueActions.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/collectionTable/collectionScoreHandler.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/collectionTable/collectionSideClick.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/collectionTable/createCollectionTable.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/collectionTable/getCollectionColumns.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/common/columnIsVisible.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/common/createSelectOnEnter.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/common/editors/idEditor.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/common/editors/numericEditor.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/common/eventTabDeleteDraws.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/common/filters/createSearchFilter.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/common/filters/eventFilter.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/common/filters/sexFilter.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/common/filters/teamFilter.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/common/formatters/arrayLength.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/common/formatters/cellBorder.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/common/formatters/collapseFormatter.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/common/formatters/collectionParticipantFormatter.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/common/formatters/eventsFormatter.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/common/formatters/flightsFormatter.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/common/formatters/genderedText.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/common/formatters/openClose.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/common/formatters/participantFormatter.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/common/formatters/percentFormatter.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/common/formatters/profileFormatter.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/common/formatters/ratingFormatter.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/common/formatters/scheduleCell.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/common/formatters/scoreFormatter.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/common/formatters/teamsFormatter.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/common/formatters/threeDots.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/common/formatters/titleFormatter.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/common/formatters/tournamentFormatter.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/common/formatters/visibility.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/common/headerMenu.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/common/navigateToEvent.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/common/sorters/competitiveProfileSorter.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/common/sorters/headerSortElement.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/common/sorters/orderSorter.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/common/sorters/participantSorter.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/common/sorters/percentSorter.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/common/sorters/ratingSorter.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/common/sorters/scoreSorter.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/common/tableSearch.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/common/toggleEditVisibility.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/eventsTable/addEntries.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/eventsTable/addToDraw.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/eventsTable/changeEntryStatus.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/eventsTable/createEntriesPanels.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/eventsTable/createEventsTable.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/eventsTable/createFlight.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/eventsTable/createPair.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/eventsTable/destroyPairs.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/eventsTable/displayAllEvents.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/eventsTable/drawEntriesClick.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/eventsTable/eventRowFormatter.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/eventsTable/getDrawsColumns.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/eventsTable/getEntriesColumns.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/eventsTable/getEventColumns.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/eventsTable/modifyEntriesStatus.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/eventsTable/moveSelected.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/eventsTable/panelDefinitions.ts","messages":[{"ruleId":"sonarjs/no-duplicate-string","severity":1,"message":"Define a constant instead of duplicating this literal 5 times.","line":90,"column":56,"endLine":90,"endColumn":70}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":1,"fixableErrorCount":0,"fixableWarningCount":0,"source":"/**\n * Panel definitions for event entries table.\n * Organizes participants by entry status (accepted, qualifying, alternates, etc.).\n */\nimport { drawDefinitionConstants, eventConstants, entryStatusConstants } from 'tods-competition-factory';\nimport { cancelManualSeeding } from './seeding/canceManuallSeeding';\nimport { seedingSelector } from './seeding/seedingSelector';\nimport { changeEntryStatus } from './changeEntryStatus';\nimport { panelItems, togglePanel } from './panelItems';\nimport { searchField } from '../common/tableSearch';\nimport { saveSeeding } from './seeding/saveSeeding';\nimport { destroySelected } from './destroyPairs';\nimport { createFlight } from './createFlight';\nimport { moveSelected } from './moveSelected';\nimport { addEntries } from './addEntries';\nimport { createPair } from './createPair';\nimport { addToDraw } from './addToDraw';\n\nimport { acceptedEntryStatuses } from 'constants/acceptedEntryStatuses';\nimport {\n ACCEPTED,\n ACCEPTED_PANEL,\n ALTERNATES_PANEL,\n LEFT,\n OVERLAY,\n QUALIFYING_PANEL,\n RIGHT,\n UNGROUPED_PANEL,\n WITHDRAWN_PANEL,\n} from 'constants/tmxConstants';\n\nconst { DIRECT_ACCEPTANCE, ALTERNATE, UNGROUPED, WITHDRAWN } = entryStatusConstants;\nconst { MAIN, QUALIFYING } = drawDefinitionConstants;\nconst { SINGLES, DOUBLES } = eventConstants;\n\nexport function panelDefinitions({ drawDefinition, event, entryData, hasFlights }: any): any[] {\n const filterEntries = (groupings: string[]) =>\n entryData.filter(({ entryStage = MAIN, entryStatus }: any) => {\n return groupings.includes(`${entryStage}.${entryStatus}`);\n });\n\n const drawCreated = !!drawDefinition;\n const drawId = drawDefinition?.drawId;\n const eventId = event?.eventId;\n\n const moves: Record<string, string[]> = {\n [ACCEPTED]: [ALTERNATE, WITHDRAWN],\n [QUALIFYING]: [ACCEPTED, ALTERNATE, WITHDRAWN],\n [ALTERNATE]: [ACCEPTED, QUALIFYING, WITHDRAWN],\n [WITHDRAWN]: event?.eventType === DOUBLES ? [UNGROUPED] : [ALTERNATE],\n };\n\n // Get createPair functionality - we handle auto-pair logic separately via toggle\n const { createPairButton } = createPair(event, false);\n const excludeColumns = !hasFlights ? ['flights'] : [];\n\n const selectWithEnter = (table: any): boolean => {\n const active = table.getData('active');\n const participantIds = active.map(({ participantId }: any) => participantId);\n if (active.length === 1) {\n table.selectRow(participantIds);\n return true;\n } else if (active.length === 2) {\n table.selectRow(participantIds);\n return false;\n }\n return false;\n };\n\n const handleUngroupedSelection = (selectedRows: any[]) => {\n // Clear LEFT search fields and focus OVERLAY search when selection occurs\n if (selectedRows?.length) {\n const searchFields = document.getElementsByClassName('search');\n Array.from(searchFields).forEach((field: any) => {\n if (field.value) field.value = '';\n });\n \n // Focus the OVERLAY search field after a brief delay\n setTimeout(() => {\n const overlaySearch = document.querySelector('.options_overlay .search') as HTMLInputElement;\n if (overlaySearch) overlaySearch.focus();\n }, 50);\n }\n \n // Initialize and check auto-pair state\n const createPairBtn = document.getElementById('create-pair') as HTMLButtonElement;\n const autoPairToggle = document.getElementById('auto-pair-toggle');\n \n // Initialize data-enabled attribute if not set\n if (autoPairToggle && !autoPairToggle.hasAttribute('data-enabled')) {\n autoPairToggle.setAttribute('data-enabled', 'true');\n }\n \n const isAutoPairEnabled = autoPairToggle?.getAttribute('data-enabled') === 'true';\n const twoSelected = selectedRows?.length === 2;\n \n // Handle pair creation based on auto-pair state\n if (event?.eventType === DOUBLES && twoSelected) {\n if (isAutoPairEnabled) {\n // Auto-pair is ON: programmatically click the create pair button\n createPairBtn?.click();\n } else {\n // Auto-pair is OFF: show the Create Pair button\n if (createPairBtn) createPairBtn.style.display = '';\n }\n } else {\n // Not 2 selected: hide the Create Pair button\n if (createPairBtn) createPairBtn.style.display = 'none';\n }\n };\n\n const acceptedEntries = filterEntries(acceptedEntryStatuses(MAIN));\n const qualifyingEntries = filterEntries([`${QUALIFYING}.${DIRECT_ACCEPTANCE}`]);\n const alternateEntries = filterEntries([`${MAIN}.${ALTERNATE}`]);\n const ungroupedEntries = filterEntries([`${MAIN}.${UNGROUPED}`]);\n const withdrawnEntries = filterEntries([`${MAIN}.${WITHDRAWN}`]);\n\n return [\n {\n placeholder: 'No accepted participants',\n items: [\n moveSelected(moves[ACCEPTED], eventId, drawId),\n changeEntryStatus(acceptedEntryStatuses(MAIN), eventId, drawId),\n addToDraw(event, drawId),\n createFlight(event, drawId),\n ...panelItems({ heading: 'Accepted', count: acceptedEntries.length }),\n !drawCreated && seedingSelector(event, ACCEPTED),\n cancelManualSeeding(event),\n saveSeeding(event),\n !drawCreated && addEntries(event, ACCEPTED),\n ],\n actions: moves[ACCEPTED],\n anchorId: ACCEPTED_PANEL,\n entries: acceptedEntries,\n group: ACCEPTED,\n excludeColumns,\n drawCreated,\n togglePanel,\n },\n {\n placeholder: 'No qualifying participants',\n items: [\n ...panelItems({ heading: 'Qualifying', count: qualifyingEntries.length }),\n moveSelected(moves[QUALIFYING], eventId, drawId),\n !drawCreated && seedingSelector(event, QUALIFYING),\n cancelManualSeeding(event),\n saveSeeding(event),\n !drawCreated && addEntries(event, QUALIFYING),\n ],\n actions: [ACCEPTED, ALTERNATE, WITHDRAWN],\n anchorId: QUALIFYING_PANEL,\n entries: qualifyingEntries,\n group: QUALIFYING,\n excludeColumns,\n drawCreated,\n togglePanel,\n },\n {\n items: [\n ...panelItems({ heading: 'Alternates', count: alternateEntries.length }),\n moveSelected(moves[ALTERNATE], eventId, drawId),\n event?.eventType === DOUBLES && destroySelected(eventId, drawId),\n !drawCreated && addEntries(event, ALTERNATE),\n ],\n actions: [ACCEPTED, QUALIFYING, WITHDRAWN],\n excludeColumns: ['seedNumber', 'flights'],\n placeholder: 'No alternates',\n anchorId: ALTERNATES_PANEL,\n entries: alternateEntries,\n group: ALTERNATE,\n drawCreated,\n togglePanel,\n },\n {\n hide: !!([SINGLES].includes(event?.eventType) || drawCreated),\n items: [\n ...panelItems({ heading: 'Ungrouped', count: ungroupedEntries.length }),\n searchField(LEFT, 'participantId', selectWithEnter),\n searchField(OVERLAY, 'participantId', selectWithEnter),\n createPairButton,\n moveSelected([WITHDRAWN], eventId, drawId),\n {\n hide: event?.eventType !== DOUBLES,\n onClick: (e: any) => {\n const button = e.target.closest('button');\n const isEnabled = button.getAttribute('data-enabled') === 'true';\n const newState = !isEnabled;\n button.setAttribute('data-enabled', String(newState));\n button.innerHTML = `Auto Pair: ${newState ? 'ON' : 'OFF'}`;\n \n // Hide create pair button if auto-pair is enabled\n const createPairBtn = document.getElementById('create-pair');\n if (createPairBtn && newState) {\n createPairBtn.style.display = 'none';\n }\n },\n label: 'Auto Pair: ON',\n intent: 'is-info',\n id: 'auto-pair-toggle',\n location: RIGHT,\n },\n ],\n placeholder: 'No ungrouped participants',\n excludeColumns: ['seedNumber', 'flights'],\n onSelection: handleUngroupedSelection,\n anchorId: UNGROUPED_PANEL,\n entries: ungroupedEntries,\n actions: [WITHDRAWN],\n group: UNGROUPED,\n drawCreated,\n togglePanel,\n },\n {\n items: [\n ...panelItems({ heading: 'Withdrawn', count: withdrawnEntries.length }),\n !drawCreated && moveSelected(moves[WITHDRAWN], eventId, drawId),\n ],\n placeholder: 'No withdrawn participants',\n excludeColumns: ['seedNumber', 'flights'],\n anchorId: WITHDRAWN_PANEL,\n entries: withdrawnEntries,\n actions: moves[WITHDRAWN],\n hide: drawCreated,\n group: WITHDRAWN,\n collapsed: true,\n drawCreated,\n togglePanel,\n },\n ];\n}\n","usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/eventsTable/panelItems.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/eventsTable/seeding/canceManuallSeeding.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/eventsTable/seeding/enableManualSeeding.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/eventsTable/seeding/generateSeedValues.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/eventsTable/seeding/hideSaveSeeding.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/eventsTable/seeding/removeSeeding.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/eventsTable/seeding/saveSeeding.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/eventsTable/seeding/saveSeedingValues.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/eventsTable/seeding/seedingSelector.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/eventsTable/seeding/setParticipantScaleItems.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/eventsTable/setEventView.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/matchUpsTable/createMatchUpsTable.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/matchUpsTable/getMatchUpColumns.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/matchUpsTable/handleMatchUpScoreClick.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/matchUpsTable/hotKeyScoring.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/matchUpsTable/setMatchUpSchedule.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/participantsTable/createParticipantsTable.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/participantsTable/createTeamsTable.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/participantsTable/editRatings/enableManualRatings.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/participantsTable/editRatings/saveRatings.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/participantsTable/editWTID/enableEditWTID.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/participantsTable/editWTID/saveWTID.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/participantsTable/getGroupingsColumns.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/participantsTable/getParticipantColumns.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/participantsTable/participantResponsiveLayoutFormatter.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/participantsTable/teamRowFormatter.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/participantsTable/toggleSignInStatus.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/roundsTable/createRoundsTable.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/roundsTable/getRoundsColumns.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/roundsTable/handleMatchUpScoreClick.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/roundsTable/mapRound.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/roundsTable/roundGroupingHeader.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/scheduleTable/createScheduleTable.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/scheduleTable/generateEmptyColumns.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/scheduleTable/getControlColumn.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/scheduleTable/getScheduleColumns.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/scheduleTable/matchUpDragStart.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/scheduleTable/matchUpDrop.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/scheduleTable/scheduleRowActions.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/scheduleTable/updateConflicts.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/selection/createSelectionTable.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/selection/getSelectionColumns.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/statsTable/createStatsTable.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/statsTable/getStatsColumns.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/statsTable/mapParticiapantResults.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/tieFormat/createTieFormatTable.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/tieFormat/getCollectionDefinitionColumns.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/tournamentsTable/createTournamentsTable.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/tournamentsTable/getTournamentColumn.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/tournamentsTable/tournamentActionFormatter.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/unscheduledTable/createUnscheduledTable.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/unscheduledTable/getUnscheduledColumns.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/unscheduledTable/matchUpReturn.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/venuesTable/createVenuesTable.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/venuesTable/getCourtColumns.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/venuesTable/getVenueColumns.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/venuesTable/setLatLong.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/components/tables/venuesTable/venueRowFormatter.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/config/config.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/config/localStorage.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/config/setWindow.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/config/version.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/constants/acceptedEntryStatuses.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/constants/comsConstants.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/constants/matchUpFormats.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/constants/mutationConstants.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/constants/tmxConstants.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/functions/hashCode.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/functions/isDev.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/functions/keyWalk.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/functions/parsing/isLatLongRegex.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/functions/parsing/parseBingCoords.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/functions/parsing/parseGoogleLink.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/functions/parsing/parseHereWeGo.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/functions/parsing/parseOpenStreetMap.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/functions/sorting/sorting.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/functions/timeStrings.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/functions/toTitleCase.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/functions/typeOf.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/initialState.ts","messages":[{"ruleId":"sonarjs/no-duplicate-string","severity":1,"message":"Define a constant instead of duplicating this literal 3 times.","line":121,"column":40,"endLine":121,"endColumn":58}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":1,"fixableErrorCount":0,"fixableWarningCount":0,"source":"/**\n * Application initialization and setup.\n * Sets up context, environment, event listeners, and routing.\n */\nimport { factoryConstants, globalState, tournamentEngine } from 'tods-competition-factory';\nimport { tournamentContent } from 'pages/tournament/container/tournamentContent';\nimport { initLoginToggle } from 'services/authentication/loginState';\nimport { initSettingsIcon } from 'components/modals/settingsModal';\nimport { courthiveComponentsVersion } from 'courthive-components';\nimport { loadSettings } from 'services/settings/settingsStorage';\nimport { EventEmitter } from './services/EventEmitter';\nimport { setWindow } from 'config/setWindow';\nimport { tmxNavigation } from 'navigation';\nimport { context } from 'services/context';\nimport { drawer } from 'components/drawer';\nimport { routeTMX } from 'router/router';\nimport { setDev } from 'services/setDev';\nimport { initConfig } from 'config/config';\nimport { version } from 'config/version';\nimport { env } from 'settings/env';\n\nimport { SPLASH, TMX_TOURNAMENTS } from 'constants/tmxConstants';\n\nimport dragMatch from 'assets/icons/dragmatch.png';\n\nimport 'courthive-components/dist/courthive-components.css';\nimport 'vanillajs-datepicker/css/datepicker-bulma.css';\nimport '@event-calendar/core/index.css';\nimport 'timepicker-ui/main.css';\nimport 'styles/legacy/scoreboard.css';\nimport 'styles/legacy/ddScoring.css';\n\nimport 'bulma-checkradio/dist/css/bulma-checkradio.min.css';\nimport 'bulma-switch/dist/css/bulma-switch.min.css';\nimport 'animate.css/animate.min.css';\nimport 'quill/dist/quill.snow.css';\nimport 'pikaday/css/pikaday.css';\n\nimport 'tippy.js/themes/light-border.css';\nimport 'tippy.js/themes/light.css';\nimport 'tippy.js/dist/tippy.css';\n\nimport 'styles/tabulator.css';\n\nimport 'bulma/css/versions/bulma-no-dark-mode.min.css';\n\nimport 'styles/tournamentContainer.css';\nimport 'styles/tournamentSchedule.css';\nimport 'styles/overlay.css';\nimport 'styles/leaflet.css';\nimport 'styles/fa.min.css';\nimport 'styles/icons.css';\nimport 'styles/tmx.css';\n\nexport function setupTMX(): void {\n // Load settings from localStorage before initializing\n const savedSettings = loadSettings();\n if (savedSettings) {\n if (savedSettings.activeScale) {\n env.activeScale = savedSettings.activeScale;\n }\n if (savedSettings.scoringApproach) {\n env.scoringApproach = savedSettings.scoringApproach;\n }\n if (savedSettings.saveLocal !== undefined) {\n env.saveLocal = savedSettings.saveLocal;\n }\n if (savedSettings.pdfPrinting !== undefined) {\n env.pdfPrinting = savedSettings.pdfPrinting;\n }\n if (savedSettings.minCourtGridRows !== undefined) {\n env.schedule.minCourtGridRows = savedSettings.minCourtGridRows;\n }\n }\n\n setEnv();\n setWindow();\n setContext();\n tournamentContent();\n initLoginToggle('login');\n initLoginToggle('burger');\n initSettingsIcon('config');\n\n if (!(Array.prototype as any).toSorted) {\n (Array.prototype as any).toSorted = function (compareFn?: (a: any, b: any) => number) {\n return this.slice().sort(compareFn);\n };\n }\n\n context.drawer = drawer();\n\n initConfig().then(tmxReady, (err) => console.log({ err }));\n}\n\nfunction tmxReady(): void {\n console.log('%c TMX ready', 'color: lightgreen');\n setDev();\n setSubscriptions();\n\n const splashElement = document.getElementById(SPLASH);\n if (splashElement) {\n splashElement.onclick = () => context.router.navigate(`/${TMX_TOURNAMENTS}`);\n }\n routeTMX();\n tmxNavigation();\n}\n\nfunction setContext(): void {\n context.dragMatch = new Image();\n context.dragMatch.src = dragMatch;\n context.ee = new EventEmitter();\n}\n\nconst RESIZE_LOOP = 'ResizeObserver loop limit exceeded';\nconst RESIZE_NOTIFICATIONS = 'ResizeObserver loop completed with undelivered notifications.';\n\nfunction setEnv(): void {\n env.device = getDevice();\n const cfv = tournamentEngine.version();\n const chcv = courthiveComponentsVersion();\n console.log(`%cversion: ${version}`, 'color: lightblue');\n console.log(`%cfactory: ${cfv}`, 'color: lightblue');\n console.log(`%ccourthive-components: ${chcv}`, 'color: lightblue');\n\n eventListeners();\n}\n\nfunction setSubscriptions(): void {\n const topicConstants = factoryConstants.topicConstants;\n globalState.setSubscriptions({\n subscriptions: {\n [topicConstants.MODIFY_MATCHUP]: (data: any) => {\n const matchUpId = data.matchUp?.matchUpId;\n context.matchUpsToBroadcast.push(matchUpId);\n if (env.devNotes) console.log('MODIFY_MATCHUP', data);\n },\n },\n });\n}\n\nfunction eventListeners(): void {\n window.addEventListener('error', (e) => {\n if ([RESIZE_LOOP, RESIZE_NOTIFICATIONS].includes(e.message)) {\n const resizeObserverErrDiv = document.getElementById('webpack-dev-server-client-overlay-div');\n const resizeObserverErr = document.getElementById('webpack-dev-server-client-overlay');\n if (resizeObserverErr) {\n resizeObserverErr.setAttribute('style', 'display: none');\n }\n if (resizeObserverErrDiv) {\n resizeObserverErrDiv.setAttribute('style', 'display: none');\n }\n }\n });\n}\n\nfunction getDevice(): any {\n const nav = getNavigator();\n return {\n isStandalone: nav && 'standalone' in nav && (nav as any).standalone,\n isIDevice: nav && /iphone|ipod|ipad/i.test(nav.userAgent),\n isIpad: nav && /iPad/i.test(nav.userAgent),\n isWindows: nav && /indows/i.test(nav.userAgent),\n isMobile: nav && /Mobi/.test(nav.userAgent),\n };\n}\n\nfunction getNavigator(): Navigator | undefined {\n try {\n return navigator || (window as any).navigator;\n } catch (err) {\n console.log(err);\n return undefined;\n }\n}\n","usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/main.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/navigation.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/pages/tournament/container/tournamentContent.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/pages/tournament/destroyTable.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/pages/tournament/tabs/eventsTab/cleanupDrawPanel.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/pages/tournament/tabs/eventsTab/editEvent.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/pages/tournament/tabs/eventsTab/elements/getFinalColumn.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/pages/tournament/tabs/eventsTab/eventsTab.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/pages/tournament/tabs/eventsTab/eventsView.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/pages/tournament/tabs/eventsTab/getEventHandlers.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/pages/tournament/tabs/eventsTab/mapDrawDefinition.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/pages/tournament/tabs/eventsTab/mapEntry.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/pages/tournament/tabs/eventsTab/mapEvent.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/pages/tournament/tabs/eventsTab/options/adHocRoundOptions.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/pages/tournament/tabs/eventsTab/options/getRoundTabs.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/pages/tournament/tabs/eventsTab/options/handleRoundHeaderClick.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/pages/tournament/tabs/eventsTab/options/roundDisplayOptions.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/pages/tournament/tabs/eventsTab/renderDrawPanel.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/pages/tournament/tabs/eventsTab/renderDraws/drawControlBar.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/pages/tournament/tabs/eventsTab/renderDraws/editMatchUpFormat.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/pages/tournament/tabs/eventsTab/renderDraws/eventControlBar/eventControlBar.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/pages/tournament/tabs/eventsTab/renderDraws/eventControlBar/eventControlItems.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/pages/tournament/tabs/eventsTab/renderDraws/generateAdHocRound.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/pages/tournament/tabs/eventsTab/renderDraws/generateQualifying.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/pages/tournament/tabs/eventsTab/renderDraws/getActionOptions.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/pages/tournament/tabs/eventsTab/renderDraws/getDrawsOptions.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/pages/tournament/tabs/eventsTab/renderDraws/getEventOptions.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/pages/tournament/tabs/eventsTab/renderDraws/getStructureOptions.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/pages/tournament/tabs/eventsTab/renderDraws/participantAssignmentMode.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/pages/tournament/tabs/eventsTab/renderDraws/removeStructure.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/pages/tournament/tabs/eventsTab/renderDraws/renderDrawView.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/pages/tournament/tabs/matchUpsTab/mapMatchUp.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/pages/tournament/tabs/matchUpsTab/matchUpsTab.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/pages/tournament/tabs/overviewTab/overviewControl.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/pages/tournament/tabs/overviewTab/renderOverview.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/pages/tournament/tabs/participantTab/addParticipantsToEvent.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/pages/tournament/tabs/participantTab/controlBar/getAddToGroupingSelection.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/pages/tournament/tabs/participantTab/controlBar/removeFromTeam.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/pages/tournament/tabs/participantTab/controlBar/signInParticipants.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/pages/tournament/tabs/participantTab/controlBar/signOutUnapproved.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/pages/tournament/tabs/participantTab/deleteParticipants.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/pages/tournament/tabs/participantTab/deleteSelectedParticipants.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/pages/tournament/tabs/participantTab/editGroupingParticipant.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/pages/tournament/tabs/participantTab/editIndividualParticipant.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/pages/tournament/tabs/participantTab/editPlayer.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/pages/tournament/tabs/participantTab/eventFromParticipants.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/pages/tournament/tabs/participantTab/getters.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/pages/tournament/tabs/participantTab/mapParticipant.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/pages/tournament/tabs/participantTab/mapTeamParticipant.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/pages/tournament/tabs/participantTab/participantOptions.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/pages/tournament/tabs/participantTab/participantsTab.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/pages/tournament/tabs/participantTab/renderGroupings.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/pages/tournament/tabs/participantTab/renderIndividuals.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/pages/tournament/tabs/participantTab/sheetsLink.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/pages/tournament/tabs/scheduleTab/autoScheduleMatchUps.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/pages/tournament/tabs/scheduleTab/clearSchedule.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/pages/tournament/tabs/scheduleTab/scheduleGridControl.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/pages/tournament/tabs/scheduleTab/scheduleTab.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/pages/tournament/tabs/scheduleTab/unscheduledGridControl.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/pages/tournament/tabs/scheduleTab/venuesTab.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/pages/tournament/tabs/venuesTab/addVenue.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/pages/tournament/tabs/venuesTab/editCourt.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/pages/tournament/tabs/venuesTab/editVenue.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/pages/tournament/tabs/venuesTab/mapVenue.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/pages/tournament/tabs/venuesTab/venueControl.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/pages/tournament/tournamentDisplay.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/pages/tournaments/calendar.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/pages/tournaments/mapTournament.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/pages/tournaments/mapTournamentRecord.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/pages/tournaments/mockTournaments.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/pages/tournaments/tournaments.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/pages/tournaments/tournamentsControls.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/router/router.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/serviceWorker.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/services/EventEmitter.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/services/apis/baseApi.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/services/apis/servicesApi.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/services/authentication/authApi.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/services/authentication/checkDevState.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/services/authentication/loginState.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/services/authentication/tokenManagement.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/services/authentication/validateToken.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/services/context.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/services/data/incomingParticipants.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/services/dom/copyClick.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/services/dom/events/eventManager.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/services/dom/events/teamHighlights.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/services/dom/idObj.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/services/dom/parentAndChild.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/services/dom/scaleFont.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/services/dom/toggleSidebar.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/services/dom/toolTip/plugins.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/services/dom/transformers.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/services/export/UTR.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/services/export/download.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/services/messaging/requestTournamentRecord.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/services/messaging/socketIo.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/services/mutation/mutationRequest.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/services/notifications/notConfigured.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/services/notifications/statusMessages.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/services/notifications/tmxToast.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/services/notifications/toaster.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/services/pdf/export/pdfExport.ts","messages":[{"ruleId":"no-async-promise-executor","severity":2,"message":"Promise executor functions should not be async.","line":83,"column":22,"messageId":"async","endLine":83,"endColumn":27},{"ruleId":"no-async-promise-executor","severity":2,"message":"Promise executor functions should not be async.","line":106,"column":22,"messageId":"async","endLine":106,"endColumn":27}],"suppressedMessages":[],"errorCount":2,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"source":"/**\n * PDF Export utilities using pdfMake\n * Ported from TMX-Suite-Legacy\n * \n * Provides functions to open, save, or emit PDFs from docDefinitions\n */\n\nimport type { TDocumentDefinitions } from 'pdfmake/interfaces';\n\n// Types for pdfMake (imported dynamically)\ninterface PdfMakeStatic {\n createPdf(documentDefinitions: TDocumentDefinitions): PdfDocument;\n vfs: any;\n}\n\ninterface PdfDocument {\n open(): void;\n download(filename?: string): void;\n getBase64(callback: (result: string) => void): void;\n getBlob(callback: (result: Blob) => void): void;\n}\n\n/**\n * Dynamically import pdfMake to avoid bloating main bundle\n * @returns Promise resolving to pdfMake instance\n */\nasync function loadPdfMake(): Promise<PdfMakeStatic> {\n // Dynamic import for code splitting\n const pdfMakeModule = await import('pdfmake/build/pdfmake');\n const pdfFontsModule = await import('pdfmake/build/vfs_fonts');\n \n // Get the default exports\n const pdfMake = (pdfMakeModule as any).default || pdfMakeModule;\n const pdfFonts = (pdfFontsModule as any).default || pdfFontsModule;\n \n // Assign fonts\n if (pdfMake && pdfFonts) {\n pdfMake.vfs = pdfFonts.pdfMake?.vfs || pdfFonts.vfs;\n }\n \n return pdfMake;\n}\n\n/**\n * Open PDF in new browser tab/window\n * @param docDefinition - pdfMake document definition\n */\nexport async function openPDF({ \n docDefinition \n}: { \n docDefinition: TDocumentDefinitions \n}): Promise<void> {\n const pdfMake = await loadPdfMake();\n pdfMake.createPdf(docDefinition).open();\n}\n\n/**\n * Download PDF file to user's computer\n * @param docDefinition - pdfMake document definition\n * @param filename - Output filename (default: 'document.pdf')\n */\nexport async function savePDF({ \n docDefinition, \n filename = 'document.pdf' \n}: { \n docDefinition: TDocumentDefinitions;\n filename?: string;\n}): Promise<void> {\n const pdfMake = await loadPdfMake();\n pdfMake.createPdf(docDefinition).download(filename);\n}\n\n/**\n * Get PDF as base64 string (useful for sending to server)\n * @param docDefinition - pdfMake document definition\n * @returns Promise resolving to base64 string\n */\nexport async function getPDFBase64({ \n docDefinition \n}: { \n docDefinition: TDocumentDefinitions \n}): Promise<string> {\n return new Promise(async (resolve, reject) => {\n try {\n const pdfMake = await loadPdfMake();\n const pdfDocGenerator = pdfMake.createPdf(docDefinition);\n pdfDocGenerator.getBase64((data) => {\n resolve(data);\n });\n } catch (error) {\n reject(error);\n }\n });\n}\n\n/**\n * Get PDF as Blob (useful for uploading or further processing)\n * @param docDefinition - pdfMake document definition\n * @returns Promise resolving to Blob\n */\nexport async function getPDFBlob({ \n docDefinition \n}: { \n docDefinition: TDocumentDefinitions \n}): Promise<Blob> {\n return new Promise(async (resolve, reject) => {\n try {\n const pdfMake = await loadPdfMake();\n const pdfDocGenerator = pdfMake.createPdf(docDefinition);\n pdfDocGenerator.getBlob((blob) => {\n resolve(blob);\n });\n } catch (error) {\n reject(error);\n }\n });\n}\n\n/**\n * Emit PDF to server or callback\n * @param docDefinition - pdfMake document definition\n * @param callback - Function to call with base64 data\n */\nexport async function emitPDF({ \n docDefinition, \n callback \n}: { \n docDefinition: TDocumentDefinitions;\n callback: (data: string) => void;\n}): Promise<void> {\n const base64 = await getPDFBase64({ docDefinition });\n callback(base64);\n}\n","usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/services/pdf/generators/drawGenerator.ts","messages":[{"ruleId":"@typescript-eslint/no-unused-vars","severity":1,"message":"'_drawDefinition' is defined but never used.","line":212,"column":34,"messageId":"unusedVar","endLine":212,"endColumn":49},{"ruleId":"@typescript-eslint/no-unused-vars","severity":1,"message":"'_includeRankings' is defined but never used.","line":212,"column":56,"messageId":"unusedVar","endLine":212,"endColumn":72}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":2,"fixableErrorCount":0,"fixableWarningCount":0,"source":"/**\n * Draw Sheet PDF Generator\n * Generates PDF documents for tournament draws\n */\nimport { savePDF, openPDF } from '../export/pdfExport';\nimport { formatDate } from '../utils/primitives';\nimport { renderDrawToPNG, canRenderDraw } from '../utils/drawRenderer';\nimport type { TDocumentDefinitions } from 'pdfmake/interfaces';\n\ninterface DrawPDFOptions {\n drawTitle?: string;\n includeSeeding?: boolean;\n includeRankings?: boolean;\n includeTimestamp?: boolean;\n includeOrganizers?: boolean;\n}\n\ninterface GenerateDrawPDFParams {\n tournament?: any;\n event: any;\n drawDefinition: any;\n structureId?: string;\n options?: DrawPDFOptions;\n action?: 'open' | 'download';\n}\n\n/**\n * Generate a PDF for a draw\n */\nexport async function generateDrawPDF({\n tournament,\n event,\n drawDefinition,\n structureId,\n options = {},\n action = 'download',\n}: GenerateDrawPDFParams): Promise<void> {\n const {\n drawTitle = drawDefinition.drawName || event.eventName,\n includeSeeding = true,\n includeRankings = true,\n includeTimestamp = true,\n includeOrganizers = true,\n } = options;\n\n // Try to render the draw\n let drawImageDataURI: string | undefined;\n const canRender = canRenderDraw(drawDefinition.drawId, structureId);\n \n if (canRender) {\n try {\n drawImageDataURI = await renderDrawToPNG({\n drawId: drawDefinition.drawId,\n structureId,\n width: 1600,\n height: 10000, // Very tall to capture full draw\n });\n } catch (error) {\n console.error('Error rendering draw to PNG:', error);\n // Continue without draw image\n }\n }\n\n // Build document definition\n const docDefinition: TDocumentDefinitions = {\n pageSize: 'LETTER',\n pageOrientation: 'portrait',\n pageMargins: [20, 40, 20, 40], // Smaller margins for more space\n\n content: [\n // Header\n {\n columns: [\n {\n width: '*',\n stack: [\n {\n text: tournament?.tournamentName || event.eventName || 'Tournament',\n style: 'tournamentName',\n },\n {\n text: formatDate(tournament?.startDate || event.startDate || new Date()),\n style: 'tournamentDate',\n },\n ],\n },\n // Space for logo (future)\n {\n width: 80,\n text: '',\n },\n ],\n },\n {\n text: drawTitle,\n style: 'drawTitle',\n alignment: 'center',\n margin: [0, 10, 0, 20],\n },\n\n // Event details - compact inline format\n {\n text: [\n { text: 'Event: ', bold: true },\n { text: event.eventName || '' },\n { text: ' | Category: ', bold: true },\n { text: event.category?.categoryName || '' },\n { text: ' | Draw Size: ', bold: true },\n { text: drawDefinition.drawSize || '' },\n ],\n fontSize: 9,\n margin: [0, 5, 0, 10],\n },\n\n // Draw content\n ...(drawImageDataURI\n ? [\n {\n image: drawImageDataURI,\n width: 550, // Full width in portrait\n alignment: 'center',\n margin: [0, 10, 0, 10],\n },\n ]\n : [\n {\n text: '[Draw Bracket]',\n alignment: 'center',\n fontSize: 14,\n italics: true,\n color: '#999',\n margin: [0, 40, 0, 40],\n },\n {\n text: 'Unable to render draw bracket. Ensure draw has participants and matches.',\n alignment: 'center',\n fontSize: 10,\n color: '#666',\n margin: [0, 0, 0, 40],\n },\n ]),\n\n // Seeding information (if enabled)\n ...(includeSeeding ? getSeededPlayersContent(drawDefinition, includeRankings) : []),\n\n // Footer information\n ...(includeTimestamp\n ? [\n {\n text: `Generated: ${new Date().toLocaleString()}`,\n fontSize: 8,\n color: '#666',\n margin: [0, 20, 0, 0],\n },\n ]\n : []),\n ...(includeOrganizers && tournament?.organizers\n ? [\n {\n text: `Organizers: ${tournament.organizers}`,\n fontSize: 8,\n color: '#666',\n },\n ]\n : []),\n ],\n\n styles: {\n tournamentName: {\n fontSize: 16,\n bold: true,\n },\n tournamentDate: {\n fontSize: 10,\n color: '#666',\n },\n drawTitle: {\n fontSize: 18,\n bold: true,\n },\n label: {\n fontSize: 10,\n bold: true,\n color: '#666',\n },\n value: {\n fontSize: 10,\n },\n sectionHeader: {\n fontSize: 12,\n bold: true,\n margin: [0, 10, 0, 5],\n },\n seededPlayer: {\n fontSize: 9,\n },\n },\n };\n\n // Execute action based on parameter\n if (action === 'open') {\n await openPDF({ docDefinition });\n } else {\n const filename = `${drawTitle.replace(/[^a-z0-9]/gi, '_').toLowerCase()}.pdf`;\n await savePDF({ docDefinition, filename });\n }\n}\n\n/**\n * Get seeded players content for PDF\n */\nfunction getSeededPlayersContent(_drawDefinition: any, _includeRankings: boolean): any[] {\n // TODO: Extract seeded participants from drawDefinition\n // For now, return placeholder\n \n const content = [\n {\n text: 'Seeded Players',\n style: 'sectionHeader',\n },\n {\n text: 'Seeded players list will be populated from draw data.',\n fontSize: 9,\n italics: true,\n color: '#999',\n margin: [0, 5, 0, 0],\n },\n ];\n\n // Example of how seeded players would be displayed:\n // const seededPlayers = extractSeededParticipants(drawDefinition);\n // if (seededPlayers.length > 0) {\n // content.push({\n // ol: seededPlayers.map(p => {\n // const name = fullName(p.participant);\n // const ranking = includeRankings ? ` [${p.ranking || 'NR'}]` : '';\n // return `${name}${ranking}`;\n // }),\n // fontSize: 9,\n // });\n // }\n\n return content;\n}\n\n/**\n * Extract seeded participants from draw definition\n * TODO: Implement when integrating with factory data\n */\n// function extractSeededParticipants(drawDefinition: any): any[] {\n// // Get all participants from structures\n// // Filter for seeded participants\n// // Sort by seed number\n// // Return formatted list\n// return [];\n// }\n","usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/services/pdf/generators/scheduleGenerator.ts","messages":[{"ruleId":"no-useless-assignment","severity":1,"message":"This assigned value is not used in subsequent statements.","line":245,"column":7,"messageId":"unnecessaryAssignment","endLine":245,"endColumn":18},{"ruleId":"no-constant-binary-expression","severity":2,"message":"Unexpected constant truthiness on the left-hand side of a `||` expression.","line":298,"column":18,"messageId":"constantShortCircuit","endLine":298,"endColumn":45}],"suppressedMessages":[],"errorCount":1,"fatalErrorCount":0,"warningCount":1,"fixableErrorCount":0,"fixableWarningCount":0,"source":"/**\n * Schedule PDF Generator\n * Generates PDF schedules using pdfMake native elements (tables, text)\n * Based on TMX-Suite-Legacy schedule PDF implementation\n */\nimport type { TDocumentDefinitions } from 'pdfmake/interfaces';\nimport { openPDF, savePDF } from '../export/pdfExport';\n\ninterface ScheduleGeneratorOptions {\n tournament: any;\n scheduledDate: string;\n courts: any[];\n rows: any[];\n options: {\n includeHeader: boolean;\n includeFooter: boolean;\n landscape: boolean;\n fullCourtNames: boolean;\n };\n action: 'open' | 'save';\n}\n\n/**\n * Generate schedule PDF\n */\nexport async function generateSchedulePDF(params: ScheduleGeneratorOptions): Promise<void> {\n const { tournament, scheduledDate, courts, rows, options, action } = params;\n\n\n\n // Determine orientation based on court count and user preference\n const pageOrientation = (courts.length >= 5 || options.landscape) ? 'landscape' : 'portrait';\n const portrait = pageOrientation === 'portrait';\n\n // Create column headers for courts\n const columnHeaders = createColumnHeaders(courts, options.fullCourtNames);\n \n // Convert grid rows to PDF rows (rows already organized by competitionEngine)\n const allPdfRows = convertGridRowsToPDFRows(rows, courts);\n \n // Filter out completely empty rows (rows with no matchUps at all)\n const pdfRows = allPdfRows.filter(row => \n row.some(cell => cell && Object.keys(cell).length > 0 && cell.matchUpId)\n );\n \n\n\n // Build the schedule table\n const scheduleTable = {\n table: {\n widths: ['*'],\n headerRows: 1,\n body: [\n [createHeaderRow(columnHeaders)],\n ...pdfRows.map((row, index) => [createMatchRow(index + 1, row)]),\n ],\n },\n layout: 'noBorders',\n };\n\n const content = [scheduleTable];\n\n // Font sizes based on orientation\n const teamFontSize = portrait ? 10 : 8;\n const headerMargin = options.includeHeader ? 80 : 0;\n const footerMargin = options.includeFooter ? 50 : 0;\n\n // Build document definition\n const docDefinition: TDocumentDefinitions = {\n pageSize: 'LETTER',\n pageOrientation,\n pageMargins: [20, headerMargin, 20, footerMargin],\n\n content,\n\n header: options.includeHeader\n ? () => createScheduleHeader(tournament, scheduledDate)\n : undefined,\n\n footer: options.includeFooter\n ? createScheduleFooter(tournament, scheduledDate)\n : undefined,\n\n styles: {\n docTitle: { fontSize: 12, bold: true },\n subtitle: { fontSize: 10, italics: true, bold: true },\n docName: { alignment: 'center', fontSize: 10, bold: true },\n tableHeader: { fontSize: 9 },\n tableData: { fontSize: 9, bold: true },\n headerNotice: { fontSize: 9, bold: true, italics: true, color: 'red' },\n teamName: { alignment: 'center', fontSize: teamFontSize - 1, bold: true },\n centeredText: { alignment: 'center', fontSize: 9, bold: false },\n centeredItalic: { alignment: 'center', fontSize: 8, bold: false, italics: true },\n centeredTableHeader: { alignment: 'center', fontSize: 9, bold: true },\n },\n };\n\n // Generate and output PDF\n const fileName = `Schedule_${scheduledDate}.pdf`;\n\n if (action === 'open') {\n await openPDF({ docDefinition });\n } else {\n await savePDF({ docDefinition, filename: fileName });\n }\n}\n\n/**\n * Convert grid rows to PDF rows\n * Grid rows from competitionEngine already have matchUps organized by court columns\n * Court keys are in format: C|0, C|1, C|2, etc. (sequential indices)\n */\nfunction convertGridRowsToPDFRows(gridRows: any[], courts: any[]): any[][] {\n return gridRows.map((gridRow) => {\n // Extract matchUp for each court using sequential index\n const rowCells = courts.map((_court, courtIndex) => {\n const courtKey = `C|${courtIndex}`;\n const matchUp = gridRow[courtKey];\n return matchUp || {};\n });\n return rowCells;\n });\n}\n\n/**\n * Create column headers for courts\n */\nfunction createColumnHeaders(courts: any[], fullNames: boolean): string[] {\n const minimumColumns = courts.length < 5 ? 1 : 8;\n const headers = new Array(Math.max(minimumColumns, courts.length)).fill('');\n \n courts.forEach((court, index) => {\n headers[index] = fullNames\n ? (court.courtName || court.name || '')\n : (court.courtName || court.name || '').split(' ')[0];\n });\n\n return headers;\n}\n\n\n\n/**\n * Create header row with court names\n */\nfunction createHeaderRow(courtNames: string[]): any {\n const headerCells = courtNames.map(name => ({\n table: {\n widths: ['*'],\n body: [[{ text: name || ' ', style: 'centeredTableHeader', margin: [0, 0, 0, 0] }]],\n },\n layout: 'noBorders',\n }));\n\n const cells = [{ text: ' ', width: 30 }, ...headerCells];\n const widths = [30, ...courtNames.map(() => '*' as const)];\n\n return {\n table: {\n widths,\n body: [cells],\n },\n layout: 'noBorders',\n };\n}\n\n/**\n * Create a match row\n */\nfunction createMatchRow(roundNumber: number, cells: any[]): any {\n const cellsArray = cells.map(matchUp => createMatchCell(matchUp));\n const matchCells = [{ stack: [createMatchCell({ oop: roundNumber })], width: 30 }, ...cellsArray];\n const widths = [30, ...cells.map(() => '*' as const)];\n\n return {\n unbreakable: true, // Prevent page breaks within match rows\n table: {\n widths,\n body: [matchCells],\n },\n layout: {\n paddingLeft: () => 0,\n paddingRight: () => 0,\n paddingTop: () => 0,\n paddingBottom: () => 0,\n // All borders\n hLineWidth: (i, node) => (i === 0 || i === node.table.body.length) ? 1 : 0,\n vLineWidth: () => 1, // All vertical lines\n hLineColor: () => '#999999',\n vLineColor: () => '#999999',\n },\n };\n}\n\n/**\n * Create a single match cell\n */\nfunction createMatchCell(matchUp: any): any {\n // Round number cell (for first column)\n if (matchUp?.oop !== undefined) {\n return {\n table: {\n widths: ['*'],\n body: [\n [{ text: matchUp.oop || ' ', style: 'centeredText', margin: [0, 0, 0, 0] }],\n ],\n },\n layout: {\n paddingLeft: () => 0,\n paddingRight: () => 0,\n paddingTop: () => 0,\n paddingBottom: () => 0,\n hLineWidth: () => 0,\n vLineWidth: () => 0,\n },\n };\n }\n \n // Empty cell - no matchUpId means no match scheduled\n if (!matchUp?.matchUpId) {\n return {\n table: {\n widths: ['*'],\n body: [\n [{ text: ' ', style: 'centeredText', margin: [0, 0, 0, 0] }],\n ],\n },\n layout: {\n paddingLeft: () => 0,\n paddingRight: () => 0,\n paddingTop: () => 0,\n paddingBottom: () => 0,\n hLineWidth: () => 0,\n vLineWidth: () => 0,\n },\n };\n }\n\n // Extract match details\n const scheduledTime = matchUp.schedule?.scheduledTime || '';\n const timeModifiers = matchUp.schedule?.timeModifiers || [];\n const timeModifierText = timeModifiers[0] || '';\n \n // Format time display\n let timeDisplay = '';\n if (timeModifierText) {\n // Map time modifier codes to display text\n const modifierMap: Record<string, string> = {\n 'FOLLOWED_BY': 'Followed by',\n 'NOT_BEFORE': 'Not before',\n 'AFTER_REST': 'After rest',\n };\n const modifierDisplay = modifierMap[timeModifierText] || timeModifierText;\n timeDisplay = scheduledTime ? `${modifierDisplay} ${scheduledTime}` : modifierDisplay;\n } else {\n timeDisplay = scheduledTime;\n }\n \n const roundName = matchUp.roundName || '';\n const eventName = matchUp.eventName || '';\n const category = matchUp.category?.categoryName || '';\n \n // Get participant names - handle both actual participants and potentials\n const getPotentialName = (participant: any) =>\n participant?.person?.standardFamilyName?.toUpperCase() || participant?.participantName || '';\n \n const potentialParticipants = matchUp.potentialParticipants || [];\n \n // Create array of potential strings for each side\n const potentialsArray = potentialParticipants.map((potential: any) => {\n if (!potential || !Array.isArray(potential)) return '';\n const names = potential.map(getPotentialName).filter(Boolean);\n return names.join(' or ');\n });\n \n const getParticipantName = (sideNumber: number) => {\n const side = matchUp.sides?.find((s: any) => s.sideNumber === sideNumber);\n return side?.participant?.participantName || '';\n };\n \n const side1 = getParticipantName(1) || potentialsArray[0] || 'TBD';\n const side2 = getParticipantName(2) || potentialsArray[1] || 'TBD';\n \n // Check if participants contain \"or\" (are potentials)\n const side1HasOr = side1.includes(' or ');\n const side2HasOr = side2.includes(' or ');\n \n const score = matchUp.score?.scoreStringSide1 || '';\n const x = ' ';\n \n // If we have a matchUpId, we always render (even if no participants yet)\n \n return {\n table: {\n widths: ['*'],\n body: [\n [{ text: timeDisplay || x, style: 'centeredText', margin: [0, 1, 0, 1], border: [false, false, false, false] }],\n [{ text: `${eventName} ${roundName}` || x, style: 'centeredItalic', margin: [0, 1, 0, 1], border: [false, false, false, false] }],\n [{ text: category || x, style: 'centeredText', margin: [0, 1, 0, 1], border: [false, false, false, false] }],\n [{ text: side1 || x, style: 'teamName', margin: [0, 1, 0, 1], border: [false, false, false, false], color: side1HasOr ? '#555555' : undefined }],\n [{ text: (side1 && side2) ? 'vs.' : x, style: 'centeredText', margin: [0, 1, 0, 1], border: [false, false, false, false] }],\n [{ text: side2 || x, style: 'teamName', margin: [0, 1, 0, 1], border: [false, false, false, false], color: side2HasOr ? '#555555' : undefined }],\n [{ text: x, style: 'centeredText', margin: [0, 0, 0, 0], border: [false, false, false, false] }],\n [{ text: score || x, style: 'centeredText', margin: [0, 1, 0, 1], border: [false, false, false, false] }],\n ],\n },\n layout: {\n paddingLeft: () => 3,\n paddingRight: () => 3,\n paddingTop: () => 1,\n paddingBottom: () => 1,\n hLineWidth: () => 0,\n vLineWidth: () => 0,\n },\n };\n}\n\n/**\n * Create schedule header\n */\nfunction createScheduleHeader(tournament: any, scheduledDate: string): any {\n const tournamentName = tournament.tournamentName || '';\n const organization = tournament.organizationName || '';\n \n return {\n margin: [20, 10, 20, 10],\n fontSize: 10,\n table: {\n widths: ['*', '*', '*'],\n body: [\n [\n { text: tournamentName, colSpan: 3, style: 'docTitle', margin: [0, 0, 0, 5] },\n {},\n {},\n ],\n [\n { text: 'Order of Play', colSpan: 3, style: 'docName', margin: [0, 0, 0, 5] },\n {},\n {},\n ],\n [\n { text: scheduledDate, colSpan: 3, style: 'centeredText', margin: [0, 0, 0, 5] },\n {},\n {},\n ],\n [\n { text: organization || '', style: 'tableData' },\n {},\n {},\n ],\n ],\n },\n layout: 'noBorders',\n };\n}\n\n/**\n * Create schedule footer\n */\nfunction createScheduleFooter(tournament: any, scheduledDate: string): any {\n return {\n margin: [20, 10, 20, 10],\n text: `${tournament.tournamentName || ''} - ${scheduledDate}`,\n alignment: 'center',\n fontSize: 9,\n };\n}\n","usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/services/pdf/utils/drawRenderer.ts","messages":[{"ruleId":"@typescript-eslint/no-unused-vars","severity":1,"message":"'error' is defined but never used.","line":151,"column":12,"messageId":"unusedVar","endLine":151,"endColumn":17}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":1,"fixableErrorCount":0,"fixableWarningCount":0,"source":"/**\n * Draw Renderer for PDF Generation\n * Renders draw structures to PNG for embedding in PDFs\n */\nimport { compositions, renderContainer, renderStructure } from 'courthive-components';\nimport { tournamentEngine } from 'tods-competition-factory';\nimport html2canvas from 'html2canvas';\n\ninterface RenderDrawOptions {\n drawId: string;\n structureId?: string;\n width?: number;\n height?: number;\n}\n\n/**\n * Render a draw structure to PNG data URI for PDF embedding\n * @param options - Draw rendering options\n * @returns Promise resolving to PNG data URI\n */\nexport async function renderDrawToPNG(options: RenderDrawOptions): Promise<string> {\n const { drawId, structureId, width = 2000 } = options;\n\n // Get matchUps for the draw\n const matchUpsResult = tournamentEngine.allDrawMatchUps({\n drawId,\n inContext: true,\n matchUpFilters: structureId ? { structureIds: [structureId] } : undefined,\n });\n\n const displayMatchUps = matchUpsResult?.matchUps || [];\n\n if (displayMatchUps.length === 0) {\n throw new Error('No matchUps found for draw');\n }\n\n // Use default composition - try different composition types\n const composition = compositions?.tableView || compositions?.gridView || compositions?.[Object.keys(compositions || {})[0]];\n\n // Create off-screen container - let it size naturally\n const offscreenContainer = document.createElement('div');\n offscreenContainer.style.position = 'absolute';\n offscreenContainer.style.top = '-99999px'; // Off-screen\n offscreenContainer.style.left = '-99999px';\n offscreenContainer.style.width = `${width}px`; // Set width but not height\n offscreenContainer.style.backgroundColor = 'white';\n document.body.appendChild(offscreenContainer);\n\n try {\n // Create event handlers for PDF rendering - omit scoreClick to prevent Score buttons from rendering\n const eventHandlers = {\n roundClick: () => {},\n scheduleClick: () => {},\n venueClick: () => {},\n participantClick: () => {},\n matchUpClick: () => {},\n // scoreClick intentionally omitted - no scoring in PDF printouts\n };\n\n\n\n // Check if composition is available\n if (!composition) {\n throw new Error('No composition available. compositions object may not be loaded.');\n }\n\n // Render the draw structure\n const content = renderContainer({\n content: renderStructure({\n context: { drawId, structureId },\n matchUps: displayMatchUps,\n composition,\n searchActive: false,\n eventHandlers,\n selectedMatchUpId: undefined,\n structureId,\n finalColumn: undefined,\n minWidth: undefined,\n }),\n theme: composition.theme,\n });\n\n offscreenContainer.appendChild(content);\n\n // Wait a moment for initial render\n await new Promise(resolve => setTimeout(resolve, 100));\n\n // Find the actual draw structure element and force it to expand fully\n const drawStructure = offscreenContainer.querySelector('.c-iZKoSQ, [class*=\"c-\"]');\n if (drawStructure) {\n (drawStructure as HTMLElement).style.overflow = 'visible !important';\n (drawStructure as HTMLElement).style.height = 'auto !important';\n (drawStructure as HTMLElement).style.maxHeight = 'none !important';\n }\n\n // Remove any overflow/height constraints from all nested elements\n const allElements = offscreenContainer.querySelectorAll('*');\n allElements.forEach((el: any) => {\n const computedStyle = window.getComputedStyle(el);\n if (computedStyle.overflow !== 'visible') {\n el.style.overflow = 'visible';\n }\n if (computedStyle.maxHeight !== 'none') {\n el.style.maxHeight = 'none';\n }\n });\n\n // Wait for re-layout after removing constraints\n await new Promise(resolve => setTimeout(resolve, 500));\n\n // Get the actual rendered dimensions from the FIRST CHILD (the draw container)\n const drawContainer = offscreenContainer.firstElementChild as HTMLElement;\n const actualHeight = drawContainer ? drawContainer.scrollHeight : offscreenContainer.scrollHeight;\n const actualWidth = drawContainer ? drawContainer.offsetWidth : offscreenContainer.offsetWidth;\n\n // Use html2canvas to convert the HTML draw to an image\n const canvas = await html2canvas(offscreenContainer, {\n backgroundColor: '#ffffff',\n scale: 2, // Higher quality\n logging: false,\n useCORS: true,\n allowTaint: true,\n width: actualWidth,\n height: actualHeight,\n windowWidth: actualWidth,\n windowHeight: actualHeight,\n });\n\n const dataURI = canvas.toDataURL('image/png');\n return dataURI;\n } finally {\n // Clean up off-screen container\n document.body.removeChild(offscreenContainer);\n }\n}\n\n/**\n * Check if a draw can be rendered (has matchUps)\n * @param drawId - Draw ID to check\n * @param structureId - Optional structure ID\n * @returns True if draw has matchUps and can be rendered\n */\nexport function canRenderDraw(drawId: string, structureId?: string): boolean {\n try {\n const matchUpsResult = tournamentEngine.allDrawMatchUps({\n drawId,\n matchUpFilters: structureId ? { structureIds: [structureId] } : undefined,\n });\n\n return (matchUpsResult?.matchUps?.length || 0) > 0;\n } catch (error) {\n return false;\n }\n}\n","usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/services/pdf/utils/primitives.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/services/pdf/utils/svgUtilities.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/services/processDirective.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/services/publishing/toggleDrawPublishState.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/services/publishing/toggleEventPublishState.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/services/qrFx.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/services/setDev.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/services/settings/settingsStorage.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/services/sheets/fetchGoogleSheet.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/services/storage/addOrUpdateTournament.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/services/storage/importTournaments.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/services/storage/removeProviderTournament.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/services/storage/saveTournamentRecord.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/services/storage/tmx2db.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/services/tmxTimer.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/services/transitions/goHome.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/services/transitions/resetTournament.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/services/transitions/scoreMatchUp.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/services/transitions/screenSlaver.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/services/translator.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/services/updateRegisteredPlayers.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/settings/env.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/settings/setActiveScale.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/tools/freeScore/freeScore.ts","messages":[{"ruleId":"no-useless-assignment","severity":1,"message":"This assigned value is not used in subsequent statements.","line":1227,"column":7,"messageId":"unnecessaryAssignment","endLine":1227,"endColumn":28}],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":1,"fixableErrorCount":0,"fixableWarningCount":0,"source":"/**\n * freeScore Parser\n *\n * Parses free-form score input using matchUpFormat context for intelligent interpretation.\n * Character-by-character state machine that handles ambiguous digit sequences.\n *\n * Goals:\n * - Accept any reasonable score notation (spaces, dashes, brackets optional)\n * - Use matchUpFormat to disambiguate (e.g., \"123\" → \"12-3\" vs \"1-23\" based on setTo)\n * - Provide confidence scores and suggestions for ambiguous inputs\n * - Give helpful error messages with position context\n * - Support incomplete scores (user typing in real-time)\n */\n\nimport { matchUpFormatCode, matchUpStatusConstants } from 'tods-competition-factory';\nimport type { ParsedFormat } from 'tods-competition-factory';\n\nconst { RETIRED, WALKOVER, DEFAULTED, SUSPENDED, CANCELLED, INCOMPLETE, DEAD_RUBBER, IN_PROGRESS, AWAITING_RESULT } =\n matchUpStatusConstants;\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface ParsedSet {\n side1Score?: number;\n side2Score?: number;\n side1TiebreakScore?: number;\n side2TiebreakScore?: number;\n winningSide?: number;\n setNumber: number;\n}\n\nexport interface ParserState {\n // Input tracking\n input: string;\n position: number;\n\n // Score building\n setIndex: number;\n currentSide1Buffer: string;\n currentSide2Buffer: string;\n currentTiebreakSide1Buffer: string;\n currentTiebreakSide2Buffer: string;\n sets: ParsedSet[];\n\n // Format context\n parsedFormat: ParsedFormat;\n\n // State machine\n state: TokenizerState;\n inTiebreak: boolean;\n expectingTiebreakOnly: boolean; // For match tiebreak formats\n\n // Irregular endings (matchUpStatus)\n irregularEnding?: string;\n irregularEndingPosition?: number;\n\n // Validation\n isValid: boolean;\n errors: ParseError[];\n warnings: string[];\n\n // Ambiguity tracking\n confidence: number; // 0.0 to 1.0\n ambiguities: string[];\n suggestions: string[];\n}\n\nexport enum TokenizerState {\n START = 'START',\n PARSING_SIDE1 = 'PARSING_SIDE1',\n PARSING_SIDE2 = 'PARSING_SIDE2',\n PARSING_TIEBREAK_SIDE1 = 'PARSING_TIEBREAK_SIDE1',\n PARSING_TIEBREAK_SIDE2 = 'PARSING_TIEBREAK_SIDE2',\n SET_COMPLETE = 'SET_COMPLETE',\n MATCH_COMPLETE = 'MATCH_COMPLETE',\n ERROR = 'ERROR',\n}\n\nexport interface ParseError {\n position: number;\n message: string;\n expected?: string;\n got?: string;\n context?: string;\n}\n\nexport interface ParseResult {\n valid: boolean;\n formattedScore: string;\n sets: ParsedSet[];\n confidence: number;\n errors: ParseError[];\n warnings: string[];\n ambiguities: string[];\n suggestions: string[];\n incomplete: boolean;\n matchComplete: boolean;\n matchUpStatus?: string; // Uses matchUpStatusConstants from factory\n}\n\n// ============================================================================\n// Helper Functions\n// ============================================================================\n\n/**\n * Detect irregular ending patterns in remaining input\n * Returns the matchUpStatus constant and the position where it was detected\n */\nfunction detectIrregularEnding(input: string, startPos: number): { ending?: string; endPos: number } {\n const remaining = input.substring(startPos).trim().toLowerCase();\n\n // Check for longer/more-specific patterns first to avoid ambiguity\n\n // IN PROGRESS: 'in', 'inp', 'in prog', 'in progress' (check first to catch \"in \" patterns)\n const inProgressPattern = /^in(\\s+p(rog(ress)?)?)?$/i;\n if (inProgressPattern.exec(remaining)) {\n return { ending: IN_PROGRESS, endPos: input.length };\n }\n\n // INCOMPLETE: 'inc', 'incomp', 'incomplete' (requires 'inc' minimum to avoid conflict with 'in')\n const incompletePattern = /^inc(omp(lete?)?)?/i;\n if (incompletePattern.exec(remaining)) {\n return { ending: INCOMPLETE, endPos: input.length };\n }\n\n // DEAD RUBBER: 'dr', 'dead', 'dead r', 'dead rubber' (check before DEFAULTED since both start with 'd')\n const deadRubberPattern = /^(dr|dead(\\s*r(ubber)?)?)/i;\n if (deadRubberPattern.exec(remaining)) {\n return { ending: DEAD_RUBBER, endPos: input.length };\n }\n\n // RETIRED: 'r', 'ret', 'retired'\n const retiredPattern = /^r(et(ired?)?)?/i;\n if (retiredPattern.exec(remaining)) {\n return { ending: RETIRED, endPos: input.length };\n }\n\n // WALKOVER: 'w', 'wo', 'w/o', 'walkover'\n const walkoverPattern = /^(w(\\/o|o|alk(over?)?)?)/i;\n if (walkoverPattern.exec(remaining)) {\n return { ending: WALKOVER, endPos: input.length };\n }\n\n // DEFAULTED: 'd', 'def', 'defaulted'\n const defaultedPattern = /^d(ef(aulted?)?)?/i;\n if (defaultedPattern.exec(remaining)) {\n return { ending: DEFAULTED, endPos: input.length };\n }\n\n // SUSPENDED: 's', 'susp', 'suspended'\n const suspendedPattern = /^s(usp(ended?)?)?/i;\n if (suspendedPattern.exec(remaining)) {\n return { ending: SUSPENDED, endPos: input.length };\n }\n\n // CANCELLED: 'c', 'canc', 'cancelled', 'canceled'\n const cancelledPattern = /^c(anc(ell?ed?)?)?/i;\n if (cancelledPattern.exec(remaining)) {\n return { ending: CANCELLED, endPos: input.length };\n }\n\n // AWAITING RESULT: 'a', 'await', 'awaiting', 'awaiting result'\n const awaitingPattern = /^a(wait(ing(\\s*r(esult)?)?)?)?/i;\n if (awaitingPattern.exec(remaining)) {\n return { ending: AWAITING_RESULT, endPos: input.length };\n }\n\n return { endPos: startPos };\n}\n\n/**\n * Get the set format for a specific set index\n * Uses finalSetFormat for the deciding set if it exists\n */\nfunction getSetFormat(parsedFormat: ParsedFormat, setIndex: number): any {\n const bestOf = parsedFormat.bestOf || 3;\n const isDecidingSet = setIndex === bestOf - 1; // Last possible set\n\n if (isDecidingSet && parsedFormat.finalSetFormat) {\n return parsedFormat.finalSetFormat;\n }\n\n return parsedFormat.setFormat;\n}\n\n/**\n * Check if current set format is tiebreak-only (match tiebreak)\n */\nfunction isTiebreakOnlySet(setFormat: any): boolean {\n return !!setFormat?.tiebreakSet?.tiebreakTo && !setFormat?.setTo;\n}\n\n/**\n * Get maximum valid score for a game in current set\n */\nfunction getMaxGameScore(setFormat: any): number {\n if (isTiebreakOnlySet(setFormat)) {\n return 0; // No game scores for tiebreak-only sets\n }\n\n const setTo = setFormat?.setTo || 6;\n return setTo + 1; // e.g., 7 for setTo:6 (can reach 7-5)\n}\n\n/**\n * Get tiebreak limit for current set\n */\nfunction getTiebreakLimit(setFormat: any): number {\n const tiebreakSet = setFormat?.tiebreakSet;\n const tiebreakFormat = setFormat?.tiebreakFormat;\n\n if (tiebreakSet) {\n return tiebreakSet.tiebreakTo || 10;\n }\n\n if (tiebreakFormat) {\n return tiebreakFormat.tiebreakTo || 7;\n }\n\n return 7; // Default\n}\n\n/**\n * Check if character is a digit\n */\nfunction isDigit(char: string): boolean {\n return char >= '0' && char <= '9';\n}\n\n/**\n * Check if string contains only digits\n */\nfunction isAllDigits(str: string): boolean {\n for (const char of str) {\n if (!isDigit(char)) {\n return false;\n }\n }\n return true;\n}\n\n/**\n * Check if character is a separator (anything non-digit, non-tiebreak-indicator)\n * This allows flexible input: spaces, dashes, commas, slashes, etc.\n */\nfunction isSeparator(char: string): boolean {\n // Not a digit, not a tiebreak indicator, not a closer\n return !isDigit(char) && !isTiebreakIndicator(char) && !isTiebreakCloser(char);\n}\n\n/**\n * Check if character indicates tiebreak (open paren or bracket)\n */\nfunction isTiebreakIndicator(char: string): boolean {\n return char === '(' || char === '[';\n}\n\n/**\n * Check if character closes tiebreak\n */\nfunction isTiebreakCloser(char: string): boolean {\n return char === ')' || char === ']';\n}\n\n/**\n * Check if score triggers tiebreak (games tied at tiebreakAt)\n */\nfunction shouldEnterTiebreak(side1: number, side2: number, setFormat: any): boolean {\n const tiebreakAt = setFormat?.tiebreakAt || setFormat?.setTo || 6;\n return side1 === tiebreakAt && side2 === tiebreakAt;\n}\n\n/**\n * Check if set is complete\n */\nfunction isSetComplete(side1: number, side2: number, setFormat: any): boolean {\n const setTo = setFormat?.setTo || 6;\n const maxScore = Math.max(side1, side2);\n const minScore = Math.min(side1, side2);\n\n // Must reach setTo\n if (maxScore < setTo) {\n return false;\n }\n\n // Win by 2\n if (maxScore - minScore >= 2) {\n return true;\n }\n\n // Can't exceed setTo + 1\n if (maxScore > setTo + 1) {\n return false;\n }\n\n return false;\n}\n\n/**\n * Check if tiebreak is complete\n */\nfunction isTiebreakComplete(side1: number, side2: number, setFormat: any): boolean {\n const tiebreakTo = getTiebreakLimit(setFormat);\n const maxScore = Math.max(side1, side2);\n const minScore = Math.min(side1, side2);\n\n // Check for NoAD in tiebreak-only sets\n const isNoAd = setFormat?.tiebreakSet?.NoAD || setFormat?.tiebreakFormat?.NoAD;\n\n if (isNoAd) {\n // First to tiebreakTo wins (no win-by-2)\n return maxScore >= tiebreakTo;\n }\n\n // Standard: must reach tiebreakTo and win by 2\n if (maxScore < tiebreakTo) {\n return false;\n }\n\n return maxScore - minScore >= 2;\n}\n\n/**\n * Parse a tiebreak set score (1-0 or 0-1)\n */\nfunction parseTiebreakSet(setString: string, setNumber: number): ParsedSet {\n const side1 = setString === '1-0' ? 1 : 0;\n const side2 = setString === '0-1' ? 1 : 0;\n\n return {\n side1TiebreakScore: side1,\n side2TiebreakScore: side2,\n setNumber,\n winningSide: side1 > side2 ? 1 : 2,\n };\n}\n\n/**\n * Parse a regular timed set score (#-#)\n */\nfunction parseRegularTimedSet(setString: string, setNumber: number, errors: ParseError[]): ParsedSet | null {\n const dashIndex = setString.indexOf('-');\n\n if (dashIndex === -1) {\n errors.push({\n position: 0,\n message: `Set ${setNumber}: Invalid format \"${setString}\". Expected \"#-#\" (e.g., \"5-3\")`,\n expected: '#-#',\n got: setString,\n });\n return null;\n }\n\n const side1Str = setString.slice(0, dashIndex);\n const side2Str = setString.slice(dashIndex + 1);\n\n if (!side1Str || !side2Str || !isAllDigits(side1Str) || !isAllDigits(side2Str)) {\n errors.push({\n position: 0,\n message: `Set ${setNumber}: Invalid format \"${setString}\". Expected \"#-#\" (e.g., \"5-3\")`,\n expected: '#-#',\n got: setString,\n });\n return null;\n }\n\n const side1 = Number.parseInt(side1Str, 10);\n const side2 = Number.parseInt(side2Str, 10);\n\n return {\n side1Score: side1,\n side2Score: side2,\n setNumber,\n };\n}\n\n/**\n * Parse final set when conditional tiebreak expected but pattern doesn't match\n */\nfunction parseFinalSetWithConditionalTB(setString: string, setNumber: number, errors: ParseError[]): ParsedSet | null {\n const dashIndex = setString.indexOf('-');\n\n if (dashIndex === -1) {\n errors.push({\n position: 0,\n message: `Final set: Invalid format \"${setString}\". Expected TB format \"1-0\" or \"0-1\", or regular \"#-#\"`,\n expected: '1-0, 0-1, or #-#',\n got: setString,\n });\n return null;\n }\n\n const side1Str = setString.slice(0, dashIndex);\n const side2Str = setString.slice(dashIndex + 1);\n\n if (!side1Str || !side2Str || !isAllDigits(side1Str) || !isAllDigits(side2Str)) {\n errors.push({\n position: 0,\n message: `Final set: Invalid format \"${setString}\". Expected TB format \"1-0\" or \"0-1\", or regular \"#-#\"`,\n expected: '1-0, 0-1, or #-#',\n got: setString,\n });\n return null;\n }\n\n const side1 = Number.parseInt(side1Str, 10);\n const side2 = Number.parseInt(side2Str, 10);\n\n return {\n side1Score: side1,\n side2Score: side2,\n setNumber,\n };\n}\n\n/**\n * Calculate aggregate totals from timed sets\n */\nfunction calculateAggregateTotals(sets: ParsedSet[], count: number): { side1: number; side2: number } {\n return sets.slice(0, count).reduce(\n (totals, set) => {\n totals.side1 += set.side1Score || 0;\n totals.side2 += set.side2Score || 0;\n return totals;\n },\n { side1: 0, side2: 0 },\n );\n}\n\n/**\n * Validate aggregate scoring with conditional tiebreak\n */\nfunction validateAggregateScoring(\n sets: ParsedSet[],\n timedSetsCount: number,\n errors: ParseError[],\n): void {\n const timedSets = sets.filter((s) => s.side1Score !== undefined);\n const hasTBSet = sets.some((s) => s.side1TiebreakScore !== undefined);\n\n if (timedSets.length < timedSetsCount) {\n return; // Not enough sets to validate yet\n }\n\n const aggregateTotals = calculateAggregateTotals(timedSets, timedSetsCount);\n const aggregateTied = aggregateTotals.side1 === aggregateTotals.side2;\n const finalSetIsTimed = timedSets.length > timedSetsCount;\n\n if (aggregateTied && !hasTBSet) {\n if (finalSetIsTimed) {\n const finalSet = timedSets[timedSetsCount];\n errors.push({\n position: 0,\n message: `Aggregate tied (${aggregateTotals.side1}-${aggregateTotals.side2}). Final set score \"${finalSet.side1Score}-${finalSet.side2Score}\" invalid. TB1 only accepts \"1-0\" or \"0-1\"`,\n });\n } else {\n errors.push({\n position: 0,\n message: `Aggregate tied (${aggregateTotals.side1}-${aggregateTotals.side2}), final TB required`,\n });\n }\n } else if (!aggregateTied && hasTBSet) {\n errors.push({\n position: 0,\n message: `Aggregate not tied (${aggregateTotals.side1}-${aggregateTotals.side2}), final TB not allowed`,\n });\n } else if (!aggregateTied && finalSetIsTimed) {\n errors.push({\n position: 0,\n message: `Aggregate not tied (${aggregateTotals.side1}-${aggregateTotals.side2}), extra sets not allowed`,\n });\n }\n}\n\n/**\n * Parse individual set strings into ParsedSet objects\n */\nfunction parseTimedSetStrings(\n setStrings: string[],\n conditionalFinalTB: boolean,\n expectedSetCount: number,\n errors: ParseError[],\n): ParsedSet[] {\n const sets: ParsedSet[] = [];\n\n for (let i = 0; i < setStrings.length; i++) {\n const setString = setStrings[i];\n const setNumber = i + 1;\n const isFinalSetPosition = setNumber === expectedSetCount;\n const matchesTBPattern = setString === '1-0' || setString === '0-1';\n const isTBSet = conditionalFinalTB && isFinalSetPosition && matchesTBPattern;\n\n if (isTBSet) {\n sets.push(parseTiebreakSet(setString, setNumber));\n } else if (conditionalFinalTB && isFinalSetPosition && !matchesTBPattern) {\n const set = parseFinalSetWithConditionalTB(setString, setNumber, errors);\n if (set) sets.push(set);\n } else {\n const set = parseRegularTimedSet(setString, setNumber, errors);\n if (set) sets.push(set);\n }\n }\n\n return sets;\n}\n\n/**\n * Create empty input result for timed exactly format\n */\nfunction createEmptyTimedExactlyResult(conditionalFinalTB: boolean, expectedSetCount: number): ParseResult {\n return {\n valid: false,\n formattedScore: '',\n sets: [],\n confidence: 1,\n errors: [],\n warnings: [],\n ambiguities: [],\n suggestions: conditionalFinalTB\n ? [`Enter ${expectedSetCount - 1} timed sets (final TB only if tied)`]\n : [`Enter ${expectedSetCount} sets in format: #-# #-# ...`],\n incomplete: true,\n matchComplete: false,\n };\n}\n\n/**\n * Validate set count for timed exactly format\n */\nfunction validateTimedSetCount(\n setsLength: number,\n minSets: number,\n maxSets: number,\n conditionalFinalTB: boolean,\n errors: ParseError[],\n): void {\n if (setsLength > maxSets) {\n const expectedSetsText = conditionalFinalTB ? `${minSets}-${maxSets}` : `${maxSets}`;\n errors.push({\n position: 0,\n message: `Too many sets: got ${setsLength}, expected ${expectedSetsText}`,\n });\n }\n}\n\n/**\n * Generate suggestions for incomplete timed sets\n */\nfunction generateTimedSetSuggestions(\n incomplete: boolean,\n conditionalFinalTB: boolean,\n minSets: number,\n setsLength: number,\n expectedSetCount: number,\n): string[] {\n if (!incomplete) return [];\n\n if (conditionalFinalTB) {\n const remaining = minSets - setsLength;\n return [`Enter ${remaining} more timed set${remaining === 1 ? '' : 's'} (or final TB if aggregate tied)`];\n }\n\n const remaining = expectedSetCount - setsLength;\n return [`Need ${remaining} more set${remaining === 1 ? '' : 's'}`];\n}\n\n/**\n * Format timed sets for output\n */\nfunction formatTimedSets(sets: ParsedSet[]): string {\n return sets\n .map((s) =>\n s.side1TiebreakScore !== undefined\n ? `${s.side1TiebreakScore}-${s.side2TiebreakScore}`\n : `${s.side1Score}-${s.side2Score}`,\n )\n .join(' ');\n}\n\n/**\n * Simple parser for timed sets with exactly format\n * For formats like SET3X-S:T10, expects exactly N sets in \"#-#\" format\n * For aggregate with conditional TB (SET3X-S:T10A-F:TB1):\n * - Accepts N-1 sets if aggregate not tied (match ends early)\n * - Requires N sets with final TB if aggregate tied\n * No smart logic - just parse literal \"#-# #-# #-#\" patterns\n */\nfunction parseTimedExactlyScore(input: string, parsedFormat: ParsedFormat): ParseResult {\n const expectedSetCount = parsedFormat.exactly || parsedFormat.bestOf || 1;\n const trimmedInput = input.trim();\n\n const isAggregateScoring = parsedFormat.setFormat?.based === 'A' || parsedFormat.finalSetFormat?.based === 'A';\n const hasFinalTiebreak = parsedFormat.finalSetFormat?.tiebreakSet?.tiebreakTo !== undefined;\n const conditionalFinalTB = isAggregateScoring && hasFinalTiebreak;\n\n if (!trimmedInput) {\n return createEmptyTimedExactlyResult(conditionalFinalTB, expectedSetCount);\n }\n\n const setStrings = trimmedInput.split(/\\s+/);\n const errors: ParseError[] = [];\n const timedSetsCount = conditionalFinalTB ? expectedSetCount - 1 : expectedSetCount;\n\n const sets = parseTimedSetStrings(setStrings, conditionalFinalTB, expectedSetCount, errors);\n\n if (conditionalFinalTB && errors.length === 0) {\n validateAggregateScoring(sets, timedSetsCount, errors);\n }\n\n const minSets = conditionalFinalTB ? timedSetsCount : expectedSetCount;\n const maxSets = expectedSetCount;\n\n validateTimedSetCount(sets.length, minSets, maxSets, conditionalFinalTB, errors);\n\n const incomplete = sets.length < minSets && errors.length === 0;\n const matchComplete =\n (sets.length === expectedSetCount || (conditionalFinalTB && sets.length === timedSetsCount)) && errors.length === 0;\n\n const formattedScore = formatTimedSets(sets);\n\n return {\n valid: errors.length === 0 && (sets.length === minSets || sets.length === maxSets),\n formattedScore,\n sets,\n confidence: errors.length === 0 ? 1 : 0,\n errors,\n warnings: [],\n ambiguities: [],\n suggestions: generateTimedSetSuggestions(incomplete, conditionalFinalTB, minSets, sets.length, expectedSetCount),\n incomplete,\n matchComplete,\n };\n}\n\n// ============================================================================\n// Main Parser Function\n// ============================================================================\n\n/**\n * Parse free-form score string using matchUpFormat context\n */\nexport function parseScore(input: string, matchUpFormat: string | ParsedFormat): ParseResult {\n // Parse format if string provided\n const parsedFormat = typeof matchUpFormat === 'string' ? matchUpFormatCode.parse(matchUpFormat) : matchUpFormat;\n\n if (!parsedFormat) {\n return {\n valid: false,\n formattedScore: '',\n sets: [],\n confidence: 0,\n errors: [\n {\n position: 0,\n message: 'Invalid matchUpFormat',\n },\n ],\n warnings: [],\n ambiguities: [],\n suggestions: [],\n incomplete: false,\n matchComplete: false,\n };\n }\n\n // IMPORTANT: For timed sets with exactly format, use simple parsing\n // No smart logic, just expect exact number of \"#-#\" entries\n const isTimed = !!(parsedFormat.setFormat?.timed || parsedFormat.finalSetFormat?.timed);\n const isExactlyFormat = !!parsedFormat.exactly || parsedFormat.bestOf === 1;\n\n if (isTimed && isExactlyFormat) {\n return parseTimedExactlyScore(input, parsedFormat);\n }\n\n // Initialize state\n const state: ParserState = {\n input,\n position: 0,\n setIndex: 0,\n currentSide1Buffer: '',\n currentSide2Buffer: '',\n currentTiebreakSide1Buffer: '',\n currentTiebreakSide2Buffer: '',\n sets: [],\n parsedFormat,\n state: TokenizerState.START,\n inTiebreak: false,\n expectingTiebreakOnly: false,\n isValid: true,\n errors: [],\n warnings: [],\n confidence: 1,\n ambiguities: [],\n suggestions: [],\n };\n\n // Check if first set is tiebreak-only\n const firstSetFormat = getSetFormat(parsedFormat, 0);\n if (isTiebreakOnlySet(firstSetFormat)) {\n state.expectingTiebreakOnly = true;\n state.inTiebreak = true;\n state.state = TokenizerState.PARSING_TIEBREAK_SIDE1;\n } else {\n state.state = TokenizerState.PARSING_SIDE1;\n }\n\n // Process character by character\n while (state.position < input.length && state.isValid) {\n processCharacter(state);\n }\n\n // Finalize any incomplete set\n finalizeCurrentScore(state);\n\n // Handle irregular endings\n let finalSets = state.sets;\n let matchUpStatus: string | undefined;\n\n if (state.irregularEnding) {\n matchUpStatus = state.irregularEnding;\n\n // Remove score for statuses where match didn't start or wasn't played\n if (\n state.irregularEnding === WALKOVER ||\n state.irregularEnding === CANCELLED ||\n state.irregularEnding === DEAD_RUBBER\n ) {\n finalSets = [];\n }\n // Keep score for other statuses (RETIRED, DEFAULTED, SUSPENDED, INCOMPLETE, IN_PROGRESS, AWAITING_RESULT)\n }\n\n // Check if match is complete\n const matchComplete = state.irregularEnding ? false : checkMatchComplete(state);\n\n // Format output\n const formattedScore = formatScore(state);\n\n return {\n valid: state.isValid && state.errors.length === 0,\n formattedScore,\n sets: finalSets,\n confidence: state.confidence,\n errors: state.errors,\n warnings: state.warnings,\n ambiguities: state.ambiguities,\n suggestions: state.suggestions,\n incomplete: !matchComplete && finalSets.length > 0,\n matchComplete,\n matchUpStatus,\n };\n}\n\n/**\n * Process a single character from input\n */\nfunction processCharacter(state: ParserState): void {\n const char = state.input[state.position];\n\n // Check for irregular endings (RET, WO, DEF, etc.)\n // These can appear after a score or by themselves\n if (!isDigit(char) && !isTiebreakIndicator(char) && !isTiebreakCloser(char)) {\n const detection = detectIrregularEnding(state.input, state.position);\n if (detection.ending) {\n state.irregularEnding = detection.ending;\n state.irregularEndingPosition = state.position;\n state.position = detection.endPos; // Skip to end of input\n return;\n }\n }\n\n // Skip whitespace and separators in certain contexts\n if (isSeparator(char)) {\n handleSeparator(state);\n return;\n }\n\n // Handle tiebreak indicators\n if (isTiebreakIndicator(char)) {\n handleTiebreakStart(state);\n return;\n }\n\n if (isTiebreakCloser(char)) {\n handleTiebreakEnd(state);\n return;\n }\n\n // Handle digits\n if (isDigit(char)) {\n handleDigit(state, char);\n return;\n }\n\n // Unknown character\n state.errors.push({\n position: state.position,\n message: `Unexpected character '${char}'`,\n got: char,\n context: getContext(state),\n });\n state.isValid = false;\n}\n\n/**\n * Handle separator characters\n */\nfunction handleSeparator(state: ParserState): void {\n // Separators can indicate:\n // 1. Between side1 and side2 (e.g., \"6-4\")\n // 2. Between sets (e.g., \"6-4 6-3\")\n // 3. Tiebreak indicator (e.g., \"6-7(5)\")\n\n if (state.state === TokenizerState.PARSING_SIDE1 && state.currentSide1Buffer) {\n // Transition to side2\n state.state = TokenizerState.PARSING_SIDE2;\n state.position++;\n return;\n }\n\n if (state.state === TokenizerState.PARSING_SIDE2 && state.currentSide2Buffer) {\n // Check if set requires a tiebreak\n const side1Score = Number.parseInt(state.currentSide1Buffer);\n const side2Score = Number.parseInt(state.currentSide2Buffer);\n const currentSetFormat = getSetFormat(state.parsedFormat, state.setIndex);\n\n if (requiresTiebreak(side1Score, side2Score, currentSetFormat)) {\n // Transition to tiebreak parsing - next digits are the tiebreak score\n state.inTiebreak = true;\n state.state = TokenizerState.PARSING_TIEBREAK_SIDE1;\n state.position++;\n return;\n }\n\n // Otherwise finalize the set normally\n finalizeSet(state);\n state.position++;\n return;\n }\n\n if (state.state === TokenizerState.PARSING_TIEBREAK_SIDE1 && state.currentTiebreakSide1Buffer) {\n // Check if this is a set tiebreak (has game scores) or match tiebreak (tiebreak-only)\n const isSetTiebreak = state.currentSide1Buffer && state.currentSide2Buffer;\n\n if (isSetTiebreak) {\n // Set tiebreak: only need one score (the losing score) - finalize the set\n finalizeSet(state);\n state.position++;\n return;\n } else {\n // Match tiebreak: need both scores - transition to side2\n state.state = TokenizerState.PARSING_TIEBREAK_SIDE2;\n state.position++;\n return;\n }\n }\n\n // Just skip separator\n state.position++;\n}\n\n/**\n * Handle start of tiebreak notation\n */\nfunction handleTiebreakStart(state: ParserState): void {\n // Check context: are we expecting a tiebreak?\n const currentSetFormat = getSetFormat(state.parsedFormat, state.setIndex);\n const isTiebreakOnly = isTiebreakOnlySet(currentSetFormat);\n\n if (isTiebreakOnly) {\n // Tiebreak-only set: bracket notation expected\n state.inTiebreak = true;\n state.state = TokenizerState.PARSING_TIEBREAK_SIDE1;\n state.position++;\n return;\n }\n\n // Regular set tiebreak\n if (state.currentSide1Buffer && state.currentSide2Buffer) {\n const side1 = Number.parseInt(state.currentSide1Buffer);\n const side2 = Number.parseInt(state.currentSide2Buffer);\n\n if (shouldEnterTiebreak(side1, side2, currentSetFormat)) {\n state.inTiebreak = true;\n state.state = TokenizerState.PARSING_TIEBREAK_SIDE1;\n state.position++;\n return;\n }\n }\n\n // Ambiguous: could be tiebreak notation\n state.warnings.push(`Tiebreak indicator at position ${state.position} may be ambiguous`);\n state.inTiebreak = true;\n state.state = TokenizerState.PARSING_TIEBREAK_SIDE1;\n state.position++;\n}\n\n/**\n * Handle end of tiebreak notation\n */\nfunction handleTiebreakEnd(state: ParserState): void {\n if (state.inTiebreak) {\n // Finalize tiebreak and set\n finalizeTiebreak(state);\n state.inTiebreak = false;\n state.position++;\n return;\n }\n\n // Unexpected closer\n state.warnings.push(`Unexpected tiebreak closer at position ${state.position}`);\n state.position++;\n}\n\n/**\n * Handle digit characters\n */\nfunction handleDigit(state: ParserState, char: string): void {\n const currentSetFormat = getSetFormat(state.parsedFormat, state.setIndex);\n\n if (state.inTiebreak) {\n handleTiebreakDigit(state, char, currentSetFormat);\n } else {\n handleGameDigit(state, char, currentSetFormat);\n }\n}\n\n/**\n * Check if adding the next digit would exceed the maximum game score\n */\nfunction wouldExceedMaxScore(buffer: string, nextDigit: string, maxGameScore: number): boolean {\n const twoDigitValue = Number.parseInt(buffer + nextDigit);\n return twoDigitValue > maxGameScore;\n}\n\n/**\n * Handle digit for side1 game score\n */\nfunction handleSide1GameDigit(state: ParserState, char: string, maxGameScore: number): void {\n state.state = TokenizerState.PARSING_SIDE1;\n state.currentSide1Buffer += char;\n\n const currentValue = Number.parseInt(state.currentSide1Buffer);\n const nextPos = state.position + 1;\n\n if (nextPos < state.input.length && isDigit(state.input[nextPos])) {\n const nextDigit = state.input[nextPos];\n\n if (wouldExceedMaxScore(state.currentSide1Buffer, nextDigit, maxGameScore)) {\n state.warnings.push(\n `Potential score issue: \"${state.currentSide1Buffer}${nextDigit}\" would exceed max game score ${maxGameScore}`,\n );\n state.state = TokenizerState.PARSING_SIDE2;\n }\n } else {\n state.state = TokenizerState.PARSING_SIDE2;\n }\n\n if (currentValue > maxGameScore) {\n state.warnings.push(`Score ${currentValue} exceeds max ${maxGameScore} at position ${state.position}`);\n }\n\n state.position++;\n}\n\n/**\n * Handle digit for side2 game score\n */\nfunction handleSide2GameDigit(state: ParserState, char: string, setFormat: any, maxGameScore: number): void {\n state.currentSide2Buffer += char;\n\n const side1 = Number.parseInt(state.currentSide1Buffer);\n const side2 = Number.parseInt(state.currentSide2Buffer);\n\n if (requiresTiebreak(side1, side2, setFormat)) {\n state.inTiebreak = true;\n state.state = TokenizerState.PARSING_TIEBREAK_SIDE1;\n state.position++;\n return;\n }\n\n const nextPos = state.position + 1;\n\n if (nextPos < state.input.length && isDigit(state.input[nextPos])) {\n const nextDigit = state.input[nextPos];\n\n if (wouldExceedMaxScore(state.currentSide2Buffer, nextDigit, maxGameScore)) {\n const isValid = isSetComplete(side1, side2, setFormat) || shouldEnterTiebreak(side1, side2, setFormat);\n if (!isValid) {\n state.warnings.push(`Set ${state.setIndex + 1} may be incomplete: ${side1}-${side2}`);\n }\n finalizeSet(state);\n }\n }\n\n state.position++;\n}\n\n/**\n * Handle digit in game score context\n */\nfunction handleGameDigit(state: ParserState, char: string, setFormat: any): void {\n const maxGameScore = getMaxGameScore(setFormat);\n\n if (state.state === TokenizerState.PARSING_SIDE1 || state.state === TokenizerState.START) {\n handleSide1GameDigit(state, char, maxGameScore);\n return;\n }\n\n if (state.state === TokenizerState.PARSING_SIDE2) {\n handleSide2GameDigit(state, char, setFormat, maxGameScore);\n return;\n }\n\n state.position++;\n}\n\n/**\n * Handle digit in tiebreak side1 context\n */\nfunction handleTiebreakSide1Digit(state: ParserState, char: string): void {\n state.currentTiebreakSide1Buffer += char;\n\n // Just keep buffering - separator will finalize\n // Don't try to be smart about when to stop\n state.position++;\n}\n\n/**\n * Handle digit in tiebreak side2 context\n */\nfunction handleTiebreakSide2Digit(state: ParserState, char: string, setFormat: any): void {\n state.currentTiebreakSide2Buffer += char;\n\n const currentValue = Number.parseInt(state.currentTiebreakSide2Buffer);\n const nextPos = state.position + 1;\n\n if (nextPos < state.input.length && isDigit(state.input[nextPos])) {\n const nextDigit = state.input[nextPos];\n const twoDigitValue = Number.parseInt(state.currentTiebreakSide2Buffer + nextDigit);\n\n // Check if we should finalize based on the same logic as side1\n // If two-digit would be unreasonable, current value is complete\n const side1 = Number.parseInt(state.currentTiebreakSide1Buffer);\n\n // Sanity check: absurdly large numbers indicate error or end of tiebreak\n const absurdMax = 999;\n const shouldFinalize = twoDigitValue > absurdMax;\n\n if (shouldFinalize) {\n const side2 = currentValue;\n const isComplete = isTiebreakComplete(side1, side2, setFormat);\n if (!isComplete) {\n state.warnings.push(`Tiebreak may be incomplete: ${side1}-${side2}`);\n }\n finalizeSet(state);\n }\n }\n\n state.position++;\n}\n\n/**\n * Handle digit in tiebreak score context\n */\nfunction handleTiebreakDigit(state: ParserState, char: string, setFormat: any): void {\n if (state.state === TokenizerState.PARSING_TIEBREAK_SIDE1) {\n handleTiebreakSide1Digit(state, char);\n return;\n }\n\n if (state.state === TokenizerState.PARSING_TIEBREAK_SIDE2) {\n handleTiebreakSide2Digit(state, char, setFormat);\n return;\n }\n\n state.position++;\n}\n\n/**\n * Create a tiebreak-only set (match tiebreak)\n */\nfunction createTiebreakOnlySet(state: ParserState): ParsedSet | null {\n if (!state.currentTiebreakSide1Buffer || !state.currentTiebreakSide2Buffer) {\n return null; // Incomplete tiebreak\n }\n\n const tb1 = Number.parseInt(state.currentTiebreakSide1Buffer);\n const tb2 = Number.parseInt(state.currentTiebreakSide2Buffer);\n\n return {\n side1Score: 0, // No game scores for tiebreak-only\n side2Score: 0,\n side1TiebreakScore: tb1,\n side2TiebreakScore: tb2,\n setNumber: state.setIndex + 1,\n winningSide: tb1 > tb2 ? 1 : 2,\n };\n}\n\n/**\n * Add tiebreak scores to a regular set\n */\nfunction addTiebreakScores(set: ParsedSet, state: ParserState, currentSetFormat: any): void {\n const tb1Str = state.currentTiebreakSide1Buffer;\n const tb2Str = state.currentTiebreakSide2Buffer;\n\n if (tb1Str && tb2Str) {\n // Both scores provided\n set.side1TiebreakScore = Number.parseInt(tb1Str);\n set.side2TiebreakScore = Number.parseInt(tb2Str);\n } else if (tb1Str || tb2Str) {\n // Only one score provided - infer the other\n inferMissingTiebreakScore(set, tb1Str, tb2Str, currentSetFormat);\n }\n}\n\n/**\n * Infer missing tiebreak score when only one is provided\n */\nfunction inferMissingTiebreakScore(set: ParsedSet, tb1Str: string, tb2Str: string, setFormat: any): void {\n const losingScore = Number.parseInt(tb1Str || tb2Str);\n const tiebreakLimit = getTiebreakLimit(setFormat);\n const isNoAd = setFormat?.tiebreakFormat?.NoAD;\n\n // Infer winning score (must be at least tiebreakTo and win by 2, unless NoAD)\n const minWinningScore = isNoAd ? tiebreakLimit : Math.max(tiebreakLimit, losingScore + 2);\n\n // Determine which side won based on game scores\n if (set.side1Score! > set.side2Score!) {\n // Side 1 won, so tb2Str is losing score\n set.side1TiebreakScore = minWinningScore;\n set.side2TiebreakScore = losingScore;\n } else {\n // Side 2 won, so tb1Str is losing score\n set.side1TiebreakScore = losingScore;\n set.side2TiebreakScore = minWinningScore;\n }\n}\n\n/**\n * Check if set scores require a tiebreak based on format rules\n */\nfunction requiresTiebreak(side1Score: number, side2Score: number, setFormat: any): boolean {\n const tiebreakAt = setFormat.tiebreakAt || setFormat.setTo || 6;\n\n // Check if both scores are at tiebreakAt and differ by exactly 1\n const atTiebreak =\n (side1Score === tiebreakAt && side2Score === tiebreakAt + 1) ||\n (side2Score === tiebreakAt && side1Score === tiebreakAt + 1);\n\n return atTiebreak;\n}\n\n/**\n * Create a regular set with game scores\n */\nfunction createRegularSet(state: ParserState, currentSetFormat: any): ParsedSet | null {\n if (!state.currentSide1Buffer || !state.currentSide2Buffer) {\n return null; // Incomplete set\n }\n\n const side1Score = Number.parseInt(state.currentSide1Buffer);\n const side2Score = Number.parseInt(state.currentSide2Buffer);\n\n // CRITICAL: Check if this set requires a tiebreak but doesn't have one yet\n if (\n requiresTiebreak(side1Score, side2Score, currentSetFormat) &&\n !state.currentTiebreakSide1Buffer &&\n !state.currentTiebreakSide2Buffer\n ) {\n // Set is incomplete - needs tiebreak score\n return null;\n }\n\n const set: ParsedSet = {\n side1Score,\n side2Score,\n setNumber: state.setIndex + 1,\n };\n\n // Add tiebreak scores if present\n if (state.currentTiebreakSide1Buffer || state.currentTiebreakSide2Buffer) {\n addTiebreakScores(set, state, currentSetFormat);\n }\n\n // Determine winner from game scores\n if (side1Score > side2Score) {\n set.winningSide = 1;\n } else if (side2Score > side1Score) {\n set.winningSide = 2;\n }\n\n return set;\n}\n\n/**\n * Reset state buffers and prepare for next set\n */\nfunction resetStateForNextSet(state: ParserState): void {\n state.currentSide1Buffer = '';\n state.currentSide2Buffer = '';\n state.currentTiebreakSide1Buffer = '';\n state.currentTiebreakSide2Buffer = '';\n state.setIndex++;\n state.inTiebreak = false; // Reset tiebreak flag\n state.state = TokenizerState.PARSING_SIDE1;\n\n // Check if next set is tiebreak-only\n const nextSetFormat = getSetFormat(state.parsedFormat, state.setIndex);\n if (isTiebreakOnlySet(nextSetFormat)) {\n state.expectingTiebreakOnly = true;\n state.inTiebreak = true;\n state.state = TokenizerState.PARSING_TIEBREAK_SIDE1;\n }\n}\n\n/**\n * Finalize current set and move to next\n */\nfunction finalizeSet(state: ParserState): void {\n const currentSetFormat = getSetFormat(state.parsedFormat, state.setIndex);\n const isTiebreakOnly = isTiebreakOnlySet(currentSetFormat);\n\n let set: ParsedSet | null = null;\n\n if (isTiebreakOnly) {\n set = createTiebreakOnlySet(state);\n } else {\n set = createRegularSet(state, currentSetFormat);\n }\n\n if (set) {\n state.sets.push(set);\n }\n\n resetStateForNextSet(state);\n}\n\n/**\n * Finalize tiebreak within current set\n */\nfunction finalizeTiebreak(state: ParserState): void {\n // Tiebreak scores are added when finalizing the set\n // Just transition back to set completion\n finalizeSet(state);\n}\n\n/**\n * Finalize any incomplete score at end of input\n */\nfunction finalizeCurrentScore(state: ParserState): void {\n if (\n state.currentSide1Buffer ||\n state.currentSide2Buffer ||\n state.currentTiebreakSide1Buffer ||\n state.currentTiebreakSide2Buffer\n ) {\n finalizeSet(state);\n }\n}\n\n/**\n * Check if match is complete\n */\nfunction checkMatchComplete(state: ParserState): boolean {\n const bestOf = state.parsedFormat.bestOf || 3;\n const setsNeeded = Math.ceil(bestOf / 2);\n\n const side1Wins = state.sets.filter((s) => s.winningSide === 1).length;\n const side2Wins = state.sets.filter((s) => s.winningSide === 2).length;\n\n return side1Wins >= setsNeeded || side2Wins >= setsNeeded;\n}\n\n/**\n * Format the parsed score for output\n */\nfunction formatScore(state: ParserState): string {\n const parts: string[] = [];\n\n for (const set of state.sets) {\n // Check if this is a tiebreak-only set (no game scores, only tiebreak scores)\n const isTiebreakOnly =\n (!set.side1Score || set.side1Score === 0) &&\n (!set.side2Score || set.side2Score === 0) &&\n (set.side1TiebreakScore !== undefined || set.side2TiebreakScore !== undefined);\n\n if (isTiebreakOnly) {\n // Match tiebreak: [10-8]\n const tb1 = set.side1TiebreakScore ?? 0;\n const tb2 = set.side2TiebreakScore ?? 0;\n parts.push(`[${tb1}-${tb2}]`);\n } else {\n // Regular set with optional tiebreak\n let setPart = `${set.side1Score || 0}-${set.side2Score || 0}`;\n\n // Add tiebreak notation if present\n if (set.side1TiebreakScore !== undefined || set.side2TiebreakScore !== undefined) {\n const tb1 = set.side1TiebreakScore ?? 0;\n const tb2 = set.side2TiebreakScore ?? 0;\n\n // Regular set tiebreak: show losing score in parentheses\n // Winner is determined by game scores, not tiebreak scores\n const losingTiebreakScore = set.winningSide === 1 ? tb2 : tb1;\n setPart += `(${losingTiebreakScore})`;\n }\n\n parts.push(setPart);\n }\n }\n\n return parts.join(' ');\n}\n\n/**\n * Get context string for error messages\n */\nfunction getContext(state: ParserState): string {\n const start = Math.max(0, state.position - 5);\n const end = Math.min(state.input.length, state.position + 5);\n const before = state.input.substring(start, state.position);\n const after = state.input.substring(state.position + 1, end);\n return `...${before}[${state.input[state.position]}]${after}...`;\n}\n","usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/utilities/safeJSON.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/utilities/unused/localeFx.js","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]},{"filePath":"/Users/charlesallen/Development/GitHub/CourtHive/TMX/src/vite-env.d.ts","messages":[],"suppressedMessages":[],"errorCount":0,"fatalErrorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":[]}]