Repository navigation
feat(web): every function in the new UI — team/player follows, seasons, data types, exports, restore; classic views removed [FX-14b] - #161
Merged
Conversation
… from /status; Health in quick search and Help [FX-14b] - The download path's coded log lines (FX-13, FX-19: sync_season_list ... sync_follow_listing, sync_extras, sync_follow_details) have texts in both locales; leagues, seasons and follows are shown by name, request outcomes and breaker reasons in words. A line with an unknown code shows the server text. - Job kinds by spec: Season list (sync only: seasons), Clean-up of old odds (clear scope history, the prune-history task, "Older than 90 days"), Deletion of league data (clear tournament_id), Restore (restore dry_run false). Targets of follows, event ids and league clears; Run again keeps them. - Job output: one league's deletion, the prune result, a real restore (index rebuilt, check after it). - The connection state comes from /status.connection (FX-19) instead of the per-tab memory of FX-14a; Health shows the last failed request and the server's last connection check. - Ctrl K "health"/"sağlık" opens Health; Help says where Health is (/health is the server's JSON).
…ownload; data types as checklists [FX-14b]
- MORE_FOLLOW_KINDS is on (FX-19). The editor searches teams and players by name
(POST /tournaments/search with kinds); a hit shows its kind, sport, country, a player's team and
"Already added", and brings its sport. A single match is added by number, or from the match page's
"Follow this match"; the match page also offers "Follow <team>". A team's or player's seasons are a
time window. Download now and "Start downloading right away" download the follow itself
(sync follows: [id]), for every kind.
- "Choose seasons" of a league without a season list starts the season-list job (sync only: seasons)
on a click, follows it, then shows the seasons by name; also on the edit page and the Seasons tab.
- A follow's page: per-season counts (include=counts) with the schedule's age; a team's stored matches
(participant); jobs by target (GET /jobs?target=kind:id) plus the downloads of every follow.
- Data step and edit page: "Use the defaults for <sport>" (slices null) or "Choose for this follow"
({include}), a grouped checklist from GET /sports/{slug} (match data, betting odds off by default and
marked so, season/team/player/sport data), the request cost per match.
- Settings › Data: defaults.slices and slices.<sport> {enable, disable} as checklists, locks respected.
- A follow of the old league list can be moved here (PATCH origin api); removing a league can delete its
stored matches too (DELETE ?delete_data=true, typed word, the clear job linked).
- A stored match without a league, or one not stored at all, can be fetched by its number (event_ids).
…leting one league's data [FX-14b] - New export: normalized data (matches, data types, score changes, betting odds, standings) as CSV, JSONL, Parquet or SQLite, next to the match table and the original data. Parquet is disabled with the reason when the server has no pyarrow (capabilities.parquet). Filters: sport, added leagues, season and match numbers, status classes (not for score changes) and a time range. - Exports list: the readable file names (FX-19) are shown; files written by `ssc export` (source "file") are listed with their download and no job link. DataTable rows may open nothing. - Restore: step 3 runs the restore as a job (dry_run false) after a typed word; 400 confirmation_required with details.occupied asks before it sends force; the job is followed and its result shown (index rebuilt, the check after the restore). The server command stays as the other way. - Data cleanup: a card to delete one league's data, or one season's (clear with tournament_id and season_id), with the typed word.
Every function of the classic views has a home in the new screens now (the map is in the PR): src/views/**, src/classic/**, the /classic routes and the "Classic interface" menu entry are removed, with the modules only they used (src/components, src/stores, src/api/client.ts, lib/appVersion, format, jobLabel, latest, sport, tabs, toast, upstream), their tests, and the classic texts of locales/en.ts and tr.ts (the sport names stay). The /classic/... addresses lead to their new screens; the old 2.x redirects stay. Python tests that pinned a removed file, updated minimally: - tests/test_throttle.py::test_settings_page_knows_the_same_default reads SAFE_RATE of screens/settings/settingsMeta.ts (was DEFAULT_RATE_LIMIT of views/SettingsView.vue); - tests/test_sports_registry.py::test_frontend_sport_list_matches_the_registry checks that the UI reads the sport list from the registry (app/sports.ts, v1.sports()) instead of the removed hard-coded list.
POST /jobs takes only: "seasons"; the recorded spec that GET /jobs returns is the service's SyncSpec, mode: "seasons". Both now name the job "Season list", leave it out of the download toast and out of a follow's last download, and Run again keeps it. Found with the offline screenshots.
The row-actions cell of a table is nowrap and right-aligned, and a dialog of a row (Remove) inherited both: a longer text ran out of the dialog. The overlay resets them. Found with the offline screenshots.
This was referenced Oct 6, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Item: FX-14b (frontend; after FX-13 #152, FX-14a #154, FX-15 #155, FX-19 #156). It wires the API features that now exist into the new web UI and removes the classic views. Frontend only (
frontend/**), plus two minimal Python test updates (below). Do not merge: the owner reviews it.Behaviour change. Team, player and single-match follows can be added (by name or number) and are downloaded; seasons can be chosen before a league's first download; the data types are checklists (per follow, globally and per sport); exports offer normalized data as CSV, JSONL, Parquet and SQLite; a backup can be restored from the UI; one league's data can be deleted; the classic interface (
/classic) is gone and its addresses lead to the new screens. "Download now" of a league follow now downloads it with its own season choice (follows: [id]) instead of every season (league_id).Branch
feat/fx-14bonorigin/mainat602f326. Six commits, each green (frontend lint,vue-tsc -b, vitest):feat(web): coded job log lines and job kinds in words; the connection from/status; Health in quick search and Helpfeat(web): team, player and match follows; seasons before the first download; data types as checklistsfeat(web): normalized, Parquet and SQLite exports; a real restore; deleting one league's datarefactor(web): remove the classic viewsfix(web): a season list as the job records it (mode: seasons)fix(web): a dialog opened from a table row wraps its text(5 and 6 were found with the offline screenshots.)
What changed, per point of the brief
MORE_FOLLOW_KINDSistrue.POST /tournaments/search {q, sport, kinds: [kind]}, one request per search; tournaments keep/search/unique-tournaments, team and player use/search/all). A hit shows its kind, sport, country, a player's team ("takımı: Arsenal") and "Zaten eklendi"; picking it brings its sport along (FX-19 Not done: "the search hit's sport should be sent"). A single unfollowed hit is still pre-selected.?kind=&id=&name=&sport=). The 404 page of a match offers the follow too, and Bu maçı çek (fetchwithevent_ids, G16).seasonsis shown as the FX-19 time window ("Son 12 ay", "Son 2 yıl", "Listelenen kadar geriye"), with a note on what is read; a single match has no seasons step.sync {follows: [id]}for every kind./events?participant=); a match links to itself; every follow's İşler tab readsGET /jobs?target=<kind>:<id>(FX-13/FX-19) merged with the downloads of every follow, shown as "İndirme · Arsenal".POST /jobs sync {league_id, only: "seasons"}), follows the job (waitForJob), then shows the season names as checkboxes; a failed list links its job. The same on the edit page and as the Seasons tab's empty state. The Seasons tab shows per-season counts (include=counts): "4 maç · 3 bitti · 3 ayrıntılı", a completion bar and "Fikstür okundu: 5 dk. önce" / "Fikstür henüz okunmadı".slices: null) or "Bu takip için seç" ({include: [...]}, P27), a grouped checklist fromGET /sports/{slug}: Maç verisi (the match itself always), Bahis oranları (header badge "varsayılan olarak kapalı" / "açık"), Sezon / Takım / Oyuncu / Spor verisi ("sezon başına bir kez çekilir"); "bu sporda her zaman bulunmaz" for optional match data; the request cost per match follows the ticks. Choosing starts from the defaults' ticks (selected). A follow that cannot hold a selection (old league list) says why. Settings › Veri: İndirilecek veriler with the globaldefaults.slicesas a checklist (unset = the registry's own ticks, "yerleşik"; a fully ticked group is written by its name, e.g.["core", "odds_featured"]) and Spora göre değişiklikler, one collapsible checklist per sport writingslices.<sport> {enable, disable}; locked values show where they are set and cannot be ticked; Undo / reset as for other settings; staged and saved with "Değişiklikleri kaydet"./status.capabilities.parquetis false. Filters: sport, added leagues, season and match numbers, status classes (not for score changes), a date range (for score changes "Kaydı şu tarihten itibaren"). The list shows the readable file names (FX-19) and the filesssc exportwrote (source: "file", badge "ssc export ile", download, no job link;DataTablerows may now open nothing).restore {name, force, dry_run: false}), a 400confirmation_requiredshowsdetails.occupiedand asks again ("Değiştir ve geri yükle") before it sendsforce; progress and the result (index rebuilt, "Geri yüklemeden sonraki denetim: Sorun yok" or the number of problems) come from the job. The server command stays under "Ya da sunucuda çalıştırın". The status is read again afterwards (the job history is the backup's).DELETE /follows/{id}?delete_data=true; the toast links the clear job, which is followed). Veri bakımı has a new card Bir ligin verisini sil (league, optionally one season;clear {scope: all, tournament_id, season_id}). A follow of the old league list shows Buraya taşı (PATCH {origin: "api"}) on its page, its edit page and in its row menu; afterwards every field is editable./status.connection(never_tried→ "Henüz denenmedi",failed→ "Son istek başarısız", a failedlast_checkwith no success since → "Son denetim başarısız"); FX-14a's per-tab memory is gone (a check's answer updates the store at once). Health adds "Son başarısız istek" (reason in words, time) and "Son bağlantı denetimi". Servers withoutconnectionstill fall back to the bridge times.sync_season_list,sync_season_list_failed,sync_schedule,sync_schedule_failed,sync_season_outdated,sync_follow_skipped,sync_refreshing,sync_details,sync_details_checking,sync_details_selected,sync_breaker_stopped,sync_extras,sync_follow_listing,sync_follow_listing_failed,sync_follow_details); leagues, seasons and follows by name, request outcomes and the breaker's "what" in words. Unknown codes show the server text. Job kinds by spec: "Sezon listesi", "Eski oranların temizliği" (the prune-historyclear {scope: history, older_than}, target "90 gün öncesine ait olanlar", output "Silinen anlık görüntü"), "Lig verisinin silinmesi", "Geri yükleme" (a real restore; the dry run stays "Yedek kontrolü"). Run again keeps follows, the season-list choice and event ids./healthin the address bar is the server's JSON check and the page is/system/health, with a link. The server serves the SPA for unknown paths; no server route changed.src/views/**,src/classic/**, the/classicroutes, the "Klasik arayüz" menu entry), with the modules only they used (src/components/**,src/stores/**,src/api/client.ts,lib/appVersion, format, jobLabel, latest, sport, tabs, toast, upstream), their 18 test files, and the classic texts oflocales/en.ts/tr.ts(the sport names stay)./classic/...addresses redirect to their new screens; the 2.x redirects stay. The Python side is untouched (the legacy/apiroutes stay until P30).Classic function → new screen
has=missing), a follow's Maçlar tabevent_ids); 404 "Bu maçı çek"The 12 newcomer tasks again (on this branch, offline, same method)
ssc watchon the server; Help explains it since FX-14a)Tests
npm run lint,vue-tsc -b,npm run build,npm audit --audit-level=high(0 vulnerabilities) andnpm run gen:api(no diff) are green. The axe checks run in the new tests (editor steps, checklists, settings data, exports dialog and list, restore dialog, remove dialog, Data cleanup, Health, job output).tests/jobWords.test.ts(every code in both languages, no English left in Turkish, names instead of ids, unknown codes as server text, the Job detail log in Turkish, kinds/targets by spec, prune labels, Run again, job outputs, Ctrl K and Help → Health, Health from/status.connection);tests/dataTypes.test.ts(selection resolution, the editor's defaults/choose, edit page, locked follow, Settings › Data global, per sport, locked);tests/followKinds.test.ts(team and player search, single match, season list on a click and its failure, per-season counts, team page, jobs by target, remove with data, typed word, "Buraya taşı", match page follow links and fetch by number, dialog wrapping);tests/exportsRestore.test.ts(normalized SQLite with filters, Parquet with pyarrow and the changes dataset, file list withssc exportfiles, restore run,confirmation_required→ force, refused restore, league/season clear).newcomer.test.ts:connectionStatetakes the status (bridge+connection) and the check is kept by the server (point 7), so its two tests were rewritten for the server-kept state plus a pre-FX-19 fallback; the Health check test now gets the server's state from/status; the palette's SofaScore search body haskinds; the placeholder key is per kind;MORE_FOLLOW_KINDSistrue; a team follow's "Download now" works (was "coming soon"); a follow page reads its jobs twice (by target and the downloads of every follow).dataScreens.test.ts: Download now sendsfollows: ['tournament:17']; the search body haskinds; a hit's radio value iskind:id; the "coming soon" note of the data step is gone; the follow body hasslices: null; the sync after saving usesfollows; "no hits" is per kind; the "coming soon" kinds test became "every kind can be chosen; a team by its number; 409"; the follow-detail jobs test answers?target=separately; a missing match offers "Fetch this match" and "Follow this match".operationsScreens.test.ts: normalized exports are enabled (Parquet's reason still shown); the restore's last step runs here (the command stays).shell.test.ts: the ⋯ menu has no "Classic interface";/classic/...addresses redirect.a11y, activityView, addLeagueDialog, auth, bridge, client, downloadSeasons, errors, jobCard, jobGuards, latest, matchDetail, matchesView, scrape, settingsConnection, settingsRateLimit, sport, version(.test.ts). Their new-UI counterparts are covered byshell,settingsScreen,healthOverview,dataScreens,jobsScreen,jobDetailand the new files.tests/test_throttle.py::test_settings_page_knows_the_same_defaultreadsSAFE_RATEofscreens/settings/settingsMeta.ts(wasDEFAULT_RATE_LIMITofviews/SettingsView.vue);tests/test_sports_registry.py::test_frontend_sport_list_matches_the_registrynow checks that the UI reads the sport list from the API (app/sports.ts,v1.sports()) and that the hard-coded list is gone. Full suite withSTORE_SHADOW_CHECK=1, home-disk TMPDIR and basetemp, foreground, frontend built in the worktree: 9735 passed, 33 skipped, 20 deselected (same as FX-19 on main).Screenshots (offline)
The review's method:
ssc serveon 127.0.0.1 from this worktree (shared.venvread-only) with the CLI goldens' fake transport (tests/characterization/cli_env/sitecustomize.py), proxies at a dead local port, a copy ofworld.jsonwith search hits (Premier League, Trendyol Süper Lig with its season list, Arsenal and Bukayo Saka via/search/all, Arsenal's match list) set up under~/.cache/fx14b; headless Chromium (the shared venv's playwright, read-only) aborted every request not to 127.0.0.1: 0 aborted requests, console errors only the expected 404s (a match that is not stored, no season list yet). States: A = empty install, B = Premier League (api) and Arsenal (team) followed and downloaded, a legacyleagues.txtrow (Süper Lig), a backup, a normalized export and onessc export-style file; R = a real restore of the backup. Folder~/.local/share/sofascore-orch/fx14b-shots/:before/46 shots (origin/main 602f326),after/60 shots (-tr-1440,-en-390).Before → after, the main ones:
A-editor-step1-team(Takım disabled "yakında" → Arsenal found by name),A-editor-step2-choose/-seasons("henüz indirilmedi" → the button, then three named seasons),A-editor-step3(one line → defaults/choose with the checklist;after-A-editor-step3-odds-on),A-settings-data(free textcore→ checklists),A-export-dialog(normalized disabled → datasets and four formats),B-follow-seasons(bare list → counts),B-follow-jobs,B-remove-dialog(+ delete data),B-maintenance(+ league card),B-event-menu(copy only → follow match/teams),B-job-log(English lines → Turkish),B-exports(job ids → readable names,ssc exportfile),B-restore-last-step(command only → typed word and run;after-R-restore-*),B-classic(classic page → redirected to Ligler ve takipler); new:after-A-editor-step1-player,after-A-palette-health,after-A-event-not-stored,after-B-team-follow,after-B-legacy-edit.API gaps found (frontend stops at the UI; no Python changed)
sync {follows: ["team:42"]}says "Takım research: SofaScore push channel — coverage, lag, cost, direct connection (Talimat 06+07) #42" unless the follows were read in this tab (Ligler ve takipler, the follow's page, Ctrl K); the job record carries no follow name (onlyprogress.league_namewhile listing). Atarget_nameonJob(or the follow name in the recorded spec) would let every screen name it without an extra request.POST /jobstakesonly: "seasons",GET /jobsreturnsmode: "seasons"(the serviceSyncSpec); a fetch byevent_idsis recorded asmode: "details"with per-tournament selections plusevent_ids. The UI reads both forms; a documented, stable job-spec shape would help./status.connectionis per process (FX-19 Not done): requests ofssccommands andssc watchdo not count.ssc watchskips player follows (FX-19 Not done); the editor's "Canlı izlemeye dahil et" is still offered for players.Design mismatches
05-web-ui.mdlines 8, 41, 138–145, 261, 299–301, 1272–1276, 1286, 1387: the classic views,/classicandfrontend/src/api/client.tsare gone;/classic/...redirects to the new screens.sync {follows: [id]}for every kind (with the follow's season choice), notleague_id.kinds), one kind per search; Single match by number or from the match page. Step 2 for team/player: the FX-19 time window words. The data step: the radios came back (FX-14a had removed them) with the grouped checklist; groups are by section (match, odds, season, team, player, sport) rather than one group pergroupvalue;requireddoes not lock a box (P27 owner decision), it only marks optional match data.targetplus the downloads of every follow; team follows get a Matches tab by participant.event_ids; the 404 page offers "Fetch this match".ssc exportare listed (section 17 of the plan still says they are not).restore; 400confirmation_requiredasks again beforeforce. Maintenance gains a fourth card (one league's or one season's data)./status.connection(FX-19), not from the browser.defaults.slicesandslices.<sport>are checklists (G-wise as designed); unset defaults are shown as the registry's ticks ("built-in"), and a fully ticked group is written by its group name./healthis the server's JSON); job kind names by spec (season list, odds clean-up, league deletion, restore).UiDialog: a dialog inside a table row inheritedwhite-space: nowrapand right alignment (since FE-2b); fixed in the overlay.Changelog entry
Added
ssc exportare listed.Changed
/healthis the server's own check.Removed
/classic); its addresses lead to the new screens. (FX-14b)Files outside ownership
tests/test_throttle.py,tests/test_sports_registry.py(one test each, see Tests).Not done
after/.src/style.cssstill carries the classic stylesheet's shared names; only its comment changed. Pruning it is a separate, visual-diff item.