From e2863c1f6cce7372a939880dac78261359644244 Mon Sep 17 00:00:00 2001 From: yohlo Date: Tue, 25 Aug 2026 21:33:25 -0700 Subject: [PATCH 01/33] feat(telemetry): collections, universal server-fn coverage, beacon, client tracker - activities indexes + client_events/client_errors/rollups/alerts collections - global function middleware via createStart; reads skipped on success - actor stash, denial dedup, control-flow error guards - api route logging, auth event overrides, beacon endpoint, web vitals - centralized admin role checks, shared skeletons, award-badges fix - extracted deriveRunState with tests --- bun.lock | 3 + package.json | 1 + .../1787961600_activities_indexes.js | 23 ++ .../1787961700_created_client_events.js | 167 +++++++++ .../1787961800_created_client_errors.js | 207 +++++++++++ ...00_created_telemetry_rollups_and_alerts.js | 240 +++++++++++++ src/app/routeTree.gen.ts | 24 +- src/app/routes/__root.tsx | 10 +- src/app/routes/_authed/admin.tsx | 3 +- src/app/routes/_authed/admin/activities.tsx | 31 +- .../admin/tournaments/$id/assign-partners.tsx | 6 +- .../_authed/admin/tournaments/$id/index.tsx | 22 +- .../_authed/admin/tournaments/$id/teams.tsx | 19 +- .../_authed/admin/tournaments/index.tsx | 24 +- .../_authed/admin/tournaments/run.$id.tsx | 69 +--- .../_authed/tournaments/$tournamentId.tsx | 3 +- src/app/routes/api/auth.$.ts | 2 - src/app/routes/api/push/subscribe.ts | 3 +- src/app/routes/api/push/test.ts | 3 +- src/app/routes/api/push/unsubscribe.ts | 3 +- src/app/routes/api/teams/upload-logo.ts | 3 +- src/app/routes/api/telemetry/beacon.ts | 128 +++++++ src/app/routes/api/tournaments/upload-logo.ts | 3 +- src/app/start.ts | 8 + src/components/DefaultCatchBoundary.tsx | 2 + src/components/ios-install-prompt.tsx | 7 +- src/components/skeletons/card-skeleton.tsx | 35 ++ src/components/skeletons/index.ts | 3 + src/components/skeletons/list-skeleton.tsx | 76 ++++ .../skeletons/stat-tile-skeleton.tsx | 42 +++ src/contexts/spotify-context.tsx | 5 +- .../admin/components/award-badges.tsx | 44 +-- .../badges/hooks/use-award-manual-badge.ts | 21 ++ src/features/badges/server.ts | 5 +- src/features/core/hooks/use-is-admin.ts | 7 + src/features/core/hooks/use-links.ts | 3 +- src/features/core/utils/roles.ts | 4 + src/features/matches/server.ts | 15 +- src/features/players/server.ts | 7 +- src/features/predictions/server.ts | 3 +- src/features/settings/server.ts | 11 +- .../components/spotify-controls-bar.tsx | 5 +- .../spotify/components/spotify-sheet.tsx | 5 +- .../components/team-form/players-picker.tsx | 5 +- src/features/teams/server.ts | 5 +- .../components/started-tournament/index.tsx | 5 +- .../components/tournament-card-list.tsx | 6 +- .../components/upcoming-tournament/index.tsx | 5 +- .../hooks/use-tournament-run-state.test.ts | 159 +++++++++ .../hooks/use-tournament-run-state.ts | 68 ++++ src/features/tournaments/server.ts | 25 +- src/lib/pocketbase/client.ts | 5 +- src/lib/pocketbase/services/telemetry.ts | 325 ++++++++++++++++++ src/lib/pocketbase/util/transform-types.ts | 36 ++ .../recipes/passwordless-development-mode.ts | 4 + .../recipes/passwordless-twilio-verify.ts | 7 +- src/lib/supertokens/server.ts | 5 + src/lib/telemetry/api-logging.server.ts | 43 +++ src/lib/telemetry/auth-events.server.ts | 125 +++++++ src/lib/telemetry/beacon-schema.test.ts | 72 ++++ src/lib/telemetry/beacon-schema.ts | 48 +++ src/lib/telemetry/client/index.ts | 185 ++++++++++ src/lib/telemetry/error-hash.test.ts | 55 +++ src/lib/telemetry/error-hash.ts | 55 +++ src/lib/telemetry/redact-args.ts | 14 + src/lib/telemetry/request-context.server.ts | 18 + src/utils/activities.test.ts | 126 +++++++ src/utils/activities.ts | 87 +++-- src/utils/supertokens-core.server.ts | 2 + src/utils/supertokens.ts | 25 +- 70 files changed, 2545 insertions(+), 275 deletions(-) create mode 100644 pb_migrations/1787961600_activities_indexes.js create mode 100644 pb_migrations/1787961700_created_client_events.js create mode 100644 pb_migrations/1787961800_created_client_errors.js create mode 100644 pb_migrations/1787961900_created_telemetry_rollups_and_alerts.js create mode 100644 src/app/routes/api/telemetry/beacon.ts create mode 100644 src/app/start.ts create mode 100644 src/components/skeletons/card-skeleton.tsx create mode 100644 src/components/skeletons/index.ts create mode 100644 src/components/skeletons/list-skeleton.tsx create mode 100644 src/components/skeletons/stat-tile-skeleton.tsx create mode 100644 src/features/badges/hooks/use-award-manual-badge.ts create mode 100644 src/features/core/hooks/use-is-admin.ts create mode 100644 src/features/core/utils/roles.ts create mode 100644 src/features/tournaments/hooks/use-tournament-run-state.test.ts create mode 100644 src/features/tournaments/hooks/use-tournament-run-state.ts create mode 100644 src/lib/pocketbase/services/telemetry.ts create mode 100644 src/lib/telemetry/api-logging.server.ts create mode 100644 src/lib/telemetry/auth-events.server.ts create mode 100644 src/lib/telemetry/beacon-schema.test.ts create mode 100644 src/lib/telemetry/beacon-schema.ts create mode 100644 src/lib/telemetry/client/index.ts create mode 100644 src/lib/telemetry/error-hash.test.ts create mode 100644 src/lib/telemetry/error-hash.ts create mode 100644 src/lib/telemetry/redact-args.ts create mode 100644 src/lib/telemetry/request-context.server.ts diff --git a/bun.lock b/bun.lock index 50968ac..a5f208e 100644 --- a/bun.lock +++ b/bun.lock @@ -41,6 +41,7 @@ "twilio": "^5.8.0", "vaul": "^1.1.2", "web-push": "^3.6.7", + "web-vitals": "^6.2.0", "zod": "^4.0.15", }, "devDependencies": { @@ -1590,6 +1591,8 @@ "web-push": ["web-push@3.6.7", "", { "dependencies": { "asn1.js": "^5.3.0", "http_ece": "1.2.0", "https-proxy-agent": "^7.0.0", "jws": "^4.0.0", "minimist": "^1.2.5" }, "bin": { "web-push": "src/cli.js" } }, "sha512-OpiIUe8cuGjrj3mMBFWY+e4MMIkW3SVT+7vEIjvD9kejGUypv8GPDf84JdPWskK8zMRIJ6xYGm+Kxr8YkPyA0A=="], + "web-vitals": ["web-vitals@6.2.0", "", {}, "sha512-i2jhLkq6eB38jgJX5OIjEkyzQKgsG7FR56v7GphJNjzKAySgbRbwAfsFC0dDfHROWmCycf912SJpU2QoYIcMVg=="], + "webidl-conversions": ["webidl-conversions@4.0.2", "", {}, "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg=="], "webpack-virtual-modules": ["webpack-virtual-modules@0.6.2", "", {}, "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ=="], diff --git a/package.json b/package.json index c14bbab..c7cb483 100644 --- a/package.json +++ b/package.json @@ -53,6 +53,7 @@ "twilio": "^5.8.0", "vaul": "^1.1.2", "web-push": "^3.6.7", + "web-vitals": "^6.2.0", "zod": "^4.0.15" }, "devDependencies": { diff --git a/pb_migrations/1787961600_activities_indexes.js b/pb_migrations/1787961600_activities_indexes.js new file mode 100644 index 0000000..bcefa6b --- /dev/null +++ b/pb_migrations/1787961600_activities_indexes.js @@ -0,0 +1,23 @@ +/// +migrate((app) => { + const collection = app.findCollectionByNameOrId("pbc_1262591861") + + unmarshal({ + "indexes": [ + "CREATE INDEX `idx_activities_created` ON `activities` (`created`)", + "CREATE INDEX `idx_activities_name_created` ON `activities` (`name`, `created`)", + "CREATE INDEX `idx_activities_player_created` ON `activities` (`player`, `created`)", + "CREATE INDEX `idx_activities_success_created` ON `activities` (`success`, `created`)" + ] + }, collection) + + return app.save(collection) +}, (app) => { + const collection = app.findCollectionByNameOrId("pbc_1262591861") + + unmarshal({ + "indexes": [] + }, collection) + + return app.save(collection) +}) diff --git a/pb_migrations/1787961700_created_client_events.js b/pb_migrations/1787961700_created_client_events.js new file mode 100644 index 0000000..972f8d0 --- /dev/null +++ b/pb_migrations/1787961700_created_client_events.js @@ -0,0 +1,167 @@ +/// +migrate((app) => { + const collection = new Collection({ + "createRule": null, + "deleteRule": null, + "fields": [ + { + "autogeneratePattern": "[a-z0-9]{15}", + "hidden": false, + "id": "text3208210256", + "max": 15, + "min": 15, + "name": "id", + "pattern": "^[a-z0-9]+$", + "presentable": false, + "primaryKey": true, + "required": true, + "system": true, + "type": "text" + }, + { + "autogeneratePattern": "", + "hidden": false, + "id": "text_ce_name", + "max": 0, + "min": 0, + "name": "name", + "pattern": "", + "presentable": false, + "primaryKey": false, + "required": true, + "system": false, + "type": "text" + }, + { + "cascadeDelete": false, + "collectionId": "pbc_3072146508", + "hidden": false, + "id": "relation_ce_player", + "maxSelect": 1, + "minSelect": 0, + "name": "player", + "presentable": false, + "required": false, + "system": false, + "type": "relation" + }, + { + "autogeneratePattern": "", + "hidden": false, + "id": "text_ce_session_id", + "max": 0, + "min": 0, + "name": "session_id", + "pattern": "", + "presentable": false, + "primaryKey": false, + "required": false, + "system": false, + "type": "text" + }, + { + "autogeneratePattern": "", + "hidden": false, + "id": "text_ce_path", + "max": 0, + "min": 0, + "name": "path", + "pattern": "", + "presentable": false, + "primaryKey": false, + "required": false, + "system": false, + "type": "text" + }, + { + "autogeneratePattern": "", + "hidden": false, + "id": "text_ce_route_id", + "max": 0, + "min": 0, + "name": "route_id", + "pattern": "", + "presentable": false, + "primaryKey": false, + "required": false, + "system": false, + "type": "text" + }, + { + "hidden": false, + "id": "number_ce_value", + "max": null, + "min": null, + "name": "value", + "onlyInt": false, + "presentable": false, + "required": false, + "system": false, + "type": "number" + }, + { + "hidden": false, + "id": "json_ce_props", + "maxSize": 0, + "name": "props", + "presentable": false, + "required": false, + "system": false, + "type": "json" + }, + { + "autogeneratePattern": "", + "hidden": false, + "id": "text_ce_user_agent", + "max": 0, + "min": 0, + "name": "user_agent", + "pattern": "", + "presentable": false, + "primaryKey": false, + "required": false, + "system": false, + "type": "text" + }, + { + "hidden": false, + "id": "autodate2990389176", + "name": "created", + "onCreate": true, + "onUpdate": false, + "presentable": false, + "system": false, + "type": "autodate" + }, + { + "hidden": false, + "id": "autodate3332085495", + "name": "updated", + "onCreate": true, + "onUpdate": true, + "presentable": false, + "system": false, + "type": "autodate" + } + ], + "id": "pbc_client_events", + "indexes": [ + "CREATE INDEX `idx_client_events_created` ON `client_events` (`created`)", + "CREATE INDEX `idx_client_events_name_created` ON `client_events` (`name`, `created`)", + "CREATE INDEX `idx_client_events_player_created` ON `client_events` (`player`, `created`)", + "CREATE INDEX `idx_client_events_session_id` ON `client_events` (`session_id`)" + ], + "listRule": null, + "name": "client_events", + "system": false, + "type": "base", + "updateRule": null, + "viewRule": null + }); + + return app.save(collection); +}, (app) => { + const collection = app.findCollectionByNameOrId("pbc_client_events"); + + return app.delete(collection); +}) diff --git a/pb_migrations/1787961800_created_client_errors.js b/pb_migrations/1787961800_created_client_errors.js new file mode 100644 index 0000000..2600f24 --- /dev/null +++ b/pb_migrations/1787961800_created_client_errors.js @@ -0,0 +1,207 @@ +/// +migrate((app) => { + const collection = new Collection({ + "createRule": null, + "deleteRule": null, + "fields": [ + { + "autogeneratePattern": "[a-z0-9]{15}", + "hidden": false, + "id": "text3208210256", + "max": 15, + "min": 15, + "name": "id", + "pattern": "^[a-z0-9]+$", + "presentable": false, + "primaryKey": true, + "required": true, + "system": true, + "type": "text" + }, + { + "autogeneratePattern": "", + "hidden": false, + "id": "text_cerr_message", + "max": 0, + "min": 0, + "name": "message", + "pattern": "", + "presentable": false, + "primaryKey": false, + "required": true, + "system": false, + "type": "text" + }, + { + "autogeneratePattern": "", + "hidden": false, + "id": "text_cerr_stack", + "max": 0, + "min": 0, + "name": "stack", + "pattern": "", + "presentable": false, + "primaryKey": false, + "required": false, + "system": false, + "type": "text" + }, + { + "hidden": false, + "id": "select_cerr_source", + "maxSelect": 1, + "name": "source", + "presentable": false, + "required": false, + "system": false, + "type": "select", + "values": [ + "window", + "unhandledrejection", + "error-boundary", + "sw" + ] + }, + { + "autogeneratePattern": "", + "hidden": false, + "id": "text_cerr_path", + "max": 0, + "min": 0, + "name": "path", + "pattern": "", + "presentable": false, + "primaryKey": false, + "required": false, + "system": false, + "type": "text" + }, + { + "autogeneratePattern": "", + "hidden": false, + "id": "text_cerr_route_id", + "max": 0, + "min": 0, + "name": "route_id", + "pattern": "", + "presentable": false, + "primaryKey": false, + "required": false, + "system": false, + "type": "text" + }, + { + "autogeneratePattern": "", + "hidden": false, + "id": "text_cerr_group_hash", + "max": 0, + "min": 0, + "name": "group_hash", + "pattern": "", + "presentable": false, + "primaryKey": false, + "required": true, + "system": false, + "type": "text" + }, + { + "hidden": false, + "id": "bool_cerr_resolved", + "name": "resolved", + "presentable": false, + "required": false, + "system": false, + "type": "bool" + }, + { + "cascadeDelete": false, + "collectionId": "pbc_3072146508", + "hidden": false, + "id": "relation_cerr_player", + "maxSelect": 1, + "minSelect": 0, + "name": "player", + "presentable": false, + "required": false, + "system": false, + "type": "relation" + }, + { + "autogeneratePattern": "", + "hidden": false, + "id": "text_cerr_session_id", + "max": 0, + "min": 0, + "name": "session_id", + "pattern": "", + "presentable": false, + "primaryKey": false, + "required": false, + "system": false, + "type": "text" + }, + { + "autogeneratePattern": "", + "hidden": false, + "id": "text_cerr_user_agent", + "max": 0, + "min": 0, + "name": "user_agent", + "pattern": "", + "presentable": false, + "primaryKey": false, + "required": false, + "system": false, + "type": "text" + }, + { + "hidden": false, + "id": "json_cerr_props", + "maxSize": 0, + "name": "props", + "presentable": false, + "required": false, + "system": false, + "type": "json" + }, + { + "hidden": false, + "id": "autodate2990389176", + "name": "created", + "onCreate": true, + "onUpdate": false, + "presentable": false, + "system": false, + "type": "autodate" + }, + { + "hidden": false, + "id": "autodate3332085495", + "name": "updated", + "onCreate": true, + "onUpdate": true, + "presentable": false, + "system": false, + "type": "autodate" + } + ], + "id": "pbc_client_errors", + "indexes": [ + "CREATE INDEX `idx_client_errors_group_created` ON `client_errors` (`group_hash`, `created`)", + "CREATE INDEX `idx_client_errors_created` ON `client_errors` (`created`)", + "CREATE INDEX `idx_client_errors_resolved_created` ON `client_errors` (`resolved`, `created`)" + ], + "listRule": null, + "name": "client_errors", + "system": false, + "type": "base", + "updateRule": null, + "viewRule": null + }); + + return app.save(collection); +}, (app) => { + const collection = app.findCollectionByNameOrId("pbc_client_errors"); + + return app.delete(collection); +}) diff --git a/pb_migrations/1787961900_created_telemetry_rollups_and_alerts.js b/pb_migrations/1787961900_created_telemetry_rollups_and_alerts.js new file mode 100644 index 0000000..2e699f7 --- /dev/null +++ b/pb_migrations/1787961900_created_telemetry_rollups_and_alerts.js @@ -0,0 +1,240 @@ +/// +migrate((app) => { + const rollups = new Collection({ + "createRule": null, + "deleteRule": null, + "fields": [ + { + "autogeneratePattern": "[a-z0-9]{15}", + "hidden": false, + "id": "text3208210256", + "max": 15, + "min": 15, + "name": "id", + "pattern": "^[a-z0-9]+$", + "presentable": false, + "primaryKey": true, + "required": true, + "system": true, + "type": "text" + }, + { + "autogeneratePattern": "", + "hidden": false, + "id": "text_tr_date", + "max": 0, + "min": 0, + "name": "date", + "pattern": "^\\d{4}-\\d{2}-\\d{2}$", + "presentable": false, + "primaryKey": false, + "required": true, + "system": false, + "type": "text" + }, + { + "autogeneratePattern": "", + "hidden": false, + "id": "text_tr_metric", + "max": 0, + "min": 0, + "name": "metric", + "pattern": "", + "presentable": false, + "primaryKey": false, + "required": true, + "system": false, + "type": "text" + }, + { + "autogeneratePattern": "", + "hidden": false, + "id": "text_tr_dim", + "max": 0, + "min": 0, + "name": "dim", + "pattern": "", + "presentable": false, + "primaryKey": false, + "required": false, + "system": false, + "type": "text" + }, + { + "hidden": false, + "id": "number_tr_value", + "max": null, + "min": null, + "name": "value", + "onlyInt": false, + "presentable": false, + "required": false, + "system": false, + "type": "number" + }, + { + "hidden": false, + "id": "json_tr_meta", + "maxSize": 0, + "name": "meta", + "presentable": false, + "required": false, + "system": false, + "type": "json" + }, + { + "hidden": false, + "id": "autodate2990389176", + "name": "created", + "onCreate": true, + "onUpdate": false, + "presentable": false, + "system": false, + "type": "autodate" + }, + { + "hidden": false, + "id": "autodate3332085495", + "name": "updated", + "onCreate": true, + "onUpdate": true, + "presentable": false, + "system": false, + "type": "autodate" + } + ], + "id": "pbc_telemetry_rollups", + "indexes": [ + "CREATE UNIQUE INDEX `idx_rollups_date_metric_dim` ON `telemetry_rollups` (`date`, `metric`, `dim`)", + "CREATE INDEX `idx_rollups_metric_date` ON `telemetry_rollups` (`metric`, `date`)" + ], + "listRule": null, + "name": "telemetry_rollups", + "system": false, + "type": "base", + "updateRule": null, + "viewRule": null + }); + + app.save(rollups); + + const alerts = new Collection({ + "createRule": null, + "deleteRule": null, + "fields": [ + { + "autogeneratePattern": "[a-z0-9]{15}", + "hidden": false, + "id": "text3208210256", + "max": 15, + "min": 15, + "name": "id", + "pattern": "^[a-z0-9]+$", + "presentable": false, + "primaryKey": true, + "required": true, + "system": true, + "type": "text" + }, + { + "autogeneratePattern": "", + "hidden": false, + "id": "text_ta_kind", + "max": 0, + "min": 0, + "name": "kind", + "pattern": "", + "presentable": false, + "primaryKey": false, + "required": true, + "system": false, + "type": "text" + }, + { + "autogeneratePattern": "", + "hidden": false, + "id": "text_ta_dim", + "max": 0, + "min": 0, + "name": "dim", + "pattern": "", + "presentable": false, + "primaryKey": false, + "required": false, + "system": false, + "type": "text" + }, + { + "autogeneratePattern": "", + "hidden": false, + "id": "text_ta_message", + "max": 0, + "min": 0, + "name": "message", + "pattern": "", + "presentable": false, + "primaryKey": false, + "required": false, + "system": false, + "type": "text" + }, + { + "hidden": false, + "id": "number_ta_value", + "max": null, + "min": null, + "name": "value", + "onlyInt": false, + "presentable": false, + "required": false, + "system": false, + "type": "number" + }, + { + "hidden": false, + "id": "json_ta_meta", + "maxSize": 0, + "name": "meta", + "presentable": false, + "required": false, + "system": false, + "type": "json" + }, + { + "hidden": false, + "id": "autodate2990389176", + "name": "created", + "onCreate": true, + "onUpdate": false, + "presentable": false, + "system": false, + "type": "autodate" + }, + { + "hidden": false, + "id": "autodate3332085495", + "name": "updated", + "onCreate": true, + "onUpdate": true, + "presentable": false, + "system": false, + "type": "autodate" + } + ], + "id": "pbc_telemetry_alerts", + "indexes": [ + "CREATE INDEX `idx_alerts_kind_dim_created` ON `telemetry_alerts` (`kind`, `dim`, `created`)" + ], + "listRule": null, + "name": "telemetry_alerts", + "system": false, + "type": "base", + "updateRule": null, + "viewRule": null + }); + + return app.save(alerts); +}, (app) => { + app.delete(app.findCollectionByNameOrId("pbc_telemetry_alerts")); + return app.delete(app.findCollectionByNameOrId("pbc_telemetry_rollups")); +}) diff --git a/src/app/routeTree.gen.ts b/src/app/routeTree.gen.ts index cdc5fb1..0b8d5e6 100644 --- a/src/app/routeTree.gen.ts +++ b/src/app/routeTree.gen.ts @@ -22,6 +22,7 @@ import { Route as AuthedAdminRouteImport } from './routes/_authed/admin' import { Route as AuthedTournamentsIndexRouteImport } from './routes/_authed/tournaments/index' import { Route as AuthedAdminIndexRouteImport } from './routes/_authed/admin/index' import { Route as ApiTournamentsUploadLogoRouteImport } from './routes/api/tournaments/upload-logo' +import { Route as ApiTelemetryBeaconRouteImport } from './routes/api/telemetry/beacon' import { Route as ApiTeamsUploadLogoRouteImport } from './routes/api/teams/upload-logo' import { Route as ApiSpotifyTokenRouteImport } from './routes/api/spotify/token' import { Route as ApiSpotifySearchRouteImport } from './routes/api/spotify/search' @@ -117,6 +118,11 @@ const ApiTournamentsUploadLogoRoute = path: '/api/tournaments/upload-logo', getParentRoute: () => rootRouteImport, } as any) +const ApiTelemetryBeaconRoute = ApiTelemetryBeaconRouteImport.update({ + id: '/api/telemetry/beacon', + path: '/api/telemetry/beacon', + getParentRoute: () => rootRouteImport, +} as any) const ApiTeamsUploadLogoRoute = ApiTeamsUploadLogoRouteImport.update({ id: '/api/teams/upload-logo', path: '/api/teams/upload-logo', @@ -303,6 +309,7 @@ export interface FileRoutesByFullPath { '/api/spotify/search': typeof ApiSpotifySearchRoute '/api/spotify/token': typeof ApiSpotifyTokenRoute '/api/teams/upload-logo': typeof ApiTeamsUploadLogoRoute + '/api/telemetry/beacon': typeof ApiTelemetryBeaconRoute '/api/tournaments/upload-logo': typeof ApiTournamentsUploadLogoRoute '/admin/': typeof AuthedAdminIndexRoute '/tournaments/': typeof AuthedTournamentsIndexRoute @@ -345,6 +352,7 @@ export interface FileRoutesByTo { '/api/spotify/search': typeof ApiSpotifySearchRoute '/api/spotify/token': typeof ApiSpotifyTokenRoute '/api/teams/upload-logo': typeof ApiTeamsUploadLogoRoute + '/api/telemetry/beacon': typeof ApiTelemetryBeaconRoute '/api/tournaments/upload-logo': typeof ApiTournamentsUploadLogoRoute '/admin': typeof AuthedAdminIndexRoute '/tournaments': typeof AuthedTournamentsIndexRoute @@ -390,6 +398,7 @@ export interface FileRoutesById { '/api/spotify/search': typeof ApiSpotifySearchRoute '/api/spotify/token': typeof ApiSpotifyTokenRoute '/api/teams/upload-logo': typeof ApiTeamsUploadLogoRoute + '/api/telemetry/beacon': typeof ApiTelemetryBeaconRoute '/api/tournaments/upload-logo': typeof ApiTournamentsUploadLogoRoute '/_authed/admin/': typeof AuthedAdminIndexRoute '/_authed/tournaments/': typeof AuthedTournamentsIndexRoute @@ -435,6 +444,7 @@ export interface FileRouteTypes { | '/api/spotify/search' | '/api/spotify/token' | '/api/teams/upload-logo' + | '/api/telemetry/beacon' | '/api/tournaments/upload-logo' | '/admin/' | '/tournaments/' @@ -477,6 +487,7 @@ export interface FileRouteTypes { | '/api/spotify/search' | '/api/spotify/token' | '/api/teams/upload-logo' + | '/api/telemetry/beacon' | '/api/tournaments/upload-logo' | '/admin' | '/tournaments' @@ -521,6 +532,7 @@ export interface FileRouteTypes { | '/api/spotify/search' | '/api/spotify/token' | '/api/teams/upload-logo' + | '/api/telemetry/beacon' | '/api/tournaments/upload-logo' | '/_authed/admin/' | '/_authed/tournaments/' @@ -555,6 +567,7 @@ export interface RootRouteChildren { ApiSpotifySearchRoute: typeof ApiSpotifySearchRoute ApiSpotifyTokenRoute: typeof ApiSpotifyTokenRoute ApiTeamsUploadLogoRoute: typeof ApiTeamsUploadLogoRoute + ApiTelemetryBeaconRoute: typeof ApiTelemetryBeaconRoute ApiTournamentsUploadLogoRoute: typeof ApiTournamentsUploadLogoRoute ApiFilesCollectionRecordIdFileRoute: typeof ApiFilesCollectionRecordIdFileRoute } @@ -652,6 +665,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof ApiTournamentsUploadLogoRouteImport parentRoute: typeof rootRouteImport } + '/api/telemetry/beacon': { + id: '/api/telemetry/beacon' + path: '/api/telemetry/beacon' + fullPath: '/api/telemetry/beacon' + preLoaderRoute: typeof ApiTelemetryBeaconRouteImport + parentRoute: typeof rootRouteImport + } '/api/teams/upload-logo': { id: '/api/teams/upload-logo' path: '/api/teams/upload-logo' @@ -944,6 +964,7 @@ const rootRouteChildren: RootRouteChildren = { ApiSpotifySearchRoute: ApiSpotifySearchRoute, ApiSpotifyTokenRoute: ApiSpotifyTokenRoute, ApiTeamsUploadLogoRoute: ApiTeamsUploadLogoRoute, + ApiTelemetryBeaconRoute: ApiTelemetryBeaconRoute, ApiTournamentsUploadLogoRoute: ApiTournamentsUploadLogoRoute, ApiFilesCollectionRecordIdFileRoute: ApiFilesCollectionRecordIdFileRoute, } @@ -952,10 +973,11 @@ export const routeTree = rootRouteImport ._addFileTypes() import type { getRouter } from './router.tsx' -import type { createStart } from '@tanstack/react-start' +import type { startInstance } from './start.ts' declare module '@tanstack/react-start' { interface Register { ssr: true router: Awaited> + config: Awaited> } } diff --git a/src/app/routes/__root.tsx b/src/app/routes/__root.tsx index ba1d90b..a81eb22 100644 --- a/src/app/routes/__root.tsx +++ b/src/app/routes/__root.tsx @@ -5,11 +5,13 @@ import { Scripts, createRootRouteWithContext, isRedirect, + useRouter, } from "@tanstack/react-router"; import * as React from "react"; import { DefaultCatchBoundary } from "@/components/DefaultCatchBoundary"; import { type QueryClient } from "@tanstack/react-query"; import { ensureSuperTokensFrontend } from "@/lib/supertokens/client"; +import { initTelemetry, track } from "@/lib/telemetry/client"; import { AuthContextType } from "@/contexts/auth-context"; import Providers from "@/features/core/components/providers"; import { SessionMonitor } from "@/components/session-monitor"; @@ -175,12 +177,15 @@ export const Route = createRootRouteWithContext<{ }); function RootComponent() { + const router = useRouter(); + React.useEffect(() => { ensureSuperTokensFrontend(); + initTelemetry(router); if (import.meta.env.PROD && 'serviceWorker' in navigator) { - navigator.serviceWorker.register('/sw.js').catch(() => {}); + navigator.serviceWorker.register('/sw.js').catch(() => track('sw_register_failed')); } - }, []); + }, [router]); return ( @@ -193,7 +198,6 @@ function RootComponent() { ); } -// todo: analytics -> process.env data-website-id function RootDocument({ children }: { children: React.ReactNode }) { return ( { - if (!context.auth?.roles?.includes("Admin")) { + if (!isAdminRole(context.auth?.roles)) { throw redirect({ to: "/" }); } diff --git a/src/app/routes/_authed/admin/activities.tsx b/src/app/routes/_authed/admin/activities.tsx index 0cc963a..a62fc78 100644 --- a/src/app/routes/_authed/admin/activities.tsx +++ b/src/app/routes/_authed/admin/activities.tsx @@ -2,7 +2,8 @@ import { createFileRoute } from "@tanstack/react-router"; import { prefetchServerQuery } from "@/lib/tanstack-query/utils/prefetch"; import { ActivitiesTable, activityQueries } from "@/features/activities"; import { PlayersActivityTable, playerQueries } from "@/features/players"; -import { Box, Divider, Group, Skeleton, Stack, Tabs } from "@mantine/core"; +import { ListSkeleton } from "@/components/skeletons"; +import { Tabs } from "@mantine/core"; import { Suspense, useState } from "react"; import { Trans } from "@lingui/react/macro"; import { msg } from "@lingui/core/macro"; @@ -25,30 +26,6 @@ export const Route = createFileRoute("/_authed/admin/activities")({ }), }); -function ActivityRowsSkeleton({ withSearch = false }: { withSearch?: boolean }) { - return ( - - {withSearch && ( - - - - )} - {Array.from({ length: 8 }).map((_, index) => ( -
- - - - - - - - -
- ))} -
- ); -} - function Stats() { const [activeTab, setActiveTab] = useState("server-functions"); @@ -60,13 +37,13 @@ function Stats() { - }> + }> - }> + }> diff --git a/src/app/routes/_authed/admin/tournaments/$id/assign-partners.tsx b/src/app/routes/_authed/admin/tournaments/$id/assign-partners.tsx index f49fb71..3df4c0f 100644 --- a/src/app/routes/_authed/admin/tournaments/$id/assign-partners.tsx +++ b/src/app/routes/_authed/admin/tournaments/$id/assign-partners.tsx @@ -1,6 +1,7 @@ import { createFileRoute, redirect, useNavigate } from "@tanstack/react-router"; import { tournamentQueries, useFreeAgents, useTournament } from "@/features/tournaments/queries"; import { ensureServerQueryData } from "@/lib/tanstack-query/utils/ensure"; +import { StatTileSkeleton } from "@/components/skeletons"; import { Stack, Text, Button, Alert, LoadingOverlay, Group, Skeleton } from "@mantine/core"; import { useState } from "react"; import useGenerateRandomTeams from "@/features/tournaments/hooks/use-generate-random-teams"; @@ -37,10 +38,7 @@ function AssignPartnersPending() { return ( - - - - + diff --git a/src/app/routes/_authed/admin/tournaments/$id/index.tsx b/src/app/routes/_authed/admin/tournaments/$id/index.tsx index ba24ea8..ae2db70 100644 --- a/src/app/routes/_authed/admin/tournaments/$id/index.tsx +++ b/src/app/routes/_authed/admin/tournaments/$id/index.tsx @@ -2,7 +2,7 @@ import { createFileRoute, redirect } from "@tanstack/react-router"; import { tournamentQueries } from "@/features/tournaments/queries"; import ManageTournament from "@/features/tournaments/components/manage-tournament"; import { ensureServerQueryData } from "@/lib/tanstack-query/utils/ensure"; -import { Divider, Group, Skeleton, Stack } from "@mantine/core"; +import { ListSkeleton } from "@/components/skeletons"; import { msg } from "@lingui/core/macro"; export const Route = createFileRoute("/_authed/admin/tournaments/$id/")({ @@ -31,18 +31,14 @@ export const Route = createFileRoute("/_authed/admin/tournaments/$id/")({ function ManageTournamentPending() { return ( - - {Array.from({ length: 5 }).map((_, index) => ( -
- - - - - - -
- ))} -
+ ); } diff --git a/src/app/routes/_authed/admin/tournaments/$id/teams.tsx b/src/app/routes/_authed/admin/tournaments/$id/teams.tsx index fa96f99..c717617 100644 --- a/src/app/routes/_authed/admin/tournaments/$id/teams.tsx +++ b/src/app/routes/_authed/admin/tournaments/$id/teams.tsx @@ -2,7 +2,8 @@ import { createFileRoute, redirect } from "@tanstack/react-router"; import { tournamentQueries } from "@/features/tournaments/queries"; import ManageTeams from "@/features/teams/components/manage-teams"; import { ensureServerQueryData } from "@/lib/tanstack-query/utils/ensure"; -import { Box, Divider, Group, Skeleton, Stack } from "@mantine/core"; +import { ListSkeleton } from "@/components/skeletons"; +import { Box, Skeleton, Stack } from "@mantine/core"; import { msg } from "@lingui/core/macro"; export const Route = createFileRoute("/_authed/admin/tournaments/$id/teams")({ @@ -38,21 +39,7 @@ function ManageTeamsPending() { - - {Array.from({ length: 8 }).map((_, index) => ( -
- - - - - - - - - -
- ))} -
+ ); } diff --git a/src/app/routes/_authed/admin/tournaments/index.tsx b/src/app/routes/_authed/admin/tournaments/index.tsx index 7210102..205cb95 100644 --- a/src/app/routes/_authed/admin/tournaments/index.tsx +++ b/src/app/routes/_authed/admin/tournaments/index.tsx @@ -1,7 +1,7 @@ import ManageTournaments from "@/features/admin/components/manage-tournaments"; import { tournamentQueries } from "@/features/tournaments/queries"; import { prefetchServerQuery } from "@/lib/tanstack-query/utils/prefetch"; -import { Divider, Group, Skeleton, Stack } from "@mantine/core"; +import { ListSkeleton } from "@/components/skeletons"; import { createFileRoute } from "@tanstack/react-router"; import { Suspense } from "react"; import { msg } from "@lingui/core/macro"; @@ -22,25 +22,13 @@ export const Route = createFileRoute("/_authed/admin/tournaments/")({ component: RouteComponent, }); -function TournamentListSkeleton() { - return ( - - {Array.from({ length: 6 }).map((_, index) => ( -
- - - - - -
- ))} -
- ); -} - function RouteComponent() { return ( - }> + + } + > ); diff --git a/src/app/routes/_authed/admin/tournaments/run.$id.tsx b/src/app/routes/_authed/admin/tournaments/run.$id.tsx index ad68e03..216ce86 100644 --- a/src/app/routes/_authed/admin/tournaments/run.$id.tsx +++ b/src/app/routes/_authed/admin/tournaments/run.$id.tsx @@ -8,12 +8,10 @@ import SeedTournament from "@/features/tournaments/components/seed-tournament"; import SetupGroupStage from "@/features/tournaments/components/setup-group-stage"; import GroupStageView from "@/features/tournaments/components/group-stage-view"; import { Container, Stack, Divider, Title, Box, Card, Group, Skeleton, SimpleGrid } from "@mantine/core"; -import { useMemo } from "react"; -import { BracketData } from "@/features/bracket/types"; -import { Match } from "@/features/matches/types"; import BracketView from "@/features/bracket/components/bracket-view"; import { SpotifyControlsBar } from "@/features/spotify/components"; -import { useAuth } from "@/contexts/auth-context"; +import { useIsAdmin } from "@/features/core/hooks/use-is-admin"; +import { useTournamentRunState } from "@/features/tournaments/hooks/use-tournament-run-state"; import { Trans } from "@lingui/react/macro"; import { msg } from "@lingui/core/macro"; @@ -83,67 +81,10 @@ function RunTournamentPending() { function RouteComponent() { const { id } = Route.useParams(); const { data: tournament } = useTournament(id); - const { roles } = useAuth(); - const isAdmin = roles?.includes('Admin') || false; + const isAdmin = useIsAdmin(); - const hasGroupStage = useMemo(() => { - return tournament.matches?.some((match) => match.round === -1) || false; - }, [tournament.matches]); - - const hasKnockout = useMemo(() => { - return tournament.matches?.some((match) => match.round !== -1) || false; - }, [tournament.matches]); - - const knockoutBracketPopulated = useMemo(() => { - return tournament.matches?.some((match) => - match.round === 0 && match.lid >= 0 && (match.home || match.away) - ) || false; - }, [tournament.matches]); - - const nextUpMatchId = useMemo(() => { - const ready = (tournament.matches ?? []) - .filter( - (match) => - match.status === "ready" && match.home && match.away && !match.bye - ) - .sort((a, b) => a.order - b.order); - return ready[0]?.id; - }, [tournament.matches]); - - const bracket: BracketData = useMemo(() => { - if (!tournament.matches || tournament.matches.length === 0) { - return { winners: [], losers: [] }; - } - - const winnersMap = new Map(); - const losersMap = new Map(); - - tournament.matches - .filter((match) => match.round !== -1) - .sort((a, b) => a.lid - b.lid) - .forEach((match) => { - if (!match.is_losers_bracket) { - if (!winnersMap.has(match.round)) { - winnersMap.set(match.round, []); - } - winnersMap.get(match.round)!.push(match); - } else { - if (!losersMap.has(match.round)) { - losersMap.set(match.round, []); - } - losersMap.get(match.round)!.push(match); - } - }); - - const winners = Array.from(winnersMap.entries()) - .sort(([a], [b]) => a - b) - .map(([, matches]) => matches); - - const losers = Array.from(losersMap.entries()) - .sort(([a], [b]) => a - b) - .map(([, matches]) => matches); - return { winners, losers }; - }, [tournament.matches]); + const { hasGroupStage, hasKnockout, knockoutBracketPopulated, nextUpMatchId, bracket } = + useTournamentRunState(tournament.matches); return ( diff --git a/src/app/routes/_authed/tournaments/$tournamentId.tsx b/src/app/routes/_authed/tournaments/$tournamentId.tsx index 9ed811c..bf42b78 100644 --- a/src/app/routes/_authed/tournaments/$tournamentId.tsx +++ b/src/app/routes/_authed/tournaments/$tournamentId.tsx @@ -1,6 +1,7 @@ import { createFileRoute } from '@tanstack/react-router' import { tournamentQueries } from '@/features/tournaments/queries'; import Profile from '@/features/tournaments/components/profile'; +import { isAdminRole } from '@/features/core/utils/roles'; import { z } from "zod"; import { prefetchServerQuery } from '@/lib/tanstack-query/utils/prefetch'; import { Suspense } from 'react'; @@ -20,7 +21,7 @@ export const Route = createFileRoute('/_authed/tournaments/$tournamentId')({ header: { collapsed: true, withBackButton: true, - settingsLink: context.auth.roles.includes("Admin") ? `/admin/tournaments/${params.tournamentId}` : undefined + settingsLink: isAdminRole(context.auth.roles) ? `/admin/tournaments/${params.tournamentId}` : undefined }, refresh: [tournamentQueries.details(params.tournamentId).queryKey], withPadding: false diff --git a/src/app/routes/api/auth.$.ts b/src/app/routes/api/auth.$.ts index 3b617e2..0c560c7 100644 --- a/src/app/routes/api/auth.$.ts +++ b/src/app/routes/api/auth.$.ts @@ -8,8 +8,6 @@ ensureSuperTokensBackend(); // forwards all supertokens api methods to our API const superTokensHandler = handleAuthAPIRequest(); const handleRequest = async ({ request }: {request: Request}) => { - console.log("=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-="); - console.log("Handling auth request:", request.method, request.url); return superTokensHandler(request); }; export const Route = createFileRoute('/api/auth/$')({ diff --git a/src/app/routes/api/push/subscribe.ts b/src/app/routes/api/push/subscribe.ts index 51b77b7..bbcc4b4 100644 --- a/src/app/routes/api/push/subscribe.ts +++ b/src/app/routes/api/push/subscribe.ts @@ -1,5 +1,6 @@ import { createFileRoute } from "@tanstack/react-router"; import { superTokensRequestMiddleware } from "@/utils/supertokens"; +import { apiLoggingMiddleware } from "@/lib/telemetry/api-logging.server"; import { pbAdmin } from "@/lib/pocketbase/client"; import { logger } from "@/lib/logger"; @@ -11,7 +12,7 @@ const json = (body: unknown, status = 200) => export const Route = createFileRoute("/api/push/subscribe")({ server: { - middleware: [superTokensRequestMiddleware], + middleware: [apiLoggingMiddleware, superTokensRequestMiddleware], handlers: { POST: async ({ request, context }) => { const player = (context as any).player; diff --git a/src/app/routes/api/push/test.ts b/src/app/routes/api/push/test.ts index 3f8f587..7cf43c9 100644 --- a/src/app/routes/api/push/test.ts +++ b/src/app/routes/api/push/test.ts @@ -1,6 +1,7 @@ import { createFileRoute } from "@tanstack/react-router"; import { msg } from "@lingui/core/macro"; import { superTokensRequestMiddleware } from "@/utils/supertokens"; +import { apiLoggingMiddleware } from "@/lib/telemetry/api-logging.server"; import { sendPushToPlayer } from "@/lib/push"; import { isPushConfigured } from "@/lib/config"; import { logger } from "@/lib/logger"; @@ -13,7 +14,7 @@ const json = (body: unknown, status = 200) => export const Route = createFileRoute("/api/push/test")({ server: { - middleware: [superTokensRequestMiddleware], + middleware: [apiLoggingMiddleware, superTokensRequestMiddleware], handlers: { POST: async ({ context }) => { const player = (context as any).player; diff --git a/src/app/routes/api/push/unsubscribe.ts b/src/app/routes/api/push/unsubscribe.ts index 8e3c687..92b3faf 100644 --- a/src/app/routes/api/push/unsubscribe.ts +++ b/src/app/routes/api/push/unsubscribe.ts @@ -1,5 +1,6 @@ import { createFileRoute } from "@tanstack/react-router"; import { superTokensRequestMiddleware } from "@/utils/supertokens"; +import { apiLoggingMiddleware } from "@/lib/telemetry/api-logging.server"; import { pbAdmin } from "@/lib/pocketbase/client"; import { logger } from "@/lib/logger"; @@ -11,7 +12,7 @@ const json = (body: unknown, status = 200) => export const Route = createFileRoute("/api/push/unsubscribe")({ server: { - middleware: [superTokensRequestMiddleware], + middleware: [apiLoggingMiddleware, superTokensRequestMiddleware], handlers: { POST: async ({ request, context }) => { const player = (context as any).player; diff --git a/src/app/routes/api/teams/upload-logo.ts b/src/app/routes/api/teams/upload-logo.ts index 1ea5723..d2a8f74 100644 --- a/src/app/routes/api/teams/upload-logo.ts +++ b/src/app/routes/api/teams/upload-logo.ts @@ -1,6 +1,7 @@ import { createFileRoute } from "@tanstack/react-router"; import { msg } from "@lingui/core/macro"; import { superTokensRequestMiddleware } from "@/utils/supertokens"; +import { apiLoggingMiddleware } from "@/lib/telemetry/api-logging.server"; import { pbAdmin } from "@/lib/pocketbase/client"; import { logger } from "@/lib/logger"; import { localizedFor } from "@/lib/i18n/server-messages"; @@ -12,7 +13,7 @@ const uploadSchema = z.object({ export const Route = createFileRoute("/api/teams/upload-logo")({ server: { - middleware: [superTokensRequestMiddleware], + middleware: [apiLoggingMiddleware, superTokensRequestMiddleware], handlers: { POST: async ({ request, context }) => { const i18n = localizedFor(context.metadata); diff --git a/src/app/routes/api/telemetry/beacon.ts b/src/app/routes/api/telemetry/beacon.ts new file mode 100644 index 0000000..38981d8 --- /dev/null +++ b/src/app/routes/api/telemetry/beacon.ts @@ -0,0 +1,128 @@ +import { createFileRoute } from "@tanstack/react-router"; +import { superTokensOptionalRequestMiddleware } from "@/utils/supertokens"; +import { apiLoggingMiddleware } from "@/lib/telemetry/api-logging.server"; +import { beaconPayloadSchema, MAX_BEACON_BYTES, type BeaconEvent } from "@/lib/telemetry/beacon-schema"; +import { errorGroupHash } from "@/lib/telemetry/error-hash"; +import { redactArguments } from "@/lib/telemetry/redact-args"; +import { Logger } from "@/lib/logger"; +import type { Player } from "@/features/players/types"; + +const logger = new Logger("Telemetry"); + +const BUCKET_CAPACITY = 120; +const REFILL_PER_MS = BUCKET_CAPACITY / 60_000; +const buckets = new Map(); + +const takeTokens = (key: string, count: number): boolean => { + const now = Date.now(); + const bucket = buckets.get(key) ?? { tokens: BUCKET_CAPACITY, updatedAt: now }; + bucket.tokens = Math.min(BUCKET_CAPACITY, bucket.tokens + (now - bucket.updatedAt) * REFILL_PER_MS); + bucket.updatedAt = now; + if (bucket.tokens < count) { + buckets.set(key, bucket); + return false; + } + bucket.tokens -= count; + buckets.set(key, bucket); + + if (buckets.size > 2000) { + for (const [k, v] of buckets) { + if (now - v.updatedAt > 5 * 60_000) buckets.delete(k); + } + } + return true; +}; + +const clientIp = (request: Request): string => + request.headers.get("x-forwarded-for")?.split(",")[0]?.trim() || + request.headers.get("x-real-ip") || + "unknown"; + +const writeEvents = async ( + events: BeaconEvent[], + base: { sessionId: string; playerId?: string; userAgent?: string } +) => { + const { pbAdmin } = await import("@/lib/pocketbase/client"); + await pbAdmin.authPromise; + + // Sequential on purpose: SQLite is single-writer; a burst of parallel + // creates just queues behind the lock. + for (const event of events) { + try { + if (event.kind === "error") { + await pbAdmin.createClientError({ + message: event.message, + stack: event.stack, + source: event.source, + path: event.path, + route_id: event.routeId, + group_hash: errorGroupHash(event), + resolved: false, + player: base.playerId, + session_id: base.sessionId, + user_agent: base.userAgent, + props: redactArguments(event.props), + }); + } else { + await pbAdmin.createClientEvent({ + name: event.kind === "vital" ? `vital.${event.metric}` : event.name, + player: base.playerId, + session_id: base.sessionId, + path: event.path, + route_id: event.routeId, + value: event.value, + props: + event.kind === "vital" + ? event.rating + ? { rating: event.rating } + : undefined + : redactArguments(event.props), + user_agent: base.userAgent, + }); + } + } catch (error) { + logger.error("Failed to persist beacon event", error); + } + } +}; + +export const Route = createFileRoute("/api/telemetry/beacon")({ + server: { + middleware: [apiLoggingMiddleware, superTokensOptionalRequestMiddleware], + handlers: { + POST: async ({ request, context }) => { + const player = (context as unknown as { player?: Player | null }).player ?? undefined; + + const raw = await request.text(); + if (raw.length > MAX_BEACON_BYTES) { + return new Response(null, { status: 413 }); + } + + let parsed: unknown; + try { + parsed = JSON.parse(raw); + } catch { + return new Response(null, { status: 400 }); + } + + const result = beaconPayloadSchema.safeParse(parsed); + if (!result.success) { + return new Response(null, { status: 400 }); + } + + const key = player ? `p:${player.id}` : `ip:${clientIp(request)}`; + if (!takeTokens(key, result.data.events.length)) { + return new Response(null, { status: 429 }); + } + + void writeEvents(result.data.events, { + sessionId: result.data.sessionId, + playerId: player?.id, + userAgent: request.headers.get("user-agent") ?? undefined, + }).catch((error) => logger.error("Beacon write batch failed", error)); + + return new Response(null, { status: 204 }); + }, + }, + }, +}); diff --git a/src/app/routes/api/tournaments/upload-logo.ts b/src/app/routes/api/tournaments/upload-logo.ts index 9b7f990..7121c65 100644 --- a/src/app/routes/api/tournaments/upload-logo.ts +++ b/src/app/routes/api/tournaments/upload-logo.ts @@ -1,6 +1,7 @@ import { createFileRoute } from "@tanstack/react-router"; import { msg } from "@lingui/core/macro"; import { superTokensRequestMiddleware } from "@/utils/supertokens"; +import { apiLoggingMiddleware } from "@/lib/telemetry/api-logging.server"; import { pbAdmin } from "@/lib/pocketbase/client"; import { logger } from "@/lib/logger"; import { localizedFor } from "@/lib/i18n/server-messages"; @@ -12,7 +13,7 @@ const uploadSchema = z.object({ export const Route = createFileRoute("/api/tournaments/upload-logo")({ server: { - middleware: [superTokensRequestMiddleware], + middleware: [apiLoggingMiddleware, superTokensRequestMiddleware], handlers: { POST: async ({ request, context }) => { const i18n = localizedFor(context.metadata); diff --git a/src/app/start.ts b/src/app/start.ts new file mode 100644 index 0000000..7f79732 --- /dev/null +++ b/src/app/start.ts @@ -0,0 +1,8 @@ +import { createStart } from "@tanstack/react-start"; +import { serverFnLoggingMiddleware } from "@/utils/activities"; + +// Global function middleware: every server fn (current and future) is audited +// without opting in at the definition site. +export const startInstance = createStart(() => ({ + functionMiddleware: [serverFnLoggingMiddleware], +})); diff --git a/src/components/DefaultCatchBoundary.tsx b/src/components/DefaultCatchBoundary.tsx index 81f4f8a..19cca0a 100644 --- a/src/components/DefaultCatchBoundary.tsx +++ b/src/components/DefaultCatchBoundary.tsx @@ -22,6 +22,7 @@ import { useDisclosure } from '@mantine/hooks' import { useEffect } from 'react' import toast from '@/lib/sonner' import { logger } from '@/lib/logger' +import { captureBoundaryError } from '@/lib/telemetry/client' import { XCircleIcon, WarningIcon } from '@phosphor-icons/react' import { Trans, useLingui } from '@lingui/react/macro' import Button from './button' @@ -41,6 +42,7 @@ export function DefaultCatchBoundary({ error }: ErrorComponentProps) { useEffect(() => { logger.error('DefaultCatchBoundary | ', error) + captureBoundaryError(error) if (errorMessage.toLowerCase().includes('unauthenticated')) { toast.error(t`You've been logged out`) diff --git a/src/components/ios-install-prompt.tsx b/src/components/ios-install-prompt.tsx index e6f17d5..b24ecf7 100644 --- a/src/components/ios-install-prompt.tsx +++ b/src/components/ios-install-prompt.tsx @@ -1,4 +1,5 @@ import { useEffect, useState } from 'react' +import { track } from '@/lib/telemetry/client' import { Box, Paper, Group, Text, ActionIcon } from '@mantine/core' import { DownloadIcon, XIcon } from '@phosphor-icons/react' import { Trans, useLingui } from '@lingui/react/macro' @@ -26,13 +27,17 @@ export function IOSInstallPrompt() { if ((isIOS || isAndroid) && !isInStandaloneMode && !hasBeenDismissed) { setPlatform(isIOS ? 'ios' : 'android') - const timer = setTimeout(() => setShow(true), 3000) + const timer = setTimeout(() => { + setShow(true) + track('pwa_ios_prompt_shown') + }, 3000) return () => clearTimeout(timer) } }, []) const handleDismiss = () => { localStorage.setItem('pwa-install-prompt-dismissed', 'true') + track('pwa_ios_prompt_dismissed') setShow(false) } diff --git a/src/components/skeletons/card-skeleton.tsx b/src/components/skeletons/card-skeleton.tsx new file mode 100644 index 0000000..8b6ee7d --- /dev/null +++ b/src/components/skeletons/card-skeleton.tsx @@ -0,0 +1,35 @@ +import { Box, Group, Paper, Skeleton } from "@mantine/core"; +import type { MantineRadius } from "@mantine/core"; + +export interface CardSkeletonProps { + withAvatar?: boolean; + avatarSize?: number; + withBadge?: boolean; + radius?: MantineRadius; + opacity?: number; +} + +export const CardSkeleton = ({ + withAvatar = true, + avatarSize = 32, + withBadge = true, + radius = "md", + opacity = 1, +}: CardSkeletonProps) => { + return ( + + + {withAvatar && ( + + )} + + + + + {withBadge && } + + + ); +}; + +export default CardSkeleton; diff --git a/src/components/skeletons/index.ts b/src/components/skeletons/index.ts new file mode 100644 index 0000000..eaec0ec --- /dev/null +++ b/src/components/skeletons/index.ts @@ -0,0 +1,3 @@ +export * from "./list-skeleton"; +export * from "./card-skeleton"; +export * from "./stat-tile-skeleton"; diff --git a/src/components/skeletons/list-skeleton.tsx b/src/components/skeletons/list-skeleton.tsx new file mode 100644 index 0000000..8344eda --- /dev/null +++ b/src/components/skeletons/list-skeleton.tsx @@ -0,0 +1,76 @@ +import { Box, Divider, Group, Skeleton, Stack } from "@mantine/core"; +import type { MantineSpacing } from "@mantine/core"; + +export interface ListSkeletonProps { + rows?: number; + withSearch?: boolean; + withAvatar?: boolean; + avatarSize?: number; + withSubtitle?: boolean; + withTrailing?: boolean | "double" | "text"; + titleWidth?: string | number; + padding?: MantineSpacing; + fadeStep?: number; + fadeFloor?: number; +} + +export const ListSkeleton = ({ + rows = 6, + withSearch = false, + withAvatar = false, + avatarSize = 40, + withSubtitle = false, + withTrailing = false, + titleWidth = "45%", + padding = "md", + fadeStep = 0.1, + fadeFloor = 0.35, +}: ListSkeletonProps) => { + return ( + + {withSearch && ( + + + + )} + {Array.from({ length: rows }).map((_, index) => ( +
+ + {withAvatar && ( + + )} + {withSubtitle ? ( + + + + + ) : ( + + )} + {withTrailing === "double" ? ( + + + + + ) : withTrailing === "text" ? ( + + ) : withTrailing ? ( + + ) : null} + + +
+ ))} +
+ ); +}; + +export default ListSkeleton; diff --git a/src/components/skeletons/stat-tile-skeleton.tsx b/src/components/skeletons/stat-tile-skeleton.tsx new file mode 100644 index 0000000..23068ff --- /dev/null +++ b/src/components/skeletons/stat-tile-skeleton.tsx @@ -0,0 +1,42 @@ +import { Card, Group, Skeleton, Stack } from "@mantine/core"; + +export interface StatTileSkeletonProps { + withCard?: boolean; + align?: "row" | "column"; + numberWidth?: number; + numberHeight?: number; + labelWidth?: number; + labelHeight?: number; +} + +export const StatTileSkeleton = ({ + withCard = false, + align = "column", + numberWidth = align === "row" ? 36 : 48, + numberHeight = 28, + labelWidth = align === "row" ? 110 : 80, + labelHeight = align === "row" ? 14 : 12, +}: StatTileSkeletonProps) => { + const content = + align === "row" ? ( + + + + + ) : ( + + + + + ); + + if (!withCard) return content; + + return ( + + {content} + + ); +}; + +export default StatTileSkeleton; diff --git a/src/contexts/spotify-context.tsx b/src/contexts/spotify-context.tsx index c2ff000..e013aa3 100644 --- a/src/contexts/spotify-context.tsx +++ b/src/contexts/spotify-context.tsx @@ -1,7 +1,7 @@ import { createContext, useCallback, useEffect, useMemo, useState, PropsWithChildren } from 'react'; import { useLingui } from '@lingui/react/macro'; import { SpotifyAuth } from '@/lib/spotify/auth'; -import { useAuth } from './auth-context'; +import { useIsAdmin } from '@/features/core/hooks/use-is-admin'; import { useConfig } from '@/hooks/use-config'; import type { SpotifyContextType, @@ -40,8 +40,7 @@ const deepEqual = (a: unknown, b: unknown): boolean => { }; export const SpotifyProvider: React.FC = ({ children }) => { - const { roles } = useAuth(); - const isAdmin = roles?.includes('Admin') || false; + const isAdmin = useIsAdmin(); const config = useConfig(); const { t } = useLingui(); diff --git a/src/features/admin/components/award-badges.tsx b/src/features/admin/components/award-badges.tsx index abab1d5..9b60a00 100644 --- a/src/features/admin/components/award-badges.tsx +++ b/src/features/admin/components/award-badges.tsx @@ -1,7 +1,7 @@ import { useState } from "react"; import { Box, Card, Text, Select, Button, Group, Stack, Badge, Divider } from "@mantine/core"; import { Trans, useLingui } from "@lingui/react/macro"; -import { awardManualBadge } from "@/features/badges/server"; +import useAwardManualBadge from "@/features/badges/hooks/use-award-manual-badge"; import { useAllBadges } from "@/features/badges/queries"; import toast from "@/lib/sonner"; import { usePlayers } from "@/features/players/queries"; @@ -13,35 +13,29 @@ const AwardBadges = () => { const [selectedPlayerId, setSelectedPlayerId] = useState(null); const [selectedBadgeId, setSelectedBadgeId] = useState(null); - const [isAwarding, setIsAwarding] = useState(false); + + const awardBadge = useAwardManualBadge(); const manualBadges = allBadges.filter((badge) => badge.type === "manual"); - const handleAwardBadge = async () => { + const handleAwardBadge = () => { if (!selectedPlayerId || !selectedBadgeId) return; - setIsAwarding(true); - try { - await awardManualBadge({ - data: { - playerId: selectedPlayerId, - badgeId: selectedBadgeId, + awardBadge.mutate( + { playerId: selectedPlayerId, badgeId: selectedBadgeId }, + { + onSuccess: () => { + const selectedPlayer = players.find((p) => p.id === selectedPlayerId); + const playerName = selectedPlayer + ? `${selectedPlayer.first_name} ${selectedPlayer.last_name}` + : t`Player`; + + toast.success(t`Badge awarded to ${playerName}`); + + setSelectedPlayerId(null); }, - }); - - const selectedPlayer = players.find((p) => p.id === selectedPlayerId); - const playerName = selectedPlayer - ? `${selectedPlayer.first_name} ${selectedPlayer.last_name}` - : t`Player`; - - toast.success(t`Badge awarded to ${playerName}`); - - setSelectedPlayerId(null); - } catch (error) { - toast.error(t`Failed to award badge`); - } finally { - setIsAwarding(false); - } + } + ); }; const playerOptions = players.map((player) => ({ @@ -98,7 +92,7 @@ const AwardBadges = () => { @@ -161,4 +184,4 @@ const EditEnrolledPlayers = ({ tournamentId }: EditEnrolledPlayersProps) => { ); }; -export default EditEnrolledPlayers; +export default FreeAgentSection; diff --git a/src/features/tournaments/components/enrollment/index.tsx b/src/features/tournaments/components/enrollment/index.tsx new file mode 100644 index 0000000..ddd6687 --- /dev/null +++ b/src/features/tournaments/components/enrollment/index.tsx @@ -0,0 +1,17 @@ +import { Tournament } from "../../types"; +import FreeAgentSection from "./free-agent-section"; +import TeamSection from "./team-section"; + +interface EnrollmentProps { + tournament: Tournament; +} + +const Enrollment = ({ tournament }: EnrollmentProps) => { + return tournament.regional === true ? ( + + ) : ( + + ); +}; + +export default Enrollment; diff --git a/src/features/tournaments/components/enrollment/team-section.tsx b/src/features/tournaments/components/enrollment/team-section.tsx new file mode 100644 index 0000000..c2b8bc0 --- /dev/null +++ b/src/features/tournaments/components/enrollment/team-section.tsx @@ -0,0 +1,283 @@ +import { + Stack, + ActionIcon, + Text, + Group, + Loader, + Box, + Button as MantineButton, +} from "@mantine/core"; +import Typeahead, { TypeaheadOption } from "@/components/typeahead"; +import { TrashIcon, PencilIcon, MusicNotesIcon } from "@phosphor-icons/react"; +import { useState, useCallback, useMemo, memo } from "react"; +import { Trans, Plural, useLingui } from "@lingui/react/macro"; +import { useNavigate } from "@tanstack/react-router"; +import { useTournament, useUnenrolledTeams } from "../../queries"; +import useEnrollTeam from "../../hooks/use-enroll-team"; +import useUnenrollTeam from "../../hooks/use-unenroll-team"; +import TeamAvatar from "@/components/team-avatar"; +import { Team, TeamInfo } from "@/features/teams/types"; +import Sheet from "@/components/sheet/sheet"; +import TeamForm from "@/features/teams/components/team-form"; +import { useTeam } from "@/features/teams/queries"; +import { useSheet } from "@/hooks/use-sheet"; +import EnrollmentStats from "./enrollment-stats"; + +interface TeamSectionProps { + tournamentId: string; +} + +interface TeamItemProps { + team: TeamInfo; + onUnenroll: (teamId: string) => void; + onEdit: (teamId: string) => void; + disabled: boolean; + isRegional?: boolean; +} + +const TeamItem = memo(({ team, onUnenroll, onEdit, disabled, isRegional }: TeamItemProps) => { + const playerNames = useMemo( + () => + team.players?.map((p) => `${p.first_name} ${p.last_name}`).join(", ") || + "", + [team.players] + ); + + return ( + + + + + {team.name} + + {playerNames && ( + + {playerNames} + + )} + + onEdit(team.id)} + size="sm" + > + + + onUnenroll(team.id)} + disabled={disabled} + size="sm" + > + + + + ); +}); + +interface TeamEditSheetProps { + teamId: string; + isOpen: boolean; + onClose: () => void; +} + +const TeamEditSheet = ({ teamId, isOpen, onClose }: TeamEditSheetProps) => { + const { data: team } = useTeam(teamId); + const { t } = useLingui(); + + return ( + + {team && ( + p.id) : [], + logo: typeof team.logo === "string" ? undefined : team.logo, + }} + close={onClose} + /> + )} + + ); +}; + +const TeamSection = ({ tournamentId }: TeamSectionProps) => { + const { t } = useLingui(); + const navigate = useNavigate(); + const { data: tournament, isLoading: tournamentLoading } = + useTournament(tournamentId); + const { data: unenrolledTeams = [], isLoading: unenrolledLoading } = + useUnenrolledTeams(tournamentId); + + const isRegional = tournament?.regional; + + const { mutate: enrollTeam, isPending: isEnrolling } = useEnrollTeam(); + const { mutate: unenrollTeam, isPending: isUnenrolling } = useUnenrollTeam(); + + const [selectedTeamId, setSelectedTeamId] = useState(null); + const { + isOpen: editTeamOpened, + open: openEditTeam, + close: closeEditTeam, + } = useSheet(); + + const handleEditTeam = useCallback( + (teamId: string) => { + setSelectedTeamId(teamId); + openEditTeam(); + }, + [openEditTeam] + ); + + const handleCloseEditTeam = useCallback(() => { + setSelectedTeamId(null); + closeEditTeam(); + }, [closeEditTeam]); + + const searchTeams = async (query: string): Promise[]> => { + if (!query.trim()) return []; + + const filtered = unenrolledTeams.filter((team: Team) => + team.name.toLowerCase().includes(query.toLowerCase()) + ); + + return filtered.map((team: Team) => ({ + id: team.id, + data: team + })); + }; + + const handleEnrollTeam = useCallback( + (option: TypeaheadOption) => { + enrollTeam({ tournamentId, teamId: option.data.id }); + }, + [enrollTeam, tournamentId] + ); + + const handleUnenrollTeam = useCallback( + (teamId: string) => { + unenrollTeam({ tournamentId, teamId }); + }, + [unenrollTeam, tournamentId] + ); + + const renderTeamOption = (option: TypeaheadOption) => { + const team = option.data; + return ( + + + + {team.name} + + + ); + }; + + const formatTeam = (option: TypeaheadOption) => { + return option.data.name; + }; + + const isLoading = tournamentLoading || unenrolledLoading; + const enrolledTeams = tournament?.teams || []; + const hasEnrolledTeams = enrolledTeams.length > 0; + + return ( + <> + + + + + + + Add Team + + + + + + } + onClick={() => + navigate({ to: `/admin/tournaments/${tournamentId}/teams` }) + } + > + Manage Team Songs/Logos + + + + + + Enrolled Teams + + + + + + + {isLoading ? ( + + + + ) : !hasEnrolledTeams ? ( + + No teams enrolled yet + + ) : ( + + {enrolledTeams.map((team: TeamInfo) => ( + + ))} + + )} + + + + {selectedTeamId && ( + + )} + + ); +}; + +export default TeamSection; diff --git a/src/features/tournaments/components/manage-tournament.tsx b/src/features/tournaments/components/manage-tournament.tsx index 4e08c46..a0ce96e 100644 --- a/src/features/tournaments/components/manage-tournament.tsx +++ b/src/features/tournaments/components/manage-tournament.tsx @@ -1,5 +1,5 @@ import { useTournament } from "../queries"; -import { List } from "@mantine/core"; +import { Box, List } from "@mantine/core"; import ListButton from "@/components/list-button"; import Sheet from "@/components/sheet/sheet"; import TournamentForm from "./tournament-form"; @@ -8,16 +8,13 @@ import { PencilLineIcon, TreeStructureIcon, UsersThreeIcon, - UsersIcon, - ShuffleIcon, } from "@phosphor-icons/react"; import { useSheet } from "@/hooks/use-sheet"; -import EditEnrolledTeams from "./edit-enrolled-teams"; -import EditEnrolledPlayers from "./edit-enrolled-players"; import ListLink from "@/components/list-link"; -import React from "react"; import EditRules from "./edit-rules"; -import { useLingui } from "@lingui/react/macro"; +import Button from "@/components/button"; +import { Trans, useLingui } from "@lingui/react/macro"; +import { useNavigate } from "@tanstack/react-router"; interface ManageTournamentProps { tournamentId: string; @@ -25,6 +22,7 @@ interface ManageTournamentProps { const ManageTournament = ({ tournamentId }: ManageTournamentProps) => { const { t } = useLingui(); + const navigate = useNavigate(); const { data: tournament } = useTournament(tournamentId); if (!tournament) throw new Error("Tournament not found."); @@ -38,17 +36,26 @@ const ManageTournament = ({ tournamentId }: ManageTournamentProps) => { open: openEditRules, close: closeEditRules, } = useSheet(); - const { - isOpen: editTeamsOpened, - open: openEditTeams, - close: closeEditTeams, - } = useSheet(); - - const [v, setV] = React.useState(""); return ( <> + + + + + { Icon={PencilLineIcon} onClick={openEditRules} /> - - {tournament.regional && ( - - )} - {!tournament.regional && ( - - )} - { > - - - {tournament.regional === true ? ( - - ) : ( - - )} - ); }; -- 2.54.0 From 4af4a6552772f0fde93eef50b0d8c1dee9750ef7 Mon Sep 17 00:00:00 2001 From: yohlo Date: Tue, 25 Aug 2026 21:54:50 -0700 Subject: [PATCH 04/33] bug --- src/features/matches/types.test.ts | 9 +++++++++ src/features/matches/types.ts | 18 +++++++++--------- src/features/tournaments/types.test.ts | 15 +++++++++++++++ src/features/tournaments/types.ts | 2 +- 4 files changed, 34 insertions(+), 10 deletions(-) create mode 100644 src/features/matches/types.test.ts create mode 100644 src/features/tournaments/types.test.ts diff --git a/src/features/matches/types.test.ts b/src/features/matches/types.test.ts new file mode 100644 index 0000000..245f506 --- /dev/null +++ b/src/features/matches/types.test.ts @@ -0,0 +1,9 @@ +import { describe, expect, it } from "vitest"; +import { matchInputSchema } from "./types"; + +describe("matchInputSchema.partial()", () => { + it("does not inject keys the caller did not send", () => { + const parsed = matchInputSchema.partial().parse({ home_cups: 7 }); + expect(parsed).toEqual({ home_cups: 7 }); + }); +}); diff --git a/src/features/matches/types.ts b/src/features/matches/types.ts index 76bac52..ae31612 100644 --- a/src/features/matches/types.ts +++ b/src/features/matches/types.ts @@ -42,20 +42,20 @@ export interface Match { export const matchInputSchema = z.object({ order: z.number().int().min(1).optional(), lid: z.number().int().min(1), - reset: z.boolean().optional().default(false), + reset: z.boolean().optional(), round: z.number().int().min(1), - home_cups: z.number().int().min(0).optional().default(0), - away_cups: z.number().int().min(0).optional().default(0), - ot_count: z.number().int().min(0).optional().default(0), + home_cups: z.number().int().min(0).optional(), + away_cups: z.number().int().min(0).optional(), + ot_count: z.number().int().min(0).optional(), start_time: z.iso.datetime().optional(), end_time: z.iso.datetime().optional(), - bye: z.boolean().optional().default(false), + bye: z.boolean().optional(), home_from_lid: z.number().int().min(1).optional(), away_from_lid: z.number().int().min(1).optional(), - home_from_loser: z.boolean().optional().default(false), - away_from_loser: z.boolean().optional().default(false), - is_losers_bracket: z.boolean().optional().default(false), - status: z.enum(["tbd", "ready", "started", "ended"]).optional().default("tbd"), + home_from_loser: z.boolean().optional(), + away_from_loser: z.boolean().optional(), + is_losers_bracket: z.boolean().optional(), + status: z.enum(["tbd", "ready", "started", "ended"]).optional(), tournament: z.string().min(1), home: z.string().min(1).nullable().optional(), away: z.string().min(1).nullable().optional(), diff --git a/src/features/tournaments/types.test.ts b/src/features/tournaments/types.test.ts new file mode 100644 index 0000000..e7f42cd --- /dev/null +++ b/src/features/tournaments/types.test.ts @@ -0,0 +1,15 @@ +import { describe, expect, it } from "vitest"; +import { tournamentInputSchema } from "./types"; + +describe("tournamentInputSchema.partial()", () => { + it("does not inject keys the caller did not send", () => { + const parsed = tournamentInputSchema.partial().parse({ rules: "

rules

" }); + expect(parsed).toEqual({ rules: "

rules

" }); + }); + + it("passes an explicit regional flag through unchanged", () => { + expect(tournamentInputSchema.partial().parse({ regional: true })).toEqual({ + regional: true, + }); + }); +}); diff --git a/src/features/tournaments/types.ts b/src/features/tournaments/types.ts index 435a99b..3fbe025 100644 --- a/src/features/tournaments/types.ts +++ b/src/features/tournaments/types.ts @@ -98,7 +98,7 @@ export const tournamentInputSchema = z.object({ enroll_time: z.string(), start_time: z.string(), end_time: z.string().optional(), - regional: z.boolean().optional().default(false), + regional: z.boolean().optional(), format: z.enum(["single_elim", "double_elim", "groups", "swiss", "swiss_bracket", "round_robin"]).optional(), phase: z.enum(["seeding", "group_stage", "knockout", "completed"]).optional(), group_config: z.object({ -- 2.54.0 From eb3dbe048b5c6acb0e2eac6a273a195379f460ad Mon Sep 17 00:00:00 2001 From: yohlo Date: Tue, 25 Aug 2026 21:54:51 -0700 Subject: [PATCH 05/33] i18n: extract catalogs for telemetry and admin redesign strings --- src/locales/de/messages.po | 623 ++++++++++++++++++++++++++----------- src/locales/en/messages.po | 623 ++++++++++++++++++++++++++----------- src/locales/es/messages.po | 623 ++++++++++++++++++++++++++----------- src/locales/ja/messages.po | 623 ++++++++++++++++++++++++++----------- 4 files changed, 1768 insertions(+), 724 deletions(-) diff --git a/src/locales/de/messages.po b/src/locales/de/messages.po index b011c0d..a7eaae2 100644 --- a/src/locales/de/messages.po +++ b/src/locales/de/messages.po @@ -18,7 +18,7 @@ msgid " (Losers)" msgstr " (Verlierer)" #. placeholder {0}: activeDevice.volume_percent -#: src/features/spotify/components/spotify-sheet.tsx:163 +#: src/features/spotify/components/spotify-sheet.tsx:162 msgid " (Volume: {0}%)" msgstr " (Lautstärke: {0}%)" @@ -26,7 +26,7 @@ msgstr " (Lautstärke: {0}%)" msgid "(713) 867-5309" msgstr "(713) 867-5309" -#: src/features/spotify/components/spotify-sheet.tsx:148 +#: src/features/spotify/components/spotify-sheet.tsx:147 msgid "(Active)" msgstr "(Aktiv)" @@ -38,14 +38,14 @@ msgstr "{0, plural, one {# Bracket in} other {# Brackets in}}" #. placeholder {0}: enrolledPlayers.length #. placeholder {0}: players.length #: src/features/players/components/players-activity-table.tsx:100 -#: src/features/tournaments/components/edit-enrolled-players.tsx:135 +#: src/features/tournaments/components/enrollment/free-agent-section.tsx:158 msgid "{0, plural, one {# player} other {# players}}" msgstr "{0, plural, one {# Spieler} other {# Spieler}}" #. placeholder {0}: enrolledTeams.length #. placeholder {0}: group.teams.length #. placeholder {0}: orderedTeams.length -#: src/features/tournaments/components/edit-enrolled-teams.tsx:154 +#: src/features/tournaments/components/enrollment/team-section.tsx:243 #: src/features/tournaments/components/group-preview.tsx:27 #: src/features/tournaments/components/seed-tournament.tsx:116 #: src/features/tournaments/components/setup-group-stage.tsx:224 @@ -53,10 +53,15 @@ msgid "{0, plural, one {# team} other {# teams}}" msgstr "{0, plural, one {# Team} other {# Teams}}" #. placeholder {0}: result.totalItems -#: src/features/activities/components/activities-table.tsx:341 +#: src/features/activities/components/activities-table.tsx:125 msgid "{0, plural, one {# total activity} other {# total activities}}" msgstr "{0, plural, one {# Aktivität insgesamt} other {# Aktivitäten insgesamt}}" +#. placeholder {0}: result.totalItems +#: src/features/telemetry/components/client-events-table.tsx:87 +msgid "{0, plural, one {# total event} other {# total events}}" +msgstr "" + #. placeholder {0}: selectedConfig.total_group_matches #: src/features/tournaments/components/setup-group-stage.tsx:249 msgid "{0, plural, one {# total group stage match} other {# total group stage matches}}" @@ -73,7 +78,9 @@ msgid "{0, plural, one {# wildcard} other {# wildcards}}" msgstr "{0, plural, one {# Wildcard} other {# Wildcards}}" #. placeholder {0}: freeAgents.length -#: src/app/routes/_authed/admin/tournaments/$id/assign-partners.tsx:126 +#. placeholder {0}: props.count +#: src/app/routes/_authed/admin/tournaments/$id/assign-partners.tsx:124 +#: src/features/tournaments/components/enrollment/enrollment-stats.tsx:22 msgid "{0, plural, one {player enrolled} other {players enrolled}}" msgstr "{0, plural, one {Spieler angemeldet} other {Spieler angemeldet}}" @@ -88,6 +95,11 @@ msgstr "{0} um {1}" msgid "{0} Badge Holders" msgstr "{0} Abzeichen-Inhaber" +#. placeholder {0}: recentErrors.totalItems +#: src/lib/telemetry/alerts.server.ts:104 +msgid "{0} client errors in the last 15 minutes" +msgstr "" + #. placeholder {0}: match.away?.name #. placeholder {0}: match.home?.name #: src/features/bracket/components/match-form.tsx:90 @@ -131,8 +143,9 @@ msgid "{0}d" msgstr "{0}T" #. placeholder {0}: activity.duration -#: src/features/activities/components/activities-table.tsx:83 -#: src/features/activities/components/activities-table.tsx:163 +#. placeholder {0}: selectedActivity.duration +#: src/features/activities/components/activities-table.tsx:148 +#: src/features/activities/components/activities-table.tsx:193 msgid "{0}ms" msgstr "{0}ms" @@ -145,15 +158,22 @@ msgstr "{count, plural, one {Spieler (#)} other {Spieler (#)}}" msgid "{count, plural, one {Teams (#)} other {Teams (#)}}" msgstr "{count, plural, one {Teams (#)} other {Teams (#)}}" +#: src/lib/telemetry/alerts.server.ts:167 +msgid "{deniedCount} denied admin access attempts in the last 15 minutes" +msgstr "" + #: src/features/players/components/players-activity-table.tsx:44 +#: src/features/telemetry/components/shared/relative-time.tsx:17 msgid "{diffDays}d ago" msgstr "vor {diffDays}T" #: src/features/players/components/players-activity-table.tsx:43 +#: src/features/telemetry/components/shared/relative-time.tsx:16 msgid "{diffHours}h ago" msgstr "vor {diffHours}Std" #: src/features/players/components/players-activity-table.tsx:42 +#: src/features/telemetry/components/shared/relative-time.tsx:15 msgid "{diffMins}m ago" msgstr "vor {diffMins}Min" @@ -177,7 +197,11 @@ msgstr "{n, plural, one {# garantiertes Spiel} other {# garantierte Spiele}}" msgid "{n, plural, one {# wildcard} other {# wildcards}}" msgstr "{n, plural, one {# Wildcard} other {# Wildcards}}" -#: src/app/routes/_authed/admin/tournaments/$id/teams.tsx:23 +#: src/lib/telemetry/alerts.server.ts:154 +msgid "{name} failed {count} times in the last 15 minutes" +msgstr "" + +#: src/app/routes/_authed/admin/tournaments/$id/teams.tsx:24 msgid "{name} Teams" msgstr "{name}-Teams" @@ -331,6 +355,10 @@ msgstr "2. Frühere Abgabe" msgid "2nd place" msgstr "2. Platz" +#: src/features/telemetry/components/usage-screen.tsx:256 +msgid "30d" +msgstr "" + #: src/features/teams/components/team-form/duration-picker.tsx:22 msgid "30s" msgstr "30 s" @@ -343,6 +371,10 @@ msgstr "3. Platz" msgid "5s" msgstr "5 s" +#: src/features/telemetry/components/usage-screen.tsx:255 +msgid "7d" +msgstr "" + #. placeholder {0}: number?.slice(0, 3) #. placeholder {1}: number?.slice(3, 6) #. placeholder {2}: number?.slice(6) @@ -350,6 +382,11 @@ msgstr "5 s" msgid "A code was sent to +1 ({0}) {1}-{2}" msgstr "Ein Code wurde an +1 ({0}) {1}-{2} gesendet" +#. placeholder {0}: info.message.slice(0, 120) +#: src/lib/telemetry/alerts.server.ts:125 +msgid "A new error appeared: {0}" +msgstr "" + #: src/features/bracket/components/match-report.tsx:304 msgid "a team" msgstr "ein Team" @@ -370,7 +407,7 @@ msgstr "Akzentfarbe" msgid "Access denied" msgstr "Zugriff verweigert" -#: src/components/DefaultCatchBoundary.tsx:58 +#: src/components/DefaultCatchBoundary.tsx:60 msgid "Access Denied" msgstr "Zugriff verweigert" @@ -379,34 +416,33 @@ msgid "Account created successfully!" msgstr "Konto erfolgreich erstellt!" #. placeholder {0}: activeDevice.name -#: src/features/spotify/components/spotify-sheet.tsx:161 +#: src/features/spotify/components/spotify-sheet.tsx:160 msgid "Active device: {0}" msgstr "Aktives Gerät: {0}" -#: src/app/routes/_authed/admin/activities.tsx:21 -#: src/features/admin/components/admin-page.tsx:90 -msgid "Activities" -msgstr "Aktivitäten" +#: src/features/admin/components/dashboard/usage-card.tsx:39 +msgid "Active players" +msgstr "" -#: src/features/activities/components/activities-table.tsx:120 +#: src/features/activities/components/activities-table.tsx:159 msgid "Activity Details" msgstr "Aktivitätsdetails" -#: src/components/ios-install-prompt.tsx:58 +#: src/components/ios-install-prompt.tsx:63 msgid "Add FLXN to your home screen for a smoother experience" msgstr "Leg FLXN auf deinen Homescreen für ein flüssigeres Erlebnis" -#: src/features/tournaments/components/edit-enrolled-players.tsx:117 +#: src/features/tournaments/components/enrollment/free-agent-section.tsx:130 msgid "Add Player" msgstr "Spieler hinzufügen" -#: src/features/tournaments/components/edit-enrolled-teams.tsx:136 +#: src/features/tournaments/components/enrollment/team-section.tsx:213 msgid "Add Team" msgstr "Team hinzufügen" -#: src/app/routes/_authed/admin.tsx:14 -#: src/app/routes/_authed/admin/index.tsx:9 -#: src/features/core/hooks/use-links.ts:36 +#: src/app/routes/_authed/admin.tsx:15 +#: src/app/routes/_authed/admin/index.tsx:22 +#: src/features/core/hooks/use-links.ts:37 msgid "Admin" msgstr "Admin" @@ -415,7 +451,7 @@ msgid "Aero" msgstr "Aero" #: src/app/routes/_authed/stats.tsx:59 -#: src/features/activities/components/activities-table.tsx:328 +#: src/features/activities/components/activities-table.tsx:92 #: src/features/players/components/profile/index.tsx:28 msgid "All" msgstr "Alle" @@ -432,7 +468,7 @@ msgstr "Alle Gruppenspiele sind beendet! Fülle den K.-o.-Baum, um qualifizierte msgid "ALM" msgstr "ALM" -#: src/components/DefaultCatchBoundary.tsx:92 +#: src/components/DefaultCatchBoundary.tsx:94 msgid "An error occurred while loading this page." msgstr "Beim Laden dieser Seite ist ein Fehler aufgetreten." @@ -448,6 +484,10 @@ msgstr "Ein unerwarteter Fehler ist aufgetreten. Bitte versuche es erneut." msgid "Announce matchup" msgstr "Matchup ansagen" +#: src/features/telemetry/components/client-events-table.tsx:49 +msgid "Anonymous" +msgstr "" + #: src/app/routes/_authed/settings.tsx:35 msgid "Appearance" msgstr "Erscheinungsbild" @@ -456,7 +496,7 @@ msgstr "Erscheinungsbild" msgid "Are you sure you want to unenroll from this tournament? You can enroll again at any point before the deadline." msgstr "Bist du sicher, dass du dich von diesem Turnier abmelden möchtest? Du kannst dich jederzeit vor Ablauf der Frist wieder anmelden." -#: src/features/activities/components/activities-table.tsx:200 +#: src/features/activities/components/activities-table.tsx:221 msgid "Arguments" msgstr "Argumente" @@ -464,7 +504,7 @@ msgstr "Argumente" msgid "Art Deco" msgstr "Art déco" -#: src/features/tournaments/components/manage-tournament.tsx:69 +#: src/features/tournaments/components/enrollment/free-agent-section.tsx:149 msgid "Assign Partners" msgstr "Partner zuweisen" @@ -473,12 +513,12 @@ msgstr "Partner zuweisen" msgid "At least one team must have 10 cups" msgstr "Mindestens ein Team muss 10 Becher haben" -#: src/contexts/spotify-context.tsx:158 +#: src/contexts/spotify-context.tsx:157 msgid "Authentication failed: {error}" msgstr "Authentifizierung fehlgeschlagen: {error}" #. placeholder {0}: decodeURIComponent(details) -#: src/contexts/spotify-context.tsx:157 +#: src/contexts/spotify-context.tsx:156 msgid "Authentication failed: {error} - {0}" msgstr "Authentifizierung fehlgeschlagen: {error} - {0}" @@ -520,15 +560,15 @@ msgstr "Ø Niederlagen-Marge" msgid "Avg Win Margin" msgstr "Ø Sieg-Marge" -#: src/features/admin/components/award-badges.tsx:104 +#: src/features/admin/components/award-badges.tsx:98 msgid "Award Badge" msgstr "Abzeichen verleihen" -#: src/features/admin/components/admin-page.tsx:55 +#: src/features/admin/components/dashboard/tools-list.tsx:54 msgid "Award Badges" msgstr "Abzeichen verleihen" -#: src/features/admin/components/award-badges.tsx:65 +#: src/features/admin/components/award-badges.tsx:59 msgid "Award Manual Badge" msgstr "Abzeichen manuell verleihen" @@ -554,23 +594,23 @@ msgstr "AWM" msgid "Back" msgstr "Zurück" -#: src/features/admin/components/award-badges.tsx:37 +#: src/features/admin/components/award-badges.tsx:33 msgid "Badge awarded to {playerName}" msgstr "Abzeichen an {playerName} verliehen" -#: src/features/admin/components/admin-page.tsx:86 +#: src/features/admin/components/dashboard/tools-list.tsx:85 msgid "Badge recalculation failed. Check the server logs." msgstr "Abzeichen-Neuberechnung fehlgeschlagen. Prüfe die Server-Logs." -#: src/features/admin/components/admin-page.tsx:39 +#: src/features/admin/components/dashboard/tools-list.tsx:38 msgid "Badge recalculation is already running" msgstr "Abzeichen-Neuberechnung läuft bereits" -#: src/features/admin/components/admin-page.tsx:38 +#: src/features/admin/components/dashboard/tools-list.tsx:37 msgid "Badge recalculation started in the background" msgstr "Abzeichen-Neuberechnung im Hintergrund gestartet" -#: src/features/admin/components/award-badges.tsx:70 +#: src/features/admin/components/award-badges.tsx:64 msgid "Badge Type" msgstr "Abzeichentyp" @@ -592,12 +632,12 @@ msgid "Both teams cannot have 10 cups" msgstr "Beide Teams können nicht 10 Becher haben" #: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:106 -#: src/features/tournaments/components/upcoming-tournament/index.tsx:150 +#: src/features/tournaments/components/upcoming-tournament/index.tsx:151 msgid "Bracket" msgstr "Turnierbaum" #: src/app/routes/_authed/admin/preview.tsx:13 -#: src/features/admin/components/admin-page.tsx:102 +#: src/features/admin/components/dashboard/tools-list.tsx:96 msgid "Bracket Preview" msgstr "Turnierbaum-Vorschau" @@ -614,6 +654,10 @@ msgstr "Brutal" msgid "CA" msgstr "CA" +#: src/features/admin/components/dashboard/now-strip.tsx:52 +msgid "Calls today" +msgstr "" + #: src/components/sheet/slide-panel/slide-panel.tsx:41 #: src/components/sheet/slide-panel/slide-panel.tsx:190 #: src/features/bracket/components/match-form.tsx:160 @@ -633,11 +677,12 @@ msgstr "Abbrechen" msgid "Cannot create groups" msgstr "Gruppen können nicht erstellt werden" -#: src/app/routes/_authed/admin/tournaments/$id/assign-partners.tsx:138 +#: src/app/routes/_authed/admin/tournaments/$id/assign-partners.tsx:136 +#: src/features/tournaments/components/enrollment/enrollment-stats.tsx:41 msgid "Cannot create teams with an odd number of players. Please have one player unenroll." msgstr "Teams können nicht mit einer ungeraden Anzahl an Spielern erstellt werden. Bitte lass einen Spieler austragen." -#: src/features/spotify/components/spotify-controls-bar.tsx:144 +#: src/features/spotify/components/spotify-controls-bar.tsx:143 msgid "Capture current state" msgstr "Aktuellen Status erfassen" @@ -659,7 +704,7 @@ msgstr "Champions" msgid "Change Logo" msgstr "Logo ändern" -#: src/features/admin/components/award-badges.tsx:87 +#: src/features/admin/components/award-badges.tsx:81 msgid "Choose a player" msgstr "Wähle einen Spieler" @@ -671,6 +716,18 @@ msgstr "Knete" msgid "Clear selected player" msgstr "Ausgewählten Spieler entfernen" +#: src/lib/telemetry/alerts.server.ts:103 +msgid "Client error spike" +msgstr "" + +#: src/features/admin/components/dashboard/errors-card.tsx:21 +msgid "Client errors" +msgstr "" + +#: src/features/telemetry/components/usage-screen.tsx:203 +msgid "Client errors per day" +msgstr "" + #: src/components/sheet/modal.tsx:35 #: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:169 #: src/features/tournaments/components/upcoming-tournament/rules-list-button.tsx:36 @@ -717,7 +774,7 @@ msgstr "Bestätigen" msgid "Confirm {scoreText}" msgstr "{scoreText} bestätigen" -#: src/app/routes/_authed/admin/tournaments/$id/assign-partners.tsx:175 +#: src/app/routes/_authed/admin/tournaments/$id/assign-partners.tsx:173 msgid "Confirm & Create Teams" msgstr "Bestätigen & Teams erstellen" @@ -738,15 +795,15 @@ msgstr "Einrichtung bestätigen" msgid "Confirm Unenrollment" msgstr "Austragung bestätigen" -#: src/features/spotify/components/spotify-controls-bar.tsx:52 +#: src/features/spotify/components/spotify-controls-bar.tsx:51 msgid "Connect Spotify" msgstr "Spotify verbinden" -#: src/features/spotify/components/spotify-sheet.tsx:87 +#: src/features/spotify/components/spotify-sheet.tsx:86 msgid "Connect with Spotify" msgstr "Mit Spotify verbinden" -#: src/features/spotify/components/spotify-sheet.tsx:75 +#: src/features/spotify/components/spotify-sheet.tsx:74 msgid "Connect your Spotify account" msgstr "Verbinde dein Spotify-Konto" @@ -796,7 +853,8 @@ msgstr "Team erstellen" msgid "Create Tournament" msgstr "Turnier erstellen" -#: src/features/activities/components/activities-table.tsx:168 +#: src/features/activities/components/activities-table.tsx:198 +#: src/features/telemetry/components/client-events-table.tsx:149 msgid "Created" msgstr "Erstellt" @@ -825,6 +883,15 @@ msgstr "Becher erzielt" msgid "Custom Hex Code" msgstr "Benutzerdefinierter Hex-Code" +#: src/features/admin/components/dashboard/usage-card.tsx:41 +#: src/features/telemetry/components/usage-screen.tsx:159 +msgid "Daily active players" +msgstr "" + +#: src/features/admin/components/dashboard/usage-card.tsx:47 +msgid "Daily page views" +msgstr "" + #: src/features/settings/components/color-scheme-picker.tsx:39 msgid "Dark" msgstr "Dunkel" @@ -833,7 +900,7 @@ msgstr "Dunkel" msgid "Dark Academia" msgstr "Dark Academia" -#: src/features/activities/components/activities-table.tsx:356 +#: src/features/activities/components/activities-table.tsx:119 msgid "Date" msgstr "Datum" @@ -845,6 +912,10 @@ msgstr "Tiefsee" msgid "Default" msgstr "Standard" +#: src/lib/telemetry/alerts.server.ts:166 +msgid "Denied access attempt" +msgstr "" + #: src/features/tournaments/components/tournament-form.tsx:150 msgid "Description" msgstr "Beschreibung" @@ -853,11 +924,11 @@ msgstr "Beschreibung" msgid "deviceId is required for transfer action" msgstr "deviceId ist für die Übertragungsaktion erforderlich" -#: src/features/spotify/components/spotify-sheet.tsx:188 +#: src/features/spotify/components/spotify-sheet.tsx:187 msgid "Disconnect Spotify" msgstr "Spotify trennen" -#: src/components/ios-install-prompt.tsx:65 +#: src/components/ios-install-prompt.tsx:70 msgid "Dismiss" msgstr "Schließen" @@ -871,28 +942,25 @@ msgstr "Sieht das richtig aus?" msgid "Downstream matches were reset because the correction changed who advances." msgstr "Nachfolgende Matches wurden zurückgesetzt, da sich durch die Korrektur geändert hat, wer weiterkommt." -#: src/features/activities/components/activities-table.tsx:161 -#: src/features/activities/components/activities-table.tsx:372 +#: src/features/activities/components/activities-table.tsx:120 +#: src/features/activities/components/activities-table.tsx:190 msgid "Duration" msgstr "Dauer" +#: src/features/telemetry/components/usage-screen.tsx:233 +msgid "Duration p50 / p95" +msgstr "" + #: src/features/tournaments/components/tournament-stats.tsx:176 msgid "Earlier regional formats aren't supported in the app and order of matches or displayed winners may be unreliable." msgstr "Frühere regionale Formate werden in der App nicht unterstützt. Die Reihenfolge der Matches oder angezeigten Gewinner kann unzuverlässig sein." #. placeholder {0}: team.name #: src/features/teams/components/manage-teams.tsx:35 +#: src/features/tournaments/components/enrollment/team-section.tsx:96 msgid "Edit {0}" msgstr "{0} bearbeiten" -#: src/features/tournaments/components/manage-tournament.tsx:117 -msgid "Edit Enrolled Teams" -msgstr "Angemeldete Teams bearbeiten" - -#: src/features/tournaments/components/manage-tournament.tsx:63 -msgid "Edit Enrollments" -msgstr "Anmeldungen bearbeiten" - #: src/features/bracket/components/match-card.tsx:392 msgid "Edit Match" msgstr "Match bearbeiten" @@ -906,17 +974,18 @@ msgstr "Spielstand bearbeiten" msgid "Edit Prediction" msgstr "Tipp bearbeiten" -#: src/features/tournaments/components/manage-tournament.tsx:58 -#: src/features/tournaments/components/manage-tournament.tsx:109 +#: src/features/tournaments/components/manage-tournament.tsx:65 +#: src/features/tournaments/components/manage-tournament.tsx:92 msgid "Edit Rules" msgstr "Regeln bearbeiten" #: src/features/teams/components/manage-teams.tsx:35 +#: src/features/tournaments/components/enrollment/team-section.tsx:96 msgid "Edit Team" msgstr "Team bearbeiten" -#: src/features/tournaments/components/manage-tournament.tsx:53 -#: src/features/tournaments/components/manage-tournament.tsx:89 +#: src/features/tournaments/components/manage-tournament.tsx:60 +#: src/features/tournaments/components/manage-tournament.tsx:72 msgid "Edit Tournament" msgstr "Turnier bearbeiten" @@ -940,6 +1009,10 @@ msgstr "Enddatum (optional)" msgid "End Match" msgstr "Match beenden" +#: src/features/telemetry/components/usage-screen.tsx:156 +msgid "Engagement" +msgstr "" + #: src/features/tournaments/components/upcoming-tournament/enroll-free-agent.tsx:26 msgid "Enroll" msgstr "Anmelden" @@ -968,12 +1041,13 @@ msgstr "Dein Team anmelden" msgid "Enrolled" msgstr "Angemeldet" -#: src/features/tournaments/components/edit-enrolled-players.tsx:132 +#: src/features/tournaments/components/enrollment/free-agent-section.tsx:155 #: src/features/tournaments/components/upcoming-tournament/enrolled-players-list-button.tsx:39 msgid "Enrolled Players" msgstr "Angemeldete Spieler" -#: src/features/tournaments/components/edit-enrolled-teams.tsx:151 +#: src/features/tournaments/components/enrollment/enrollment-stats.tsx:28 +#: src/features/tournaments/components/enrollment/team-section.tsx:240 #: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:145 #: src/features/tournaments/components/upcoming-tournament/team-list-button.tsx:31 msgid "Enrolled Teams" @@ -983,16 +1057,22 @@ msgstr "Angemeldete Teams" msgid "Enrolling as a free agent adds you to a pool of players looking for teammates." msgstr "Als Free Agent kommst du in einen Pool von Spielern, die noch einen Teampartner suchen." +#: src/app/routes/_authed/admin/tournaments/$id/enrollment.tsx:33 +#: src/features/admin/components/dashboard/tournament-card.tsx:61 #: src/features/tournaments/components/upcoming-tournament/enroll-free-agent.tsx:29 -#: src/features/tournaments/components/upcoming-tournament/index.tsx:68 +#: src/features/tournaments/components/upcoming-tournament/index.tsx:69 msgid "Enrollment" msgstr "Anmeldung" +#: src/features/tournaments/components/manage-tournament.tsx:55 +msgid "Enrollment & Teams" +msgstr "" + #: src/features/tournaments/components/tournament-form.tsx:196 msgid "Enrollment Due" msgstr "Anmeldeschluss" -#: src/features/tournaments/components/upcoming-tournament/index.tsx:80 +#: src/features/tournaments/components/upcoming-tournament/index.tsx:81 msgid "Enrollment has been closed for this tournament." msgstr "Die Anmeldung für dieses Turnier ist geschlossen." @@ -1013,14 +1093,39 @@ msgstr "Gib deinen Namen ein" msgid "Enter your phone number" msgstr "Gib deine Telefonnummer ein" -#: src/features/activities/components/activities-table.tsx:187 +#: src/features/telemetry/components/error-browser/error-group-row.tsx:29 +msgid "Error group reopened" +msgstr "" + +#: src/features/telemetry/components/error-browser/error-group-row.tsx:29 +msgid "Error group resolved" +msgstr "" + +#: src/features/activities/components/activities-table.tsx:207 msgid "Error Message" msgstr "Fehlermeldung" -#: src/components/DefaultCatchBoundary.tsx:96 +#: src/components/DefaultCatchBoundary.tsx:98 msgid "Error: {errorMessage}" msgstr "Fehler: {errorMessage}" +#: src/app/routes/_authed/admin/telemetry.tsx:51 +#: src/features/telemetry/components/usage-screen.tsx:200 +msgid "Errors" +msgstr "" + +#: src/features/telemetry/components/client-events-table.tsx:113 +msgid "Event Details" +msgstr "" + +#: src/features/telemetry/components/client-events-table.tsx:70 +msgid "Event name" +msgstr "" + +#: src/app/routes/_authed/admin/telemetry.tsx:48 +msgid "Events" +msgstr "" + #: src/features/tournaments/components/group-stage-view.tsx:368 msgid "Expand standings" msgstr "Tabelle erweitern" @@ -1029,16 +1134,12 @@ msgstr "Tabelle erweitern" msgid "eyes" msgstr "Augen" -#: src/features/activities/components/activities-table.tsx:145 +#: src/features/activities/components/activities-table.tsx:181 msgid "Failed" msgstr "Fehlgeschlagen" -#: src/features/admin/components/award-badges.tsx:41 -msgid "Failed to award badge" -msgstr "Abzeichen konnte nicht vergeben werden" - #: src/app/routes/api/spotify/capture.ts:54 -#: src/contexts/spotify-context.tsx:427 +#: src/contexts/spotify-context.tsx:426 msgid "Failed to capture playback state" msgstr "Wiedergabestatus konnte nicht erfasst werden" @@ -1054,7 +1155,7 @@ msgstr "Team konnte nicht erstellt werden" msgid "Failed to create tournament" msgstr "Turnier konnte nicht erstellt werden" -#: src/contexts/spotify-context.tsx:335 +#: src/contexts/spotify-context.tsx:334 msgid "Failed to get devices" msgstr "Geräte konnten nicht abgerufen werden" @@ -1068,19 +1169,19 @@ msgid "Failed to load team" msgstr "Team konnte nicht geladen werden" #: src/app/routes/api/spotify/resume.ts:54 -#: src/contexts/spotify-context.tsx:447 +#: src/contexts/spotify-context.tsx:446 msgid "Failed to resume playback state" msgstr "Wiedergabestatus konnte nicht fortgesetzt werden" -#: src/contexts/spotify-context.tsx:360 +#: src/contexts/spotify-context.tsx:359 msgid "Failed to set active device" msgstr "Aktives Gerät konnte nicht festgelegt werden" -#: src/contexts/spotify-context.tsx:314 +#: src/contexts/spotify-context.tsx:313 msgid "Failed to set volume" msgstr "Lautstärke konnte nicht festgelegt werden" -#: src/features/admin/components/admin-page.tsx:42 +#: src/features/admin/components/dashboard/tools-list.tsx:41 msgid "Failed to start badge recalculation" msgstr "Abzeichen-Neuberechnung konnte nicht gestartet werden" @@ -1092,19 +1193,19 @@ msgstr "Team konnte nicht aktualisiert werden" msgid "Failed to update tournament" msgstr "Turnier konnte nicht aktualisiert werden" -#: src/app/routes/api/teams/upload-logo.ts:140 -#: src/app/routes/api/tournaments/upload-logo.ts:138 +#: src/app/routes/api/teams/upload-logo.ts:141 +#: src/app/routes/api/tournaments/upload-logo.ts:139 #: src/features/teams/components/team-form/index.tsx:148 #: src/features/tournaments/components/tournament-form.tsx:88 msgid "Failed to upload logo" msgstr "Logo konnte nicht hochgeladen werden" -#: src/features/activities/components/activities-table.tsx:330 +#: src/features/activities/components/activities-table.tsx:94 msgid "Failure" msgstr "Fehler" -#: src/app/routes/api/teams/upload-logo.ts:79 -#: src/app/routes/api/tournaments/upload-logo.ts:80 +#: src/app/routes/api/teams/upload-logo.ts:80 +#: src/app/routes/api/tournaments/upload-logo.ts:81 msgid "File too large. Maximum size is 10MB." msgstr "Datei zu groß. Maximale Größe ist 10 MB." @@ -1112,7 +1213,7 @@ msgstr "Datei zu groß. Maximale Größe ist 10 MB." msgid "Final" msgstr "Finale" -#: src/features/tournaments/components/started-tournament/index.tsx:114 +#: src/features/tournaments/components/started-tournament/index.tsx:115 msgid "Final Results" msgstr "Endergebnisse" @@ -1150,11 +1251,19 @@ msgstr "Free-Agent-Anmeldung" msgid "Free Agents" msgstr "Free Agents" -#: src/features/activities/components/activities-table.tsx:124 +#: src/features/telemetry/components/usage-screen.tsx:226 +msgid "Function" +msgstr "" + +#: src/features/activities/components/activities-table.tsx:165 msgid "Function Name" msgstr "Funktionsname" -#: src/app/routes/_authed/admin/tournaments/$id/assign-partners.tsx:148 +#: src/app/routes/_authed/admin/telemetry.tsx:45 +msgid "Functions" +msgstr "" + +#: src/app/routes/_authed/admin/tournaments/$id/assign-partners.tsx:146 msgid "Generate Random Pairings" msgstr "Zufällige Paarungen generieren" @@ -1163,11 +1272,15 @@ msgstr "Zufällige Paarungen generieren" msgid "Go back" msgstr "Zurück" -#: src/components/DefaultCatchBoundary.tsx:67 -#: src/components/DefaultCatchBoundary.tsx:132 +#: src/components/DefaultCatchBoundary.tsx:69 +#: src/components/DefaultCatchBoundary.tsx:134 msgid "Go Back" msgstr "Zurück" +#: src/features/telemetry/components/usage-screen.tsx:54 +msgid "Good" +msgstr "" + #: src/themes/seventies/manifest.ts:6 msgid "Groovy" msgstr "Groovy" @@ -1229,13 +1342,13 @@ msgstr "Hast du schon mal mitgespielt?" msgid "Head to Head" msgstr "Direktvergleich" -#: src/components/DefaultCatchBoundary.tsx:103 +#: src/components/DefaultCatchBoundary.tsx:105 msgid "Hide details" msgstr "Details ausblenden" -#: src/components/DefaultCatchBoundary.tsx:74 -#: src/components/DefaultCatchBoundary.tsx:125 -#: src/features/core/hooks/use-links.ts:11 +#: src/components/DefaultCatchBoundary.tsx:76 +#: src/components/DefaultCatchBoundary.tsx:127 +#: src/features/core/hooks/use-links.ts:12 msgid "Home" msgstr "Start" @@ -1263,7 +1376,7 @@ msgstr "So funktioniert die Tipp-Wertung" #. placeholder {0}: response.status #. placeholder {1}: response.statusText -#: src/contexts/spotify-context.tsx:65 +#: src/contexts/spotify-context.tsx:64 msgid "HTTP {0}: {1}" msgstr "HTTP {0}: {1}" @@ -1275,6 +1388,10 @@ msgstr "Wenn du zum ersten Mal dabei bist, wähle <0>Neuer Spieler, andernfa msgid "Important: Enrolling as a free agent does not guarantee a tournament spot. To secure a spot, one team member must register through the app and select a walkout song." msgstr "Wichtig: Die Anmeldung als Free Agent garantiert keinen Turnierplatz. Um dir einen Platz zu sichern, muss sich ein Teammitglied über die App registrieren und einen Walkout-Song auswählen." +#: src/features/telemetry/components/error-browser/error-browser.tsx:56 +msgid "Include resolved" +msgstr "" + #: src/features/login/components/code-prompt.tsx:43 msgid "Incorrect code" msgstr "Falscher Code" @@ -1283,8 +1400,8 @@ msgstr "Falscher Code" msgid "Invalid action" msgstr "Ungültige Aktion" -#: src/app/routes/api/teams/upload-logo.ts:65 -#: src/app/routes/api/tournaments/upload-logo.ts:66 +#: src/app/routes/api/teams/upload-logo.ts:66 +#: src/app/routes/api/tournaments/upload-logo.ts:67 msgid "Invalid file type. Only JPEG, PNG and GIF are allowed." msgstr "Ungültiger Dateityp. Nur JPEG, PNG und GIF sind erlaubt." @@ -1292,8 +1409,8 @@ msgstr "Ungültiger Dateityp. Nur JPEG, PNG und GIF sind erlaubt." msgid "Invalid hex color format" msgstr "Ungültiges Hex-Farbformat" -#: src/app/routes/api/teams/upload-logo.ts:33 -#: src/app/routes/api/tournaments/upload-logo.ts:34 +#: src/app/routes/api/teams/upload-logo.ts:34 +#: src/app/routes/api/tournaments/upload-logo.ts:35 msgid "Invalid input" msgstr "Ungültige Eingabe" @@ -1302,10 +1419,11 @@ msgid "Invalid request" msgstr "Ungültige Anfrage" #: src/features/players/components/players-activity-table.tsx:41 +#: src/features/telemetry/components/shared/relative-time.tsx:14 msgid "Just now" msgstr "Gerade eben" -#: src/app/routes/_authed/admin/tournaments/run.$id.tsx:166 +#: src/app/routes/_authed/admin/tournaments/run.$id.tsx:107 msgid "Knockout Bracket" msgstr "K.-o.-Runde" @@ -1347,7 +1465,7 @@ msgid "Last name must be 3-20 characters long and contain only letters" msgstr "Der Nachname muss 3-20 Zeichen lang sein und darf nur Buchstaben enthalten" #. placeholder {0}: migrationStatus.result.totalBadgesEarned -#: src/features/admin/components/admin-page.tsx:74 +#: src/features/admin/components/dashboard/tools-list.tsx:73 msgid "Last recalculation: {0, plural, one {# badge earned} other {# badges earned}}" msgstr "Letzte Neuberechnung: {0, plural, one {# Abzeichen verdient} other {# Abzeichen verdient}}" @@ -1359,7 +1477,7 @@ msgstr "Hell" msgid "lip" msgstr "Lippen" -#: src/features/tournaments/components/started-tournament/index.tsx:121 +#: src/features/tournaments/components/started-tournament/index.tsx:122 msgid "Live Matches" msgstr "Live-Matches" @@ -1381,8 +1499,8 @@ msgstr "Ort ist erforderlich" msgid "Logo" msgstr "Logo" -#: src/app/routes/api/teams/upload-logo.ts:46 -#: src/app/routes/api/tournaments/upload-logo.ts:47 +#: src/app/routes/api/teams/upload-logo.ts:47 +#: src/app/routes/api/tournaments/upload-logo.ts:48 msgid "Logo file is required" msgstr "Logo-Datei ist erforderlich" @@ -1420,26 +1538,24 @@ msgstr "Tipp abgeben" msgid "Make Your Prediction" msgstr "Gib deinen Tipp ab" +#: src/features/admin/components/dashboard/tournament-card.tsx:68 #: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:89 -#: src/features/tournaments/components/upcoming-tournament/index.tsx:143 +#: src/features/tournaments/components/upcoming-tournament/index.tsx:144 msgid "Manage" msgstr "Verwalten" -#: src/app/routes/_authed/admin/tournaments/$id/assign-partners.tsx:28 +#: src/app/routes/_authed/admin/tournaments/$id/assign-partners.tsx:29 #: src/app/routes/_authed/admin/tournaments/$id/index.tsx:23 msgid "Manage {name}" msgstr "{name} verwalten" -#: src/features/tournaments/components/manage-tournament.tsx:117 -msgid "Manage Enrollments" -msgstr "Anmeldungen verwalten" - -#: src/features/tournaments/components/manage-tournament.tsx:76 +#: src/features/tournaments/components/enrollment/team-section.tsx:234 msgid "Manage Team Songs/Logos" msgstr "Team-Songs/Logos verwalten" #: src/app/routes/_authed/admin/tournaments/index.tsx:17 -#: src/features/admin/components/admin-page.tsx:50 +#: src/features/admin/components/dashboard/tools-list.tsx:49 +#: src/features/admin/components/dashboard/tournament-card.tsx:25 msgid "Manage Tournaments" msgstr "Turniere verwalten" @@ -1450,6 +1566,10 @@ msgstr "Turniere verwalten" msgid "margin" msgstr "Differenz" +#: src/features/telemetry/components/error-browser/error-group-row.tsx:98 +msgid "Mark resolved" +msgstr "" + #. placeholder {0}: match.order #. placeholder {1}: match.round + 1 #: src/features/bracket/components/match-dock.tsx:126 @@ -1514,11 +1634,15 @@ msgstr "Matchup" msgid "Matchup details" msgstr "Matchup-Details" +#: src/features/telemetry/components/usage-screen.tsx:169 +msgid "MAU" +msgstr "" + #: src/features/players/components/league-head-to-head.tsx:120 msgid "Me" msgstr "Ich" -#: src/features/admin/components/admin-page.tsx:60 +#: src/features/admin/components/dashboard/tools-list.tsx:59 msgid "Migrate Badge Progress" msgstr "Abzeichen-Fortschritt migrieren" @@ -1532,6 +1656,7 @@ msgid "MMR Calculation:" msgstr "MMR-Berechnung:" #: src/features/teams/components/team-form/index.tsx:200 +#: src/features/telemetry/components/client-events-table.tsx:119 #: src/features/tournaments/components/tournament-form.tsx:137 msgid "Name" msgstr "Name" @@ -1545,7 +1670,8 @@ msgstr "Name ist erforderlich" msgid "Name updated successfully!" msgstr "Name erfolgreich aktualisiert!" -#: src/app/routes/_authed/admin/tournaments/$id/assign-partners.tsx:132 +#: src/app/routes/_authed/admin/tournaments/$id/assign-partners.tsx:130 +#: src/features/tournaments/components/enrollment/enrollment-stats.tsx:35 msgid "Need at least 2 players to create teams" msgstr "Mindestens 2 Spieler nötig, um Teams zu erstellen" @@ -1554,6 +1680,10 @@ msgstr "Mindestens 2 Spieler nötig, um Teams zu erstellen" msgid "Need at least 4 teams to create a group stage format. Current team count: {0}" msgstr "Mindestens 4 Teams nötig, um eine Gruppenphase zu erstellen. Aktuelle Teamanzahl: {0}" +#: src/features/telemetry/components/usage-screen.tsx:56 +msgid "Needs improvement" +msgstr "" + #: src/features/reactions/components/emoji-picker.tsx:23 msgid "nerd" msgstr "Nerd" @@ -1567,6 +1697,7 @@ msgid "Neumorphic" msgstr "Neumorph" #: src/features/players/components/players-activity-table.tsx:27 +#: src/features/telemetry/components/shared/relative-time.tsx:6 msgid "Never" msgstr "Nie" @@ -1574,6 +1705,10 @@ msgstr "Nie" msgid "Never active" msgstr "Nie aktiv" +#: src/lib/telemetry/alerts.server.ts:124 +msgid "New client error" +msgstr "" + #: src/features/login/components/player-prompt/new-player-button.tsx:18 msgid "New Player" msgstr "Neuer Spieler" @@ -1582,7 +1717,7 @@ msgstr "Neuer Spieler" msgid "Next match" msgstr "Nächstes Match" -#: src/features/spotify/components/spotify-controls-bar.tsx:130 +#: src/features/spotify/components/spotify-controls-bar.tsx:129 msgid "Next track" msgstr "Nächster Titel" @@ -1597,11 +1732,11 @@ msgstr "Kein Zugriffstoken gefunden" msgid "No active device found. Please select a device first." msgstr "Kein aktives Gerät gefunden. Bitte wähle zuerst ein Gerät aus." -#: src/features/spotify/components/spotify-controls-bar.tsx:204 +#: src/features/spotify/components/spotify-controls-bar.tsx:203 msgid "No active device. Please select a device in settings." msgstr "Kein aktives Gerät. Bitte wähle ein Gerät in den Einstellungen aus." -#: src/features/tournaments/components/started-tournament/index.tsx:142 +#: src/features/tournaments/components/started-tournament/index.tsx:143 msgid "No active matches" msgstr "Keine aktiven Matches" @@ -1609,18 +1744,26 @@ msgstr "Keine aktiven Matches" msgid "No active playback to capture" msgstr "Keine aktive Wiedergabe zum Erfassen" -#: src/features/activities/components/activities-table.tsx:234 +#: src/features/activities/components/activities-table.tsx:132 msgid "No Activities Found" msgstr "Keine Aktivitäten gefunden" +#: src/features/admin/components/dashboard/activity-feed.tsx:78 +msgid "No activity yet" +msgstr "" + #: src/features/badges/components/badge-stats-table.tsx:45 msgid "No Badges Available" msgstr "Keine Abzeichen verfügbar" -#: src/features/spotify/components/spotify-sheet.tsx:155 +#: src/features/spotify/components/spotify-sheet.tsx:154 msgid "No devices found." msgstr "Keine Geräte gefunden." +#: src/features/telemetry/components/client-events-table.tsx:94 +msgid "No Events Found" +msgstr "" + #: src/features/tournaments/components/group-stage-view.tsx:294 msgid "No groups have been created yet" msgstr "Es wurden noch keine Gruppen erstellt" @@ -1637,7 +1780,7 @@ msgstr "Keine Spieleraktivität gefunden" msgid "No players available" msgstr "Keine Spieler verfügbar" -#: src/features/tournaments/components/edit-enrolled-players.tsx:145 +#: src/features/tournaments/components/enrollment/free-agent-section.tsx:168 #: src/features/tournaments/components/upcoming-tournament/enrolled-players-list-button.tsx:42 msgid "No players enrolled yet" msgstr "Noch keine Spieler angemeldet" @@ -1668,7 +1811,7 @@ msgstr "Kein Snapshot angegeben" msgid "No song selected" msgstr "Kein Song ausgewählt" -#: src/features/spotify/components/spotify-controls-bar.tsx:157 +#: src/features/spotify/components/spotify-controls-bar.tsx:156 msgid "No state captured" msgstr "Kein Status erfasst" @@ -1689,7 +1832,7 @@ msgstr "Keine Teams zugewiesen" msgid "No Teams Enrolled" msgstr "Keine Teams angemeldet" -#: src/features/tournaments/components/edit-enrolled-teams.tsx:164 +#: src/features/tournaments/components/enrollment/team-section.tsx:253 msgid "No teams enrolled yet" msgstr "Noch keine Teams angemeldet" @@ -1701,10 +1844,19 @@ msgstr "Keine Teams gefunden, die zu \"{search}\" passen" msgid "No tournaments found" msgstr "Keine Turniere gefunden" +#: src/features/admin/components/dashboard/tournament-card.tsx:18 +msgid "No tournaments yet" +msgstr "" + #: src/features/tournaments/components/tournament-card-list.tsx:40 msgid "No Tournaments Yet" msgstr "Noch keine Turniere" +#: src/features/admin/components/dashboard/errors-card.tsx:53 +#: src/features/telemetry/components/error-browser/error-browser.tsx:27 +msgid "No unresolved errors" +msgstr "" + #: src/features/predictions/components/prediction-leaderboard.tsx:136 msgid "Nobody made a prediction for this tournament." msgstr "Niemand hat einen Tipp für dieses Turnier abgegeben." @@ -1717,6 +1869,10 @@ msgstr "Stimmt nicht" msgid "Not right — enter our score" msgstr "Stimmt nicht — unser Ergebnis eingeben" +#: src/features/telemetry/components/error-browser/error-browser.tsx:28 +msgid "Nothing has broken in the last 30 days. Keep it up." +msgstr "" + #: src/features/settings/components/notifications-section.tsx:18 #: src/features/settings/components/notifications-section.tsx:59 msgid "Notifications" @@ -1738,7 +1894,7 @@ msgstr "Benachrichtigungen auf diesem Gerät deaktiviert" msgid "Notifications enabled on this device" msgstr "Benachrichtigungen auf diesem Gerät aktiviert" -#: src/features/spotify/components/spotify-sheet.tsx:96 +#: src/features/spotify/components/spotify-sheet.tsx:95 msgid "Now Playing" msgstr "Läuft gerade" @@ -1758,7 +1914,15 @@ msgstr "Sobald du angemeldet bist, kannst du andere Free Agents und ihre Telefon msgid "One time code" msgstr "Einmalcode" -#: src/features/admin/components/admin-page.tsx:95 +#: src/features/admin/components/dashboard/now-strip.tsx:44 +msgid "Online now" +msgstr "" + +#: src/features/admin/components/dashboard/now-strip.tsx:46 +msgid "Open errors" +msgstr "" + +#: src/features/admin/components/dashboard/tools-list.tsx:89 msgid "Open Pocketbase" msgstr "Pocketbase öffnen" @@ -1767,7 +1931,7 @@ msgid "Opponent" msgstr "Gegner" #: src/features/tournaments/components/upcoming-tournament/enroll-team/team-selection-view.tsx:60 -#: src/features/tournaments/components/upcoming-tournament/index.tsx:92 +#: src/features/tournaments/components/upcoming-tournament/index.tsx:93 msgid "or" msgstr "oder" @@ -1800,7 +1964,23 @@ msgstr "Übersicht" msgid "owo" msgstr "owo" -#: src/app/routes/_authed/admin/tournaments/$id/assign-partners.tsx:157 +#: src/features/telemetry/components/usage-screen.tsx:97 +msgid "p50" +msgstr "" + +#: src/features/telemetry/components/usage-screen.tsx:113 +msgid "p95" +msgstr "" + +#: src/features/admin/components/dashboard/usage-card.tsx:45 +msgid "Page views" +msgstr "" + +#: src/features/telemetry/components/usage-screen.tsx:177 +msgid "Page views by route" +msgstr "" + +#: src/app/routes/_authed/admin/tournaments/$id/assign-partners.tsx:155 msgid "Partner Assignments" msgstr "Partner-Zuweisungen" @@ -1808,7 +1988,11 @@ msgstr "Partner-Zuweisungen" msgid "Partners will be randomly assigned when enrollment closes" msgstr "Partner werden nach Anmeldeschluss zufällig zugewiesen" -#: src/features/spotify/components/spotify-controls-bar.tsx:117 +#: src/features/telemetry/components/client-events-table.tsx:122 +msgid "Path" +msgstr "" + +#: src/features/spotify/components/spotify-controls-bar.tsx:116 msgid "Pause" msgstr "Pause" @@ -1839,7 +2023,7 @@ msgstr "Wähle zuerst die früheren Spiele — diese Teams stehen noch nicht fes msgid "PICKS" msgstr "TIPPS" -#: src/features/spotify/components/spotify-controls-bar.tsx:117 +#: src/features/spotify/components/spotify-controls-bar.tsx:116 msgid "Play" msgstr "Abspielen" @@ -1847,23 +2031,22 @@ msgstr "Abspielen" msgid "Playback control failed" msgstr "Wiedergabesteuerung fehlgeschlagen" -#: src/features/activities/components/activities-table.tsx:154 -#: src/features/admin/components/award-badges.tsx:35 +#: src/features/activities/components/activities-table.tsx:98 +#: src/features/activities/components/activities-table.tsx:188 +#: src/features/admin/components/award-badges.tsx:31 +#: src/features/telemetry/components/client-events-table.tsx:73 +#: src/features/telemetry/components/client-events-table.tsx:120 msgid "Player" msgstr "Spieler" -#: src/features/teams/components/team-form/players-picker.tsx:45 +#: src/features/teams/components/team-form/players-picker.tsx:46 msgid "Player 1" msgstr "Spieler 1" -#: src/features/teams/components/team-form/players-picker.tsx:53 +#: src/features/teams/components/team-form/players-picker.tsx:54 msgid "Player 2" msgstr "Spieler 2" -#: src/app/routes/_authed/admin/activities.tsx:59 -msgid "Player Activity" -msgstr "Spieleraktivität" - #: src/features/login/components/player-prompt/index.tsx:46 msgid "Player already exists" msgstr "Spieler existiert bereits" @@ -1880,6 +2063,7 @@ msgstr "Spieler erfolgreich entfernt" msgid "Player Stats" msgstr "Spielerstatistiken" +#: src/app/routes/_authed/admin/telemetry.tsx:54 #: src/features/teams/components/team-profile/index.tsx:29 msgid "Players" msgstr "Spieler" @@ -1889,10 +2073,14 @@ msgid "Players are required" msgstr "Spieler sind erforderlich" #. placeholder {0}: activeDevice.name -#: src/features/spotify/components/spotify-controls-bar.tsx:175 +#: src/features/spotify/components/spotify-controls-bar.tsx:174 msgid "Playing on {0}" msgstr "Wird auf {0} abgespielt" +#: src/features/telemetry/components/usage-screen.tsx:58 +msgid "Poor" +msgstr "" + #: src/features/tournaments/components/group-stage-view.tsx:314 msgid "Populate Knockout Bracket" msgstr "K.-o.-Runde befüllen" @@ -1919,11 +2107,11 @@ msgstr "Tipps sind offen" msgid "Previous match" msgstr "Vorheriges Spiel" -#: src/features/spotify/components/spotify-controls-bar.tsx:105 +#: src/features/spotify/components/spotify-controls-bar.tsx:104 msgid "Previous track" msgstr "Vorheriger Titel" -#: src/features/core/hooks/use-links.ts:27 +#: src/features/core/hooks/use-links.ts:28 msgid "Profile" msgstr "Profil" @@ -1931,6 +2119,10 @@ msgstr "Profil" msgid "Progress " msgstr "Fortschritt " +#: src/features/telemetry/components/client-events-table.tsx:136 +msgid "Props" +msgstr "" + #: src/app/routes/_authed/tournaments/$id.predictions_.$playerId.tsx:106 #: src/features/predictions/components/prediction-leaderboard.tsx:261 msgid "PTS" @@ -1944,7 +2136,7 @@ msgstr "Q" msgid "Quest Log" msgstr "Questlog" -#: src/app/routes/_authed/admin/tournaments/$id/assign-partners.tsx:167 +#: src/app/routes/_authed/admin/tournaments/$id/assign-partners.tsx:165 msgid "Re-roll" msgstr "Neu würfeln" @@ -1954,10 +2146,14 @@ msgstr "Reaktionen" #. placeholder {0}: migrationStatus.playersProcessed #. placeholder {1}: migrationStatus.totalPlayers -#: src/features/admin/components/admin-page.tsx:67 +#: src/features/admin/components/dashboard/tools-list.tsx:66 msgid "Recalculating badges in the background ({0}/{1})" msgstr "Abzeichen werden im Hintergrund neu berechnet ({0}/{1})" +#: src/features/admin/components/dashboard/activity-feed.tsx:34 +msgid "Recent activity" +msgstr "" + #: src/features/tournaments/components/started-tournament/sections/recent-results.tsx:34 msgid "Recently finished" msgstr "Kürzlich beendet" @@ -1972,6 +2168,10 @@ msgstr "Regional" msgid "Regional Tournament" msgstr "Regionalturnier" +#: src/features/telemetry/components/error-browser/error-group-row.tsx:98 +msgid "Reopen" +msgstr "" + #: src/features/bracket/components/match-report.tsx:127 msgid "Report cleared" msgstr "Meldung gelöscht" @@ -1981,11 +2181,15 @@ msgstr "Meldung gelöscht" msgid "Report Score" msgstr "Ergebnis melden" -#: src/contexts/spotify-context.tsx:60 +#: src/contexts/spotify-context.tsx:59 msgid "Request failed" msgstr "Anfrage fehlgeschlagen" -#: src/features/spotify/components/spotify-controls-bar.tsx:157 +#: src/features/telemetry/components/error-browser/error-group-row.tsx:63 +msgid "Resolved" +msgstr "" + +#: src/features/spotify/components/spotify-controls-bar.tsx:156 msgid "Restore captured state" msgstr "Erfassten Status wiederherstellen" @@ -1993,7 +2197,7 @@ msgstr "Erfassten Status wiederherstellen" msgid "Results" msgstr "Ergebnisse" -#: src/components/DefaultCatchBoundary.tsx:117 +#: src/components/DefaultCatchBoundary.tsx:119 msgid "Retry" msgstr "Erneut versuchen" @@ -2010,16 +2214,21 @@ msgstr "rofl" msgid "Round {0}" msgstr "Runde {0}" +#: src/features/telemetry/components/client-events-table.tsx:125 +msgid "Route" +msgstr "" + #: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:142 #: src/features/tournaments/components/upcoming-tournament/rules-list-button.tsx:23 msgid "Rules" msgstr "Regeln" -#: src/app/routes/_authed/admin/tournaments/run.$id.tsx:38 +#: src/app/routes/_authed/admin/tournaments/run.$id.tsx:36 msgid "Run {name}" msgstr "{name} ausführen" -#: src/features/tournaments/components/manage-tournament.tsx:82 +#: src/features/admin/components/dashboard/tournament-card.tsx:53 +#: src/features/tournaments/components/manage-tournament.tsx:49 msgid "Run Tournament" msgstr "Turnier starten" @@ -2053,7 +2262,7 @@ msgstr "Schrei" msgid "Search for a Song" msgstr "Nach einem Song suchen" -#: src/features/tournaments/components/edit-enrolled-players.tsx:120 +#: src/features/tournaments/components/enrollment/free-agent-section.tsx:133 msgid "Search for players to enroll..." msgstr "Spieler zum Anmelden suchen..." @@ -2061,7 +2270,7 @@ msgstr "Spieler zum Anmelden suchen..." msgid "Search for songs..." msgstr "Nach Songs suchen..." -#: src/features/tournaments/components/edit-enrolled-teams.tsx:139 +#: src/features/tournaments/components/enrollment/team-section.tsx:216 msgid "Search for teams to enroll..." msgstr "Teams zum Anmelden suchen..." @@ -2095,11 +2304,11 @@ msgstr "Segment darf höchstens 15 Sekunden lang sein" msgid "Segment must be within song duration" msgstr "Segment muss innerhalb der Songdauer liegen" -#: src/features/admin/components/award-badges.tsx:71 +#: src/features/admin/components/award-badges.tsx:65 msgid "Select a badge" msgstr "Abzeichen auswählen" -#: src/features/spotify/components/spotify-sheet.tsx:145 +#: src/features/spotify/components/spotify-sheet.tsx:144 msgid "Select a device..." msgstr "Gerät auswählen..." @@ -2121,7 +2330,7 @@ msgstr "Uhrzeit auswählen" msgid "Select Color" msgstr "Farbe auswählen" -#: src/features/spotify/components/spotify-sheet.tsx:127 +#: src/features/spotify/components/spotify-sheet.tsx:126 msgid "Select Device" msgstr "Gerät auswählen" @@ -2141,7 +2350,7 @@ msgstr "Anmeldeschluss auswählen" msgid "Select one of your existing teams" msgstr "Wähle eines deiner bestehenden Teams" -#: src/features/admin/components/award-badges.tsx:86 +#: src/features/admin/components/award-badges.tsx:80 msgid "Select Player" msgstr "Spieler auswählen" @@ -2174,20 +2383,32 @@ msgstr "Testbenachrichtigung senden" msgid "Series is tied" msgstr "Serie steht unentschieden" -#: src/app/routes/_authed/admin/activities.tsx:58 -msgid "Server Functions" -msgstr "Serverfunktionen" +#: src/lib/telemetry/alerts.server.ts:153 +msgid "Server function failing" +msgstr "" -#: src/features/activities/components/activities-table.tsx:309 +#: src/features/telemetry/components/usage-screen.tsx:186 +msgid "Server functions" +msgstr "" + +#: src/features/activities/components/activities-table.tsx:82 msgid "serverFn name" msgstr "serverFn-Name" +#: src/features/telemetry/components/client-events-table.tsx:128 +msgid "Session" +msgstr "" + +#: src/features/telemetry/components/usage-screen.tsx:172 +msgid "Sessions" +msgstr "" + #: src/app/routes/_authed/settings.tsx:16 #: src/features/core/components/settings-button.tsx:46 msgid "Settings" msgstr "Einstellungen" -#: src/components/DefaultCatchBoundary.tsx:103 +#: src/components/DefaultCatchBoundary.tsx:105 msgid "Show details" msgstr "Details anzeigen" @@ -2213,7 +2434,11 @@ msgstr "Skizzenbuch" msgid "Skeuomorphic" msgstr "Skeuomorph" -#: src/components/DefaultCatchBoundary.tsx:89 +#: src/features/activities/components/activities-table.tsx:113 +msgid "Slow >1s (this page)" +msgstr "" + +#: src/components/DefaultCatchBoundary.tsx:91 msgid "Something went wrong" msgstr "Etwas ist schiefgelaufen" @@ -2241,8 +2466,8 @@ msgstr "Song-Segment darf höchstens 15 Sekunden lang sein" msgid "Song start time is required" msgstr "Startzeit des Songs ist erforderlich" -#: src/features/activities/components/activities-table.tsx:345 #: src/features/players/components/player-stats-table.tsx:340 +#: src/features/telemetry/components/shared/search-filter-bar.tsx:102 msgid "Sort:" msgstr "Sortieren:" @@ -2250,7 +2475,7 @@ msgstr "Sortieren:" msgid "Sorted by win percentage" msgstr "Sortiert nach Gewinnquote" -#: src/features/spotify/components/spotify-sheet.tsx:68 +#: src/features/spotify/components/spotify-sheet.tsx:67 msgid "Spotify Controls" msgstr "Spotify-Steuerung" @@ -2262,7 +2487,7 @@ msgstr "Für die Wiedergabesteuerung ist Spotify Premium erforderlich." msgid "Spotify premium required" msgstr "Spotify Premium erforderlich" -#: src/features/spotify/components/spotify-controls-bar.tsx:180 +#: src/features/spotify/components/spotify-controls-bar.tsx:179 msgid "Spotify settings" msgstr "Spotify-Einstellungen" @@ -2292,6 +2517,10 @@ msgstr "Startzeit ist erforderlich" msgid "Start typing to search..." msgstr "Tippen, um zu suchen …" +#: src/features/admin/components/dashboard/tournament-card.tsx:43 +msgid "Starts in" +msgstr "" + #: src/features/players/components/player-stats-table.tsx:372 msgid "Stat abbreviations and MMR info" msgstr "Stat-Abkürzungen und MMR-Infos" @@ -2300,7 +2529,7 @@ msgstr "Stat-Abkürzungen und MMR-Infos" msgid "Stat Abbreviations:" msgstr "Stat-Abkürzungen:" -#: src/features/core/hooks/use-links.ts:16 +#: src/features/core/hooks/use-links.ts:17 #: src/features/players/components/profile/index.tsx:26 #: src/features/teams/components/team-profile/index.tsx:34 msgid "Statistics" @@ -2315,8 +2544,8 @@ msgstr "Stats" msgid "Stats Comparison" msgstr "Stats-Vergleich" -#: src/features/activities/components/activities-table.tsx:131 -#: src/features/activities/components/activities-table.tsx:321 +#: src/features/activities/components/activities-table.tsx:85 +#: src/features/activities/components/activities-table.tsx:167 msgid "Status" msgstr "Status" @@ -2328,8 +2557,8 @@ msgstr "Status" msgid "Submit" msgstr "Absenden" -#: src/features/activities/components/activities-table.tsx:138 -#: src/features/activities/components/activities-table.tsx:329 +#: src/features/activities/components/activities-table.tsx:93 +#: src/features/activities/components/activities-table.tsx:174 msgid "Success" msgstr "Erfolg" @@ -2337,8 +2566,7 @@ msgstr "Erfolg" msgid "Successfully logged in. Welcome back!" msgstr "Erfolgreich angemeldet. Willkommen zurück!" -#: src/features/activities/components/activities-table.tsx:42 -#: src/features/activities/components/activities-table.tsx:112 +#: src/features/activities/components/activities-table.tsx:53 #: src/features/settings/components/color-scheme-picker.tsx:47 msgid "System" msgstr "System" @@ -2351,11 +2579,11 @@ msgstr "T" msgid "Tap an opponent below to compare" msgstr "Tippe unten auf einen Gegner zum Vergleichen" -#: src/components/ios-install-prompt.tsx:43 +#: src/components/ios-install-prompt.tsx:48 msgid "Tap Menu (⋮) → Add to Home screen" msgstr "Tippe auf Menü (⋮) → Zum Home-Bildschirm hinzufügen" -#: src/components/ios-install-prompt.tsx:42 +#: src/components/ios-install-prompt.tsx:47 msgid "Tap Share → Add to Home Screen" msgstr "Tippe auf Teilen → Zum Home-Bildschirm hinzufügen" @@ -2385,7 +2613,7 @@ msgstr "Team-Details" msgid "Team enrolled successfully!" msgstr "Team erfolgreich angemeldet!" -#: src/app/routes/api/teams/upload-logo.ts:92 +#: src/app/routes/api/teams/upload-logo.ts:93 #: src/features/teams/components/team-profile/index.tsx:21 msgid "Team not found" msgstr "Team nicht gefunden" @@ -2419,11 +2647,15 @@ msgstr "Teams" msgid "Teams created successfully!" msgstr "Teams erfolgreich erstellt!" +#: src/app/routes/_authed/admin/telemetry.tsx:20 +msgid "Telemetry" +msgstr "" + #: src/themes/terminal/manifest.ts:6 msgid "Terminal" msgstr "Terminal" -#: src/app/routes/api/push/test.ts:29 +#: src/app/routes/api/push/test.ts:30 msgid "Test notification — push is working on this device." msgstr "Test-Benachrichtigung — Push funktioniert auf diesem Gerät." @@ -2498,6 +2730,10 @@ msgstr "Die besten {0, plural, one {# Team} other {# Teams}} aus jeder Gruppe ko msgid "Top {n} advance" msgstr "Top {n} kommen weiter" +#: src/features/telemetry/components/usage-screen.tsx:191 +msgid "Top functions by call count" +msgstr "" + #: src/features/matches/components/team-head-to-head-sheet.tsx:158 #: src/features/players/components/player-head-to-head-sheet.tsx:193 msgid "Total Cups" @@ -2517,7 +2753,7 @@ msgstr "Turnier erstellt, aber Logo-Upload fehlgeschlagen: {0}" msgid "Tournament created successfully!" msgstr "Turnier erfolgreich erstellt!" -#: src/app/routes/api/tournaments/upload-logo.ts:93 +#: src/app/routes/api/tournaments/upload-logo.ts:94 msgid "Tournament not found" msgstr "Turnier nicht gefunden" @@ -2536,7 +2772,7 @@ msgid "Tournament updated successfully!" msgstr "Turnier erfolgreich aktualisiert!" #: src/app/routes/_authed/tournaments/index.tsx:17 -#: src/features/core/hooks/use-links.ts:21 +#: src/features/core/hooks/use-links.ts:22 #: src/features/teams/components/team-profile/index.tsx:53 #: src/features/teams/components/team-profile/skeleton.tsx:80 msgid "Tournaments" @@ -2550,6 +2786,10 @@ msgstr "trackId ist für die playTrack-Aktion erforderlich" msgid "Try the new themes" msgstr "Probier die neuen Designs aus" +#: src/features/admin/components/dashboard/quiet-boundary.tsx:26 +msgid "Unavailable right now" +msgstr "" + #: src/features/tournaments/components/upcoming-tournament/unenroll-free-agent.tsx:26 #: src/features/tournaments/components/upcoming-tournament/unenroll-team.tsx:34 msgid "Unenroll" @@ -2563,12 +2803,12 @@ msgstr "Vom Turnier abmelden?" msgid "Unenroll Team" msgstr "Team abmelden" -#: src/components/DefaultCatchBoundary.tsx:39 +#: src/components/DefaultCatchBoundary.tsx:40 msgid "Unknown error" msgstr "Unbekannter Fehler" -#: src/app/routes/api/teams/upload-logo.ts:141 -#: src/app/routes/api/tournaments/upload-logo.ts:139 +#: src/app/routes/api/teams/upload-logo.ts:142 +#: src/app/routes/api/tournaments/upload-logo.ts:140 msgid "Unknown error occurred" msgstr "Unbekannter Fehler aufgetreten" @@ -2585,7 +2825,7 @@ msgstr "Unbekannter Spieler" msgid "Unnamed Player" msgstr "Unbenannter Spieler" -#: src/features/tournaments/components/started-tournament/index.tsx:128 +#: src/features/tournaments/components/started-tournament/index.tsx:129 msgid "Upcoming" msgstr "Bevorstehend" @@ -2607,10 +2847,23 @@ msgstr "Team aktualisieren" msgid "Update Tournament" msgstr "Turnier aktualisieren" -#: src/features/activities/components/activities-table.tsx:176 +#: src/app/routes/_authed/admin/telemetry.tsx:57 +msgid "Usage" +msgstr "" + +#: src/features/admin/components/dashboard/usage-card.tsx:34 +msgid "Usage, 7 days" +msgstr "" + +#: src/features/activities/components/activities-table.tsx:202 +#: src/features/telemetry/components/client-events-table.tsx:146 msgid "User Agent" msgstr "User-Agent" +#: src/features/telemetry/components/client-events-table.tsx:131 +msgid "Value" +msgstr "" + #: src/features/bracket/components/match-card.tsx:201 #: src/features/bracket/components/match-card.tsx:226 msgid "versus" @@ -2645,6 +2898,10 @@ msgstr "Tipps ansehen" msgid "Viewport" msgstr "Viewport" +#: src/features/telemetry/components/usage-screen.tsx:211 +msgid "Vitals" +msgstr "" + #: src/app/routes/api/spotify/playback.ts:70 msgid "volumePercent must be a number" msgstr "volumePercent muss eine Zahl sein" @@ -2680,6 +2937,10 @@ msgstr "Walkout-Segment ({segmentDuration}s)" msgid "Walkout Song" msgstr "Walkout-Song" +#: src/features/telemetry/components/usage-screen.tsx:165 +msgid "WAU" +msgstr "" + #: src/features/tournaments/components/group-stage-view.tsx:407 msgid "WC" msgstr "WC" @@ -2731,7 +2992,7 @@ msgstr "Du und 1 weitere" msgid "You are not authorized to perform this action" msgstr "Du bist nicht berechtigt, diese Aktion durchzuführen" -#: src/components/DefaultCatchBoundary.tsx:60 +#: src/components/DefaultCatchBoundary.tsx:62 msgid "You don't have permission to access this page." msgstr "Du hast keine Berechtigung, auf diese Seite zuzugreifen." @@ -2755,7 +3016,7 @@ msgstr "Du bist der erste Free Agent!" msgid "You've been added as a free agent!" msgstr "Du wurdest als Free Agent hinzugefügt!" -#: src/components/DefaultCatchBoundary.tsx:46 +#: src/components/DefaultCatchBoundary.tsx:48 msgid "You've been logged out" msgstr "Du wurdest abgemeldet" diff --git a/src/locales/en/messages.po b/src/locales/en/messages.po index 0cdc7bf..59df585 100644 --- a/src/locales/en/messages.po +++ b/src/locales/en/messages.po @@ -18,7 +18,7 @@ msgid " (Losers)" msgstr " (Losers)" #. placeholder {0}: activeDevice.volume_percent -#: src/features/spotify/components/spotify-sheet.tsx:163 +#: src/features/spotify/components/spotify-sheet.tsx:162 msgid " (Volume: {0}%)" msgstr " (Volume: {0}%)" @@ -26,7 +26,7 @@ msgstr " (Volume: {0}%)" msgid "(713) 867-5309" msgstr "(713) 867-5309" -#: src/features/spotify/components/spotify-sheet.tsx:148 +#: src/features/spotify/components/spotify-sheet.tsx:147 msgid "(Active)" msgstr "(Active)" @@ -38,14 +38,14 @@ msgstr "{0, plural, one {# bracket in} other {# brackets in}}" #. placeholder {0}: enrolledPlayers.length #. placeholder {0}: players.length #: src/features/players/components/players-activity-table.tsx:100 -#: src/features/tournaments/components/edit-enrolled-players.tsx:135 +#: src/features/tournaments/components/enrollment/free-agent-section.tsx:158 msgid "{0, plural, one {# player} other {# players}}" msgstr "{0, plural, one {# player} other {# players}}" #. placeholder {0}: enrolledTeams.length #. placeholder {0}: group.teams.length #. placeholder {0}: orderedTeams.length -#: src/features/tournaments/components/edit-enrolled-teams.tsx:154 +#: src/features/tournaments/components/enrollment/team-section.tsx:243 #: src/features/tournaments/components/group-preview.tsx:27 #: src/features/tournaments/components/seed-tournament.tsx:116 #: src/features/tournaments/components/setup-group-stage.tsx:224 @@ -53,10 +53,15 @@ msgid "{0, plural, one {# team} other {# teams}}" msgstr "{0, plural, one {# team} other {# teams}}" #. placeholder {0}: result.totalItems -#: src/features/activities/components/activities-table.tsx:341 +#: src/features/activities/components/activities-table.tsx:125 msgid "{0, plural, one {# total activity} other {# total activities}}" msgstr "{0, plural, one {# total activity} other {# total activities}}" +#. placeholder {0}: result.totalItems +#: src/features/telemetry/components/client-events-table.tsx:87 +msgid "{0, plural, one {# total event} other {# total events}}" +msgstr "{0, plural, one {# total event} other {# total events}}" + #. placeholder {0}: selectedConfig.total_group_matches #: src/features/tournaments/components/setup-group-stage.tsx:249 msgid "{0, plural, one {# total group stage match} other {# total group stage matches}}" @@ -73,7 +78,9 @@ msgid "{0, plural, one {# wildcard} other {# wildcards}}" msgstr "{0, plural, one {# wildcard} other {# wildcards}}" #. placeholder {0}: freeAgents.length -#: src/app/routes/_authed/admin/tournaments/$id/assign-partners.tsx:126 +#. placeholder {0}: props.count +#: src/app/routes/_authed/admin/tournaments/$id/assign-partners.tsx:124 +#: src/features/tournaments/components/enrollment/enrollment-stats.tsx:22 msgid "{0, plural, one {player enrolled} other {players enrolled}}" msgstr "{0, plural, one {player enrolled} other {players enrolled}}" @@ -88,6 +95,11 @@ msgstr "{0} at {1}" msgid "{0} Badge Holders" msgstr "{0} Badge Holders" +#. placeholder {0}: recentErrors.totalItems +#: src/lib/telemetry/alerts.server.ts:104 +msgid "{0} client errors in the last 15 minutes" +msgstr "{0} client errors in the last 15 minutes" + #. placeholder {0}: match.away?.name #. placeholder {0}: match.home?.name #: src/features/bracket/components/match-form.tsx:90 @@ -131,8 +143,9 @@ msgid "{0}d" msgstr "{0}d" #. placeholder {0}: activity.duration -#: src/features/activities/components/activities-table.tsx:83 -#: src/features/activities/components/activities-table.tsx:163 +#. placeholder {0}: selectedActivity.duration +#: src/features/activities/components/activities-table.tsx:148 +#: src/features/activities/components/activities-table.tsx:193 msgid "{0}ms" msgstr "{0}ms" @@ -145,15 +158,22 @@ msgstr "{count, plural, one {Players (#)} other {Players (#)}}" msgid "{count, plural, one {Teams (#)} other {Teams (#)}}" msgstr "{count, plural, one {Teams (#)} other {Teams (#)}}" +#: src/lib/telemetry/alerts.server.ts:167 +msgid "{deniedCount} denied admin access attempts in the last 15 minutes" +msgstr "{deniedCount} denied admin access attempts in the last 15 minutes" + #: src/features/players/components/players-activity-table.tsx:44 +#: src/features/telemetry/components/shared/relative-time.tsx:17 msgid "{diffDays}d ago" msgstr "{diffDays}d ago" #: src/features/players/components/players-activity-table.tsx:43 +#: src/features/telemetry/components/shared/relative-time.tsx:16 msgid "{diffHours}h ago" msgstr "{diffHours}h ago" #: src/features/players/components/players-activity-table.tsx:42 +#: src/features/telemetry/components/shared/relative-time.tsx:15 msgid "{diffMins}m ago" msgstr "{diffMins}m ago" @@ -177,7 +197,11 @@ msgstr "{n, plural, one {# match guaranteed} other {# matches guaranteed}}" msgid "{n, plural, one {# wildcard} other {# wildcards}}" msgstr "{n, plural, one {# wildcard} other {# wildcards}}" -#: src/app/routes/_authed/admin/tournaments/$id/teams.tsx:23 +#: src/lib/telemetry/alerts.server.ts:154 +msgid "{name} failed {count} times in the last 15 minutes" +msgstr "{name} failed {count} times in the last 15 minutes" + +#: src/app/routes/_authed/admin/tournaments/$id/teams.tsx:24 msgid "{name} Teams" msgstr "{name} Teams" @@ -331,6 +355,10 @@ msgstr "2. Earlier submission" msgid "2nd place" msgstr "2nd place" +#: src/features/telemetry/components/usage-screen.tsx:256 +msgid "30d" +msgstr "30d" + #: src/features/teams/components/team-form/duration-picker.tsx:22 msgid "30s" msgstr "30s" @@ -343,6 +371,10 @@ msgstr "3rd place" msgid "5s" msgstr "5s" +#: src/features/telemetry/components/usage-screen.tsx:255 +msgid "7d" +msgstr "7d" + #. placeholder {0}: number?.slice(0, 3) #. placeholder {1}: number?.slice(3, 6) #. placeholder {2}: number?.slice(6) @@ -350,6 +382,11 @@ msgstr "5s" msgid "A code was sent to +1 ({0}) {1}-{2}" msgstr "A code was sent to +1 ({0}) {1}-{2}" +#. placeholder {0}: info.message.slice(0, 120) +#: src/lib/telemetry/alerts.server.ts:125 +msgid "A new error appeared: {0}" +msgstr "A new error appeared: {0}" + #: src/features/bracket/components/match-report.tsx:304 msgid "a team" msgstr "a team" @@ -370,7 +407,7 @@ msgstr "Accent Color" msgid "Access denied" msgstr "Access denied" -#: src/components/DefaultCatchBoundary.tsx:58 +#: src/components/DefaultCatchBoundary.tsx:60 msgid "Access Denied" msgstr "Access Denied" @@ -379,34 +416,33 @@ msgid "Account created successfully!" msgstr "Account created successfully!" #. placeholder {0}: activeDevice.name -#: src/features/spotify/components/spotify-sheet.tsx:161 +#: src/features/spotify/components/spotify-sheet.tsx:160 msgid "Active device: {0}" msgstr "Active device: {0}" -#: src/app/routes/_authed/admin/activities.tsx:21 -#: src/features/admin/components/admin-page.tsx:90 -msgid "Activities" -msgstr "Activities" +#: src/features/admin/components/dashboard/usage-card.tsx:39 +msgid "Active players" +msgstr "Active players" -#: src/features/activities/components/activities-table.tsx:120 +#: src/features/activities/components/activities-table.tsx:159 msgid "Activity Details" msgstr "Activity Details" -#: src/components/ios-install-prompt.tsx:58 +#: src/components/ios-install-prompt.tsx:63 msgid "Add FLXN to your home screen for a smoother experience" msgstr "Add FLXN to your home screen for a smoother experience" -#: src/features/tournaments/components/edit-enrolled-players.tsx:117 +#: src/features/tournaments/components/enrollment/free-agent-section.tsx:130 msgid "Add Player" msgstr "Add Player" -#: src/features/tournaments/components/edit-enrolled-teams.tsx:136 +#: src/features/tournaments/components/enrollment/team-section.tsx:213 msgid "Add Team" msgstr "Add Team" -#: src/app/routes/_authed/admin.tsx:14 -#: src/app/routes/_authed/admin/index.tsx:9 -#: src/features/core/hooks/use-links.ts:36 +#: src/app/routes/_authed/admin.tsx:15 +#: src/app/routes/_authed/admin/index.tsx:22 +#: src/features/core/hooks/use-links.ts:37 msgid "Admin" msgstr "Admin" @@ -415,7 +451,7 @@ msgid "Aero" msgstr "Aero" #: src/app/routes/_authed/stats.tsx:59 -#: src/features/activities/components/activities-table.tsx:328 +#: src/features/activities/components/activities-table.tsx:92 #: src/features/players/components/profile/index.tsx:28 msgid "All" msgstr "All" @@ -432,7 +468,7 @@ msgstr "All group matches are finished! Populate the knockout bracket to advance msgid "ALM" msgstr "ALM" -#: src/components/DefaultCatchBoundary.tsx:92 +#: src/components/DefaultCatchBoundary.tsx:94 msgid "An error occurred while loading this page." msgstr "An error occurred while loading this page." @@ -448,6 +484,10 @@ msgstr "An unexpected error occurred. Please try again." msgid "Announce matchup" msgstr "Announce matchup" +#: src/features/telemetry/components/client-events-table.tsx:49 +msgid "Anonymous" +msgstr "Anonymous" + #: src/app/routes/_authed/settings.tsx:35 msgid "Appearance" msgstr "Appearance" @@ -456,7 +496,7 @@ msgstr "Appearance" msgid "Are you sure you want to unenroll from this tournament? You can enroll again at any point before the deadline." msgstr "Are you sure you want to unenroll from this tournament? You can enroll again at any point before the deadline." -#: src/features/activities/components/activities-table.tsx:200 +#: src/features/activities/components/activities-table.tsx:221 msgid "Arguments" msgstr "Arguments" @@ -464,7 +504,7 @@ msgstr "Arguments" msgid "Art Deco" msgstr "Art Deco" -#: src/features/tournaments/components/manage-tournament.tsx:69 +#: src/features/tournaments/components/enrollment/free-agent-section.tsx:149 msgid "Assign Partners" msgstr "Assign Partners" @@ -473,12 +513,12 @@ msgstr "Assign Partners" msgid "At least one team must have 10 cups" msgstr "At least one team must have 10 cups" -#: src/contexts/spotify-context.tsx:158 +#: src/contexts/spotify-context.tsx:157 msgid "Authentication failed: {error}" msgstr "Authentication failed: {error}" #. placeholder {0}: decodeURIComponent(details) -#: src/contexts/spotify-context.tsx:157 +#: src/contexts/spotify-context.tsx:156 msgid "Authentication failed: {error} - {0}" msgstr "Authentication failed: {error} - {0}" @@ -520,15 +560,15 @@ msgstr "Avg Loss Margin" msgid "Avg Win Margin" msgstr "Avg Win Margin" -#: src/features/admin/components/award-badges.tsx:104 +#: src/features/admin/components/award-badges.tsx:98 msgid "Award Badge" msgstr "Award Badge" -#: src/features/admin/components/admin-page.tsx:55 +#: src/features/admin/components/dashboard/tools-list.tsx:54 msgid "Award Badges" msgstr "Award Badges" -#: src/features/admin/components/award-badges.tsx:65 +#: src/features/admin/components/award-badges.tsx:59 msgid "Award Manual Badge" msgstr "Award Manual Badge" @@ -554,23 +594,23 @@ msgstr "AWM" msgid "Back" msgstr "Back" -#: src/features/admin/components/award-badges.tsx:37 +#: src/features/admin/components/award-badges.tsx:33 msgid "Badge awarded to {playerName}" msgstr "Badge awarded to {playerName}" -#: src/features/admin/components/admin-page.tsx:86 +#: src/features/admin/components/dashboard/tools-list.tsx:85 msgid "Badge recalculation failed. Check the server logs." msgstr "Badge recalculation failed. Check the server logs." -#: src/features/admin/components/admin-page.tsx:39 +#: src/features/admin/components/dashboard/tools-list.tsx:38 msgid "Badge recalculation is already running" msgstr "Badge recalculation is already running" -#: src/features/admin/components/admin-page.tsx:38 +#: src/features/admin/components/dashboard/tools-list.tsx:37 msgid "Badge recalculation started in the background" msgstr "Badge recalculation started in the background" -#: src/features/admin/components/award-badges.tsx:70 +#: src/features/admin/components/award-badges.tsx:64 msgid "Badge Type" msgstr "Badge Type" @@ -592,12 +632,12 @@ msgid "Both teams cannot have 10 cups" msgstr "Both teams cannot have 10 cups" #: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:106 -#: src/features/tournaments/components/upcoming-tournament/index.tsx:150 +#: src/features/tournaments/components/upcoming-tournament/index.tsx:151 msgid "Bracket" msgstr "Bracket" #: src/app/routes/_authed/admin/preview.tsx:13 -#: src/features/admin/components/admin-page.tsx:102 +#: src/features/admin/components/dashboard/tools-list.tsx:96 msgid "Bracket Preview" msgstr "Bracket Preview" @@ -614,6 +654,10 @@ msgstr "Brutal" msgid "CA" msgstr "CA" +#: src/features/admin/components/dashboard/now-strip.tsx:52 +msgid "Calls today" +msgstr "Calls today" + #: src/components/sheet/slide-panel/slide-panel.tsx:41 #: src/components/sheet/slide-panel/slide-panel.tsx:190 #: src/features/bracket/components/match-form.tsx:160 @@ -633,11 +677,12 @@ msgstr "Cancel" msgid "Cannot create groups" msgstr "Cannot create groups" -#: src/app/routes/_authed/admin/tournaments/$id/assign-partners.tsx:138 +#: src/app/routes/_authed/admin/tournaments/$id/assign-partners.tsx:136 +#: src/features/tournaments/components/enrollment/enrollment-stats.tsx:41 msgid "Cannot create teams with an odd number of players. Please have one player unenroll." msgstr "Cannot create teams with an odd number of players. Please have one player unenroll." -#: src/features/spotify/components/spotify-controls-bar.tsx:144 +#: src/features/spotify/components/spotify-controls-bar.tsx:143 msgid "Capture current state" msgstr "Capture current state" @@ -659,7 +704,7 @@ msgstr "Champions" msgid "Change Logo" msgstr "Change Logo" -#: src/features/admin/components/award-badges.tsx:87 +#: src/features/admin/components/award-badges.tsx:81 msgid "Choose a player" msgstr "Choose a player" @@ -671,6 +716,18 @@ msgstr "Claymation" msgid "Clear selected player" msgstr "Clear selected player" +#: src/lib/telemetry/alerts.server.ts:103 +msgid "Client error spike" +msgstr "Client error spike" + +#: src/features/admin/components/dashboard/errors-card.tsx:21 +msgid "Client errors" +msgstr "Client errors" + +#: src/features/telemetry/components/usage-screen.tsx:203 +msgid "Client errors per day" +msgstr "Client errors per day" + #: src/components/sheet/modal.tsx:35 #: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:169 #: src/features/tournaments/components/upcoming-tournament/rules-list-button.tsx:36 @@ -717,7 +774,7 @@ msgstr "Confirm" msgid "Confirm {scoreText}" msgstr "Confirm {scoreText}" -#: src/app/routes/_authed/admin/tournaments/$id/assign-partners.tsx:175 +#: src/app/routes/_authed/admin/tournaments/$id/assign-partners.tsx:173 msgid "Confirm & Create Teams" msgstr "Confirm & Create Teams" @@ -738,15 +795,15 @@ msgstr "Confirm Setup" msgid "Confirm Unenrollment" msgstr "Confirm Unenrollment" -#: src/features/spotify/components/spotify-controls-bar.tsx:52 +#: src/features/spotify/components/spotify-controls-bar.tsx:51 msgid "Connect Spotify" msgstr "Connect Spotify" -#: src/features/spotify/components/spotify-sheet.tsx:87 +#: src/features/spotify/components/spotify-sheet.tsx:86 msgid "Connect with Spotify" msgstr "Connect with Spotify" -#: src/features/spotify/components/spotify-sheet.tsx:75 +#: src/features/spotify/components/spotify-sheet.tsx:74 msgid "Connect your Spotify account" msgstr "Connect your Spotify account" @@ -796,7 +853,8 @@ msgstr "Create Team" msgid "Create Tournament" msgstr "Create Tournament" -#: src/features/activities/components/activities-table.tsx:168 +#: src/features/activities/components/activities-table.tsx:198 +#: src/features/telemetry/components/client-events-table.tsx:149 msgid "Created" msgstr "Created" @@ -825,6 +883,15 @@ msgstr "Cups Made" msgid "Custom Hex Code" msgstr "Custom Hex Code" +#: src/features/admin/components/dashboard/usage-card.tsx:41 +#: src/features/telemetry/components/usage-screen.tsx:159 +msgid "Daily active players" +msgstr "Daily active players" + +#: src/features/admin/components/dashboard/usage-card.tsx:47 +msgid "Daily page views" +msgstr "Daily page views" + #: src/features/settings/components/color-scheme-picker.tsx:39 msgid "Dark" msgstr "Dark" @@ -833,7 +900,7 @@ msgstr "Dark" msgid "Dark Academia" msgstr "Dark Academia" -#: src/features/activities/components/activities-table.tsx:356 +#: src/features/activities/components/activities-table.tsx:119 msgid "Date" msgstr "Date" @@ -845,6 +912,10 @@ msgstr "Deep Sea" msgid "Default" msgstr "Default" +#: src/lib/telemetry/alerts.server.ts:166 +msgid "Denied access attempt" +msgstr "Denied access attempt" + #: src/features/tournaments/components/tournament-form.tsx:150 msgid "Description" msgstr "Description" @@ -853,11 +924,11 @@ msgstr "Description" msgid "deviceId is required for transfer action" msgstr "deviceId is required for transfer action" -#: src/features/spotify/components/spotify-sheet.tsx:188 +#: src/features/spotify/components/spotify-sheet.tsx:187 msgid "Disconnect Spotify" msgstr "Disconnect Spotify" -#: src/components/ios-install-prompt.tsx:65 +#: src/components/ios-install-prompt.tsx:70 msgid "Dismiss" msgstr "Dismiss" @@ -871,28 +942,25 @@ msgstr "Does this look right?" msgid "Downstream matches were reset because the correction changed who advances." msgstr "Downstream matches were reset because the correction changed who advances." -#: src/features/activities/components/activities-table.tsx:161 -#: src/features/activities/components/activities-table.tsx:372 +#: src/features/activities/components/activities-table.tsx:120 +#: src/features/activities/components/activities-table.tsx:190 msgid "Duration" msgstr "Duration" +#: src/features/telemetry/components/usage-screen.tsx:233 +msgid "Duration p50 / p95" +msgstr "Duration p50 / p95" + #: src/features/tournaments/components/tournament-stats.tsx:176 msgid "Earlier regional formats aren't supported in the app and order of matches or displayed winners may be unreliable." msgstr "Earlier regional formats aren't supported in the app and order of matches or displayed winners may be unreliable." #. placeholder {0}: team.name #: src/features/teams/components/manage-teams.tsx:35 +#: src/features/tournaments/components/enrollment/team-section.tsx:96 msgid "Edit {0}" msgstr "Edit {0}" -#: src/features/tournaments/components/manage-tournament.tsx:117 -msgid "Edit Enrolled Teams" -msgstr "Edit Enrolled Teams" - -#: src/features/tournaments/components/manage-tournament.tsx:63 -msgid "Edit Enrollments" -msgstr "Edit Enrollments" - #: src/features/bracket/components/match-card.tsx:392 msgid "Edit Match" msgstr "Edit Match" @@ -906,17 +974,18 @@ msgstr "Edit match score" msgid "Edit Prediction" msgstr "Edit Prediction" -#: src/features/tournaments/components/manage-tournament.tsx:58 -#: src/features/tournaments/components/manage-tournament.tsx:109 +#: src/features/tournaments/components/manage-tournament.tsx:65 +#: src/features/tournaments/components/manage-tournament.tsx:92 msgid "Edit Rules" msgstr "Edit Rules" #: src/features/teams/components/manage-teams.tsx:35 +#: src/features/tournaments/components/enrollment/team-section.tsx:96 msgid "Edit Team" msgstr "Edit Team" -#: src/features/tournaments/components/manage-tournament.tsx:53 -#: src/features/tournaments/components/manage-tournament.tsx:89 +#: src/features/tournaments/components/manage-tournament.tsx:60 +#: src/features/tournaments/components/manage-tournament.tsx:72 msgid "Edit Tournament" msgstr "Edit Tournament" @@ -940,6 +1009,10 @@ msgstr "End Date (Optional)" msgid "End Match" msgstr "End Match" +#: src/features/telemetry/components/usage-screen.tsx:156 +msgid "Engagement" +msgstr "Engagement" + #: src/features/tournaments/components/upcoming-tournament/enroll-free-agent.tsx:26 msgid "Enroll" msgstr "Enroll" @@ -968,12 +1041,13 @@ msgstr "Enroll Your Team" msgid "Enrolled" msgstr "Enrolled" -#: src/features/tournaments/components/edit-enrolled-players.tsx:132 +#: src/features/tournaments/components/enrollment/free-agent-section.tsx:155 #: src/features/tournaments/components/upcoming-tournament/enrolled-players-list-button.tsx:39 msgid "Enrolled Players" msgstr "Enrolled Players" -#: src/features/tournaments/components/edit-enrolled-teams.tsx:151 +#: src/features/tournaments/components/enrollment/enrollment-stats.tsx:28 +#: src/features/tournaments/components/enrollment/team-section.tsx:240 #: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:145 #: src/features/tournaments/components/upcoming-tournament/team-list-button.tsx:31 msgid "Enrolled Teams" @@ -983,16 +1057,22 @@ msgstr "Enrolled Teams" msgid "Enrolling as a free agent adds you to a pool of players looking for teammates." msgstr "Enrolling as a free agent adds you to a pool of players looking for teammates." +#: src/app/routes/_authed/admin/tournaments/$id/enrollment.tsx:33 +#: src/features/admin/components/dashboard/tournament-card.tsx:61 #: src/features/tournaments/components/upcoming-tournament/enroll-free-agent.tsx:29 -#: src/features/tournaments/components/upcoming-tournament/index.tsx:68 +#: src/features/tournaments/components/upcoming-tournament/index.tsx:69 msgid "Enrollment" msgstr "Enrollment" +#: src/features/tournaments/components/manage-tournament.tsx:55 +msgid "Enrollment & Teams" +msgstr "Enrollment & Teams" + #: src/features/tournaments/components/tournament-form.tsx:196 msgid "Enrollment Due" msgstr "Enrollment Due" -#: src/features/tournaments/components/upcoming-tournament/index.tsx:80 +#: src/features/tournaments/components/upcoming-tournament/index.tsx:81 msgid "Enrollment has been closed for this tournament." msgstr "Enrollment has been closed for this tournament." @@ -1013,14 +1093,39 @@ msgstr "Enter your name" msgid "Enter your phone number" msgstr "Enter your phone number" -#: src/features/activities/components/activities-table.tsx:187 +#: src/features/telemetry/components/error-browser/error-group-row.tsx:29 +msgid "Error group reopened" +msgstr "Error group reopened" + +#: src/features/telemetry/components/error-browser/error-group-row.tsx:29 +msgid "Error group resolved" +msgstr "Error group resolved" + +#: src/features/activities/components/activities-table.tsx:207 msgid "Error Message" msgstr "Error Message" -#: src/components/DefaultCatchBoundary.tsx:96 +#: src/components/DefaultCatchBoundary.tsx:98 msgid "Error: {errorMessage}" msgstr "Error: {errorMessage}" +#: src/app/routes/_authed/admin/telemetry.tsx:51 +#: src/features/telemetry/components/usage-screen.tsx:200 +msgid "Errors" +msgstr "Errors" + +#: src/features/telemetry/components/client-events-table.tsx:113 +msgid "Event Details" +msgstr "Event Details" + +#: src/features/telemetry/components/client-events-table.tsx:70 +msgid "Event name" +msgstr "Event name" + +#: src/app/routes/_authed/admin/telemetry.tsx:48 +msgid "Events" +msgstr "Events" + #: src/features/tournaments/components/group-stage-view.tsx:368 msgid "Expand standings" msgstr "Expand standings" @@ -1029,16 +1134,12 @@ msgstr "Expand standings" msgid "eyes" msgstr "eyes" -#: src/features/activities/components/activities-table.tsx:145 +#: src/features/activities/components/activities-table.tsx:181 msgid "Failed" msgstr "Failed" -#: src/features/admin/components/award-badges.tsx:41 -msgid "Failed to award badge" -msgstr "Failed to award badge" - #: src/app/routes/api/spotify/capture.ts:54 -#: src/contexts/spotify-context.tsx:427 +#: src/contexts/spotify-context.tsx:426 msgid "Failed to capture playback state" msgstr "Failed to capture playback state" @@ -1054,7 +1155,7 @@ msgstr "Failed to create team" msgid "Failed to create tournament" msgstr "Failed to create tournament" -#: src/contexts/spotify-context.tsx:335 +#: src/contexts/spotify-context.tsx:334 msgid "Failed to get devices" msgstr "Failed to get devices" @@ -1068,19 +1169,19 @@ msgid "Failed to load team" msgstr "Failed to load team" #: src/app/routes/api/spotify/resume.ts:54 -#: src/contexts/spotify-context.tsx:447 +#: src/contexts/spotify-context.tsx:446 msgid "Failed to resume playback state" msgstr "Failed to resume playback state" -#: src/contexts/spotify-context.tsx:360 +#: src/contexts/spotify-context.tsx:359 msgid "Failed to set active device" msgstr "Failed to set active device" -#: src/contexts/spotify-context.tsx:314 +#: src/contexts/spotify-context.tsx:313 msgid "Failed to set volume" msgstr "Failed to set volume" -#: src/features/admin/components/admin-page.tsx:42 +#: src/features/admin/components/dashboard/tools-list.tsx:41 msgid "Failed to start badge recalculation" msgstr "Failed to start badge recalculation" @@ -1092,19 +1193,19 @@ msgstr "Failed to update team" msgid "Failed to update tournament" msgstr "Failed to update tournament" -#: src/app/routes/api/teams/upload-logo.ts:140 -#: src/app/routes/api/tournaments/upload-logo.ts:138 +#: src/app/routes/api/teams/upload-logo.ts:141 +#: src/app/routes/api/tournaments/upload-logo.ts:139 #: src/features/teams/components/team-form/index.tsx:148 #: src/features/tournaments/components/tournament-form.tsx:88 msgid "Failed to upload logo" msgstr "Failed to upload logo" -#: src/features/activities/components/activities-table.tsx:330 +#: src/features/activities/components/activities-table.tsx:94 msgid "Failure" msgstr "Failure" -#: src/app/routes/api/teams/upload-logo.ts:79 -#: src/app/routes/api/tournaments/upload-logo.ts:80 +#: src/app/routes/api/teams/upload-logo.ts:80 +#: src/app/routes/api/tournaments/upload-logo.ts:81 msgid "File too large. Maximum size is 10MB." msgstr "File too large. Maximum size is 10MB." @@ -1112,7 +1213,7 @@ msgstr "File too large. Maximum size is 10MB." msgid "Final" msgstr "Final" -#: src/features/tournaments/components/started-tournament/index.tsx:114 +#: src/features/tournaments/components/started-tournament/index.tsx:115 msgid "Final Results" msgstr "Final Results" @@ -1150,11 +1251,19 @@ msgstr "Free Agent Enrollment" msgid "Free Agents" msgstr "Free Agents" -#: src/features/activities/components/activities-table.tsx:124 +#: src/features/telemetry/components/usage-screen.tsx:226 +msgid "Function" +msgstr "Function" + +#: src/features/activities/components/activities-table.tsx:165 msgid "Function Name" msgstr "Function Name" -#: src/app/routes/_authed/admin/tournaments/$id/assign-partners.tsx:148 +#: src/app/routes/_authed/admin/telemetry.tsx:45 +msgid "Functions" +msgstr "Functions" + +#: src/app/routes/_authed/admin/tournaments/$id/assign-partners.tsx:146 msgid "Generate Random Pairings" msgstr "Generate Random Pairings" @@ -1163,11 +1272,15 @@ msgstr "Generate Random Pairings" msgid "Go back" msgstr "Go back" -#: src/components/DefaultCatchBoundary.tsx:67 -#: src/components/DefaultCatchBoundary.tsx:132 +#: src/components/DefaultCatchBoundary.tsx:69 +#: src/components/DefaultCatchBoundary.tsx:134 msgid "Go Back" msgstr "Go Back" +#: src/features/telemetry/components/usage-screen.tsx:54 +msgid "Good" +msgstr "Good" + #: src/themes/seventies/manifest.ts:6 msgid "Groovy" msgstr "Groovy" @@ -1229,13 +1342,13 @@ msgstr "Have you played before?" msgid "Head to Head" msgstr "Head to Head" -#: src/components/DefaultCatchBoundary.tsx:103 +#: src/components/DefaultCatchBoundary.tsx:105 msgid "Hide details" msgstr "Hide details" -#: src/components/DefaultCatchBoundary.tsx:74 -#: src/components/DefaultCatchBoundary.tsx:125 -#: src/features/core/hooks/use-links.ts:11 +#: src/components/DefaultCatchBoundary.tsx:76 +#: src/components/DefaultCatchBoundary.tsx:127 +#: src/features/core/hooks/use-links.ts:12 msgid "Home" msgstr "Home" @@ -1263,7 +1376,7 @@ msgstr "How prediction scoring works" #. placeholder {0}: response.status #. placeholder {1}: response.statusText -#: src/contexts/spotify-context.tsx:65 +#: src/contexts/spotify-context.tsx:64 msgid "HTTP {0}: {1}" msgstr "HTTP {0}: {1}" @@ -1275,6 +1388,10 @@ msgstr "If this is your first time participating, please select <0>New Player1s (this page)" +msgstr "Slow >1s (this page)" + +#: src/components/DefaultCatchBoundary.tsx:91 msgid "Something went wrong" msgstr "Something went wrong" @@ -2241,8 +2466,8 @@ msgstr "Song segment must be no more than 15 seconds" msgid "Song start time is required" msgstr "Song start time is required" -#: src/features/activities/components/activities-table.tsx:345 #: src/features/players/components/player-stats-table.tsx:340 +#: src/features/telemetry/components/shared/search-filter-bar.tsx:102 msgid "Sort:" msgstr "Sort:" @@ -2250,7 +2475,7 @@ msgstr "Sort:" msgid "Sorted by win percentage" msgstr "Sorted by win percentage" -#: src/features/spotify/components/spotify-sheet.tsx:68 +#: src/features/spotify/components/spotify-sheet.tsx:67 msgid "Spotify Controls" msgstr "Spotify Controls" @@ -2262,7 +2487,7 @@ msgstr "Spotify Premium is required for playback control." msgid "Spotify premium required" msgstr "Spotify premium required" -#: src/features/spotify/components/spotify-controls-bar.tsx:180 +#: src/features/spotify/components/spotify-controls-bar.tsx:179 msgid "Spotify settings" msgstr "Spotify settings" @@ -2292,6 +2517,10 @@ msgstr "Start time is required" msgid "Start typing to search..." msgstr "Start typing to search..." +#: src/features/admin/components/dashboard/tournament-card.tsx:43 +msgid "Starts in" +msgstr "Starts in" + #: src/features/players/components/player-stats-table.tsx:372 msgid "Stat abbreviations and MMR info" msgstr "Stat abbreviations and MMR info" @@ -2300,7 +2529,7 @@ msgstr "Stat abbreviations and MMR info" msgid "Stat Abbreviations:" msgstr "Stat Abbreviations:" -#: src/features/core/hooks/use-links.ts:16 +#: src/features/core/hooks/use-links.ts:17 #: src/features/players/components/profile/index.tsx:26 #: src/features/teams/components/team-profile/index.tsx:34 msgid "Statistics" @@ -2315,8 +2544,8 @@ msgstr "Stats" msgid "Stats Comparison" msgstr "Stats Comparison" -#: src/features/activities/components/activities-table.tsx:131 -#: src/features/activities/components/activities-table.tsx:321 +#: src/features/activities/components/activities-table.tsx:85 +#: src/features/activities/components/activities-table.tsx:167 msgid "Status" msgstr "Status" @@ -2328,8 +2557,8 @@ msgstr "Status" msgid "Submit" msgstr "Submit" -#: src/features/activities/components/activities-table.tsx:138 -#: src/features/activities/components/activities-table.tsx:329 +#: src/features/activities/components/activities-table.tsx:93 +#: src/features/activities/components/activities-table.tsx:174 msgid "Success" msgstr "Success" @@ -2337,8 +2566,7 @@ msgstr "Success" msgid "Successfully logged in. Welcome back!" msgstr "Successfully logged in. Welcome back!" -#: src/features/activities/components/activities-table.tsx:42 -#: src/features/activities/components/activities-table.tsx:112 +#: src/features/activities/components/activities-table.tsx:53 #: src/features/settings/components/color-scheme-picker.tsx:47 msgid "System" msgstr "System" @@ -2351,11 +2579,11 @@ msgstr "T" msgid "Tap an opponent below to compare" msgstr "Tap an opponent below to compare" -#: src/components/ios-install-prompt.tsx:43 +#: src/components/ios-install-prompt.tsx:48 msgid "Tap Menu (⋮) → Add to Home screen" msgstr "Tap Menu (⋮) → Add to Home screen" -#: src/components/ios-install-prompt.tsx:42 +#: src/components/ios-install-prompt.tsx:47 msgid "Tap Share → Add to Home Screen" msgstr "Tap Share → Add to Home Screen" @@ -2385,7 +2613,7 @@ msgstr "Team Details" msgid "Team enrolled successfully!" msgstr "Team enrolled successfully!" -#: src/app/routes/api/teams/upload-logo.ts:92 +#: src/app/routes/api/teams/upload-logo.ts:93 #: src/features/teams/components/team-profile/index.tsx:21 msgid "Team not found" msgstr "Team not found" @@ -2419,11 +2647,15 @@ msgstr "Teams" msgid "Teams created successfully!" msgstr "Teams created successfully!" +#: src/app/routes/_authed/admin/telemetry.tsx:20 +msgid "Telemetry" +msgstr "Telemetry" + #: src/themes/terminal/manifest.ts:6 msgid "Terminal" msgstr "Terminal" -#: src/app/routes/api/push/test.ts:29 +#: src/app/routes/api/push/test.ts:30 msgid "Test notification — push is working on this device." msgstr "Test notification — push is working on this device." @@ -2498,6 +2730,10 @@ msgstr "Top {0, plural, one {# team} other {# teams}} from each group will advan msgid "Top {n} advance" msgstr "Top {n} advance" +#: src/features/telemetry/components/usage-screen.tsx:191 +msgid "Top functions by call count" +msgstr "Top functions by call count" + #: src/features/matches/components/team-head-to-head-sheet.tsx:158 #: src/features/players/components/player-head-to-head-sheet.tsx:193 msgid "Total Cups" @@ -2517,7 +2753,7 @@ msgstr "Tournament created but logo upload failed: {0}" msgid "Tournament created successfully!" msgstr "Tournament created successfully!" -#: src/app/routes/api/tournaments/upload-logo.ts:93 +#: src/app/routes/api/tournaments/upload-logo.ts:94 msgid "Tournament not found" msgstr "Tournament not found" @@ -2536,7 +2772,7 @@ msgid "Tournament updated successfully!" msgstr "Tournament updated successfully!" #: src/app/routes/_authed/tournaments/index.tsx:17 -#: src/features/core/hooks/use-links.ts:21 +#: src/features/core/hooks/use-links.ts:22 #: src/features/teams/components/team-profile/index.tsx:53 #: src/features/teams/components/team-profile/skeleton.tsx:80 msgid "Tournaments" @@ -2550,6 +2786,10 @@ msgstr "trackId is required for playTrack action" msgid "Try the new themes" msgstr "Try the new themes" +#: src/features/admin/components/dashboard/quiet-boundary.tsx:26 +msgid "Unavailable right now" +msgstr "Unavailable right now" + #: src/features/tournaments/components/upcoming-tournament/unenroll-free-agent.tsx:26 #: src/features/tournaments/components/upcoming-tournament/unenroll-team.tsx:34 msgid "Unenroll" @@ -2563,12 +2803,12 @@ msgstr "Unenroll from tournament?" msgid "Unenroll Team" msgstr "Unenroll Team" -#: src/components/DefaultCatchBoundary.tsx:39 +#: src/components/DefaultCatchBoundary.tsx:40 msgid "Unknown error" msgstr "Unknown error" -#: src/app/routes/api/teams/upload-logo.ts:141 -#: src/app/routes/api/tournaments/upload-logo.ts:139 +#: src/app/routes/api/teams/upload-logo.ts:142 +#: src/app/routes/api/tournaments/upload-logo.ts:140 msgid "Unknown error occurred" msgstr "Unknown error occurred" @@ -2585,7 +2825,7 @@ msgstr "Unknown Player" msgid "Unnamed Player" msgstr "Unnamed Player" -#: src/features/tournaments/components/started-tournament/index.tsx:128 +#: src/features/tournaments/components/started-tournament/index.tsx:129 msgid "Upcoming" msgstr "Upcoming" @@ -2607,10 +2847,23 @@ msgstr "Update Team" msgid "Update Tournament" msgstr "Update Tournament" -#: src/features/activities/components/activities-table.tsx:176 +#: src/app/routes/_authed/admin/telemetry.tsx:57 +msgid "Usage" +msgstr "Usage" + +#: src/features/admin/components/dashboard/usage-card.tsx:34 +msgid "Usage, 7 days" +msgstr "Usage, 7 days" + +#: src/features/activities/components/activities-table.tsx:202 +#: src/features/telemetry/components/client-events-table.tsx:146 msgid "User Agent" msgstr "User Agent" +#: src/features/telemetry/components/client-events-table.tsx:131 +msgid "Value" +msgstr "Value" + #: src/features/bracket/components/match-card.tsx:201 #: src/features/bracket/components/match-card.tsx:226 msgid "versus" @@ -2645,6 +2898,10 @@ msgstr "View Predictions" msgid "Viewport" msgstr "Viewport" +#: src/features/telemetry/components/usage-screen.tsx:211 +msgid "Vitals" +msgstr "Vitals" + #: src/app/routes/api/spotify/playback.ts:70 msgid "volumePercent must be a number" msgstr "volumePercent must be a number" @@ -2680,6 +2937,10 @@ msgstr "Walkout Segment ({segmentDuration}s)" msgid "Walkout Song" msgstr "Walkout Song" +#: src/features/telemetry/components/usage-screen.tsx:165 +msgid "WAU" +msgstr "WAU" + #: src/features/tournaments/components/group-stage-view.tsx:407 msgid "WC" msgstr "WC" @@ -2731,7 +2992,7 @@ msgstr "You and 1 other" msgid "You are not authorized to perform this action" msgstr "You are not authorized to perform this action" -#: src/components/DefaultCatchBoundary.tsx:60 +#: src/components/DefaultCatchBoundary.tsx:62 msgid "You don't have permission to access this page." msgstr "You don't have permission to access this page." @@ -2755,7 +3016,7 @@ msgstr "You're the first free agent!" msgid "You've been added as a free agent!" msgstr "You've been added as a free agent!" -#: src/components/DefaultCatchBoundary.tsx:46 +#: src/components/DefaultCatchBoundary.tsx:48 msgid "You've been logged out" msgstr "You've been logged out" diff --git a/src/locales/es/messages.po b/src/locales/es/messages.po index 1a962de..2ecd106 100644 --- a/src/locales/es/messages.po +++ b/src/locales/es/messages.po @@ -18,7 +18,7 @@ msgid " (Losers)" msgstr " (Perdedores)" #. placeholder {0}: activeDevice.volume_percent -#: src/features/spotify/components/spotify-sheet.tsx:163 +#: src/features/spotify/components/spotify-sheet.tsx:162 msgid " (Volume: {0}%)" msgstr " (Volumen: {0}%)" @@ -26,7 +26,7 @@ msgstr " (Volumen: {0}%)" msgid "(713) 867-5309" msgstr "(713) 867-5309" -#: src/features/spotify/components/spotify-sheet.tsx:148 +#: src/features/spotify/components/spotify-sheet.tsx:147 msgid "(Active)" msgstr "(Activo)" @@ -38,14 +38,14 @@ msgstr "{0, plural, one {# bracket en} other {# brackets en}}" #. placeholder {0}: enrolledPlayers.length #. placeholder {0}: players.length #: src/features/players/components/players-activity-table.tsx:100 -#: src/features/tournaments/components/edit-enrolled-players.tsx:135 +#: src/features/tournaments/components/enrollment/free-agent-section.tsx:158 msgid "{0, plural, one {# player} other {# players}}" msgstr "{0, plural, one {# jugador} other {# jugadores}}" #. placeholder {0}: enrolledTeams.length #. placeholder {0}: group.teams.length #. placeholder {0}: orderedTeams.length -#: src/features/tournaments/components/edit-enrolled-teams.tsx:154 +#: src/features/tournaments/components/enrollment/team-section.tsx:243 #: src/features/tournaments/components/group-preview.tsx:27 #: src/features/tournaments/components/seed-tournament.tsx:116 #: src/features/tournaments/components/setup-group-stage.tsx:224 @@ -53,10 +53,15 @@ msgid "{0, plural, one {# team} other {# teams}}" msgstr "{0, plural, one {# equipo} other {# equipos}}" #. placeholder {0}: result.totalItems -#: src/features/activities/components/activities-table.tsx:341 +#: src/features/activities/components/activities-table.tsx:125 msgid "{0, plural, one {# total activity} other {# total activities}}" msgstr "{0, plural, one {# actividad total} other {# actividades totales}}" +#. placeholder {0}: result.totalItems +#: src/features/telemetry/components/client-events-table.tsx:87 +msgid "{0, plural, one {# total event} other {# total events}}" +msgstr "" + #. placeholder {0}: selectedConfig.total_group_matches #: src/features/tournaments/components/setup-group-stage.tsx:249 msgid "{0, plural, one {# total group stage match} other {# total group stage matches}}" @@ -73,7 +78,9 @@ msgid "{0, plural, one {# wildcard} other {# wildcards}}" msgstr "{0, plural, one {# comodín} other {# comodines}}" #. placeholder {0}: freeAgents.length -#: src/app/routes/_authed/admin/tournaments/$id/assign-partners.tsx:126 +#. placeholder {0}: props.count +#: src/app/routes/_authed/admin/tournaments/$id/assign-partners.tsx:124 +#: src/features/tournaments/components/enrollment/enrollment-stats.tsx:22 msgid "{0, plural, one {player enrolled} other {players enrolled}}" msgstr "{0, plural, one {jugador inscrito} other {jugadores inscritos}}" @@ -88,6 +95,11 @@ msgstr "{0} a las {1}" msgid "{0} Badge Holders" msgstr "Poseedores de la insignia {0}" +#. placeholder {0}: recentErrors.totalItems +#: src/lib/telemetry/alerts.server.ts:104 +msgid "{0} client errors in the last 15 minutes" +msgstr "" + #. placeholder {0}: match.away?.name #. placeholder {0}: match.home?.name #: src/features/bracket/components/match-form.tsx:90 @@ -131,8 +143,9 @@ msgid "{0}d" msgstr "{0}d" #. placeholder {0}: activity.duration -#: src/features/activities/components/activities-table.tsx:83 -#: src/features/activities/components/activities-table.tsx:163 +#. placeholder {0}: selectedActivity.duration +#: src/features/activities/components/activities-table.tsx:148 +#: src/features/activities/components/activities-table.tsx:193 msgid "{0}ms" msgstr "{0}ms" @@ -145,15 +158,22 @@ msgstr "{count, plural, one {Jugadores (#)} other {Jugadores (#)}}" msgid "{count, plural, one {Teams (#)} other {Teams (#)}}" msgstr "{count, plural, one {Equipos (#)} other {Equipos (#)}}" +#: src/lib/telemetry/alerts.server.ts:167 +msgid "{deniedCount} denied admin access attempts in the last 15 minutes" +msgstr "" + #: src/features/players/components/players-activity-table.tsx:44 +#: src/features/telemetry/components/shared/relative-time.tsx:17 msgid "{diffDays}d ago" msgstr "hace {diffDays}d" #: src/features/players/components/players-activity-table.tsx:43 +#: src/features/telemetry/components/shared/relative-time.tsx:16 msgid "{diffHours}h ago" msgstr "hace {diffHours}h" #: src/features/players/components/players-activity-table.tsx:42 +#: src/features/telemetry/components/shared/relative-time.tsx:15 msgid "{diffMins}m ago" msgstr "hace {diffMins}m" @@ -177,7 +197,11 @@ msgstr "{n, plural, one {# partido garantizado} other {# partidos garantizados}} msgid "{n, plural, one {# wildcard} other {# wildcards}}" msgstr "{n, plural, one {# comodín} other {# comodines}}" -#: src/app/routes/_authed/admin/tournaments/$id/teams.tsx:23 +#: src/lib/telemetry/alerts.server.ts:154 +msgid "{name} failed {count} times in the last 15 minutes" +msgstr "" + +#: src/app/routes/_authed/admin/tournaments/$id/teams.tsx:24 msgid "{name} Teams" msgstr "Equipos de {name}" @@ -331,6 +355,10 @@ msgstr "2. Envío más temprano" msgid "2nd place" msgstr "2do lugar" +#: src/features/telemetry/components/usage-screen.tsx:256 +msgid "30d" +msgstr "" + #: src/features/teams/components/team-form/duration-picker.tsx:22 msgid "30s" msgstr "30s" @@ -343,6 +371,10 @@ msgstr "3er lugar" msgid "5s" msgstr "5s" +#: src/features/telemetry/components/usage-screen.tsx:255 +msgid "7d" +msgstr "" + #. placeholder {0}: number?.slice(0, 3) #. placeholder {1}: number?.slice(3, 6) #. placeholder {2}: number?.slice(6) @@ -350,6 +382,11 @@ msgstr "5s" msgid "A code was sent to +1 ({0}) {1}-{2}" msgstr "Se envió un código al +1 ({0}) {1}-{2}" +#. placeholder {0}: info.message.slice(0, 120) +#: src/lib/telemetry/alerts.server.ts:125 +msgid "A new error appeared: {0}" +msgstr "" + #: src/features/bracket/components/match-report.tsx:304 msgid "a team" msgstr "un equipo" @@ -370,7 +407,7 @@ msgstr "Color de acento" msgid "Access denied" msgstr "Acceso denegado" -#: src/components/DefaultCatchBoundary.tsx:58 +#: src/components/DefaultCatchBoundary.tsx:60 msgid "Access Denied" msgstr "Acceso Denegado" @@ -379,34 +416,33 @@ msgid "Account created successfully!" msgstr "¡Cuenta creada exitosamente!" #. placeholder {0}: activeDevice.name -#: src/features/spotify/components/spotify-sheet.tsx:161 +#: src/features/spotify/components/spotify-sheet.tsx:160 msgid "Active device: {0}" msgstr "Dispositivo activo: {0}" -#: src/app/routes/_authed/admin/activities.tsx:21 -#: src/features/admin/components/admin-page.tsx:90 -msgid "Activities" -msgstr "Actividades" +#: src/features/admin/components/dashboard/usage-card.tsx:39 +msgid "Active players" +msgstr "" -#: src/features/activities/components/activities-table.tsx:120 +#: src/features/activities/components/activities-table.tsx:159 msgid "Activity Details" msgstr "Detalles de la actividad" -#: src/components/ios-install-prompt.tsx:58 +#: src/components/ios-install-prompt.tsx:63 msgid "Add FLXN to your home screen for a smoother experience" msgstr "Agrega FLXN a tu pantalla de inicio para una experiencia más fluida" -#: src/features/tournaments/components/edit-enrolled-players.tsx:117 +#: src/features/tournaments/components/enrollment/free-agent-section.tsx:130 msgid "Add Player" msgstr "Agregar Jugador" -#: src/features/tournaments/components/edit-enrolled-teams.tsx:136 +#: src/features/tournaments/components/enrollment/team-section.tsx:213 msgid "Add Team" msgstr "Agregar Equipo" -#: src/app/routes/_authed/admin.tsx:14 -#: src/app/routes/_authed/admin/index.tsx:9 -#: src/features/core/hooks/use-links.ts:36 +#: src/app/routes/_authed/admin.tsx:15 +#: src/app/routes/_authed/admin/index.tsx:22 +#: src/features/core/hooks/use-links.ts:37 msgid "Admin" msgstr "Admin" @@ -415,7 +451,7 @@ msgid "Aero" msgstr "Aero" #: src/app/routes/_authed/stats.tsx:59 -#: src/features/activities/components/activities-table.tsx:328 +#: src/features/activities/components/activities-table.tsx:92 #: src/features/players/components/profile/index.tsx:28 msgid "All" msgstr "Todos" @@ -432,7 +468,7 @@ msgstr "¡Todos los partidos de grupo terminaron! Llena el bracket eliminatorio msgid "ALM" msgstr "ALM" -#: src/components/DefaultCatchBoundary.tsx:92 +#: src/components/DefaultCatchBoundary.tsx:94 msgid "An error occurred while loading this page." msgstr "Ocurrió un error al cargar esta página." @@ -448,6 +484,10 @@ msgstr "Ocurrió un error inesperado. Intenta de nuevo." msgid "Announce matchup" msgstr "Anunciar enfrentamiento" +#: src/features/telemetry/components/client-events-table.tsx:49 +msgid "Anonymous" +msgstr "" + #: src/app/routes/_authed/settings.tsx:35 msgid "Appearance" msgstr "Apariencia" @@ -456,7 +496,7 @@ msgstr "Apariencia" msgid "Are you sure you want to unenroll from this tournament? You can enroll again at any point before the deadline." msgstr "¿Seguro que quieres darte de baja de este torneo? Puedes volver a inscribirte en cualquier momento antes de la fecha límite." -#: src/features/activities/components/activities-table.tsx:200 +#: src/features/activities/components/activities-table.tsx:221 msgid "Arguments" msgstr "Argumentos" @@ -464,7 +504,7 @@ msgstr "Argumentos" msgid "Art Deco" msgstr "Art Déco" -#: src/features/tournaments/components/manage-tournament.tsx:69 +#: src/features/tournaments/components/enrollment/free-agent-section.tsx:149 msgid "Assign Partners" msgstr "Asignar Compañeros" @@ -473,12 +513,12 @@ msgstr "Asignar Compañeros" msgid "At least one team must have 10 cups" msgstr "Al menos un equipo debe tener 10 vasos" -#: src/contexts/spotify-context.tsx:158 +#: src/contexts/spotify-context.tsx:157 msgid "Authentication failed: {error}" msgstr "Falló la autenticación: {error}" #. placeholder {0}: decodeURIComponent(details) -#: src/contexts/spotify-context.tsx:157 +#: src/contexts/spotify-context.tsx:156 msgid "Authentication failed: {error} - {0}" msgstr "Falló la autenticación: {error} - {0}" @@ -520,15 +560,15 @@ msgstr "Prom. Margen de Derrota" msgid "Avg Win Margin" msgstr "Prom. Margen de Victoria" -#: src/features/admin/components/award-badges.tsx:104 +#: src/features/admin/components/award-badges.tsx:98 msgid "Award Badge" msgstr "Otorgar insignia" -#: src/features/admin/components/admin-page.tsx:55 +#: src/features/admin/components/dashboard/tools-list.tsx:54 msgid "Award Badges" msgstr "Otorgar insignias" -#: src/features/admin/components/award-badges.tsx:65 +#: src/features/admin/components/award-badges.tsx:59 msgid "Award Manual Badge" msgstr "Otorgar insignia manual" @@ -554,23 +594,23 @@ msgstr "AWM" msgid "Back" msgstr "Atrás" -#: src/features/admin/components/award-badges.tsx:37 +#: src/features/admin/components/award-badges.tsx:33 msgid "Badge awarded to {playerName}" msgstr "Insignia otorgada a {playerName}" -#: src/features/admin/components/admin-page.tsx:86 +#: src/features/admin/components/dashboard/tools-list.tsx:85 msgid "Badge recalculation failed. Check the server logs." msgstr "Falló el recálculo de insignias. Revisa los registros del servidor." -#: src/features/admin/components/admin-page.tsx:39 +#: src/features/admin/components/dashboard/tools-list.tsx:38 msgid "Badge recalculation is already running" msgstr "El recálculo de insignias ya está en curso" -#: src/features/admin/components/admin-page.tsx:38 +#: src/features/admin/components/dashboard/tools-list.tsx:37 msgid "Badge recalculation started in the background" msgstr "Recálculo de insignias iniciado en segundo plano" -#: src/features/admin/components/award-badges.tsx:70 +#: src/features/admin/components/award-badges.tsx:64 msgid "Badge Type" msgstr "Tipo de insignia" @@ -592,12 +632,12 @@ msgid "Both teams cannot have 10 cups" msgstr "Los dos equipos no pueden tener 10 vasos" #: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:106 -#: src/features/tournaments/components/upcoming-tournament/index.tsx:150 +#: src/features/tournaments/components/upcoming-tournament/index.tsx:151 msgid "Bracket" msgstr "Bracket" #: src/app/routes/_authed/admin/preview.tsx:13 -#: src/features/admin/components/admin-page.tsx:102 +#: src/features/admin/components/dashboard/tools-list.tsx:96 msgid "Bracket Preview" msgstr "Vista previa del bracket" @@ -614,6 +654,10 @@ msgstr "Brutal" msgid "CA" msgstr "VC" +#: src/features/admin/components/dashboard/now-strip.tsx:52 +msgid "Calls today" +msgstr "" + #: src/components/sheet/slide-panel/slide-panel.tsx:41 #: src/components/sheet/slide-panel/slide-panel.tsx:190 #: src/features/bracket/components/match-form.tsx:160 @@ -633,11 +677,12 @@ msgstr "Cancelar" msgid "Cannot create groups" msgstr "No se pueden crear grupos" -#: src/app/routes/_authed/admin/tournaments/$id/assign-partners.tsx:138 +#: src/app/routes/_authed/admin/tournaments/$id/assign-partners.tsx:136 +#: src/features/tournaments/components/enrollment/enrollment-stats.tsx:41 msgid "Cannot create teams with an odd number of players. Please have one player unenroll." msgstr "No se pueden crear equipos con un número impar de jugadores. Pide que un jugador se dé de baja." -#: src/features/spotify/components/spotify-controls-bar.tsx:144 +#: src/features/spotify/components/spotify-controls-bar.tsx:143 msgid "Capture current state" msgstr "Capturar estado actual" @@ -659,7 +704,7 @@ msgstr "Campeones" msgid "Change Logo" msgstr "Cambiar logo" -#: src/features/admin/components/award-badges.tsx:87 +#: src/features/admin/components/award-badges.tsx:81 msgid "Choose a player" msgstr "Elige un jugador" @@ -671,6 +716,18 @@ msgstr "Plastilina" msgid "Clear selected player" msgstr "Quitar jugador seleccionado" +#: src/lib/telemetry/alerts.server.ts:103 +msgid "Client error spike" +msgstr "" + +#: src/features/admin/components/dashboard/errors-card.tsx:21 +msgid "Client errors" +msgstr "" + +#: src/features/telemetry/components/usage-screen.tsx:203 +msgid "Client errors per day" +msgstr "" + #: src/components/sheet/modal.tsx:35 #: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:169 #: src/features/tournaments/components/upcoming-tournament/rules-list-button.tsx:36 @@ -717,7 +774,7 @@ msgstr "Confirmar" msgid "Confirm {scoreText}" msgstr "Confirmar {scoreText}" -#: src/app/routes/_authed/admin/tournaments/$id/assign-partners.tsx:175 +#: src/app/routes/_authed/admin/tournaments/$id/assign-partners.tsx:173 msgid "Confirm & Create Teams" msgstr "Confirmar y crear equipos" @@ -738,15 +795,15 @@ msgstr "Confirmar configuración" msgid "Confirm Unenrollment" msgstr "Confirmar baja" -#: src/features/spotify/components/spotify-controls-bar.tsx:52 +#: src/features/spotify/components/spotify-controls-bar.tsx:51 msgid "Connect Spotify" msgstr "Conectar Spotify" -#: src/features/spotify/components/spotify-sheet.tsx:87 +#: src/features/spotify/components/spotify-sheet.tsx:86 msgid "Connect with Spotify" msgstr "Conectar con Spotify" -#: src/features/spotify/components/spotify-sheet.tsx:75 +#: src/features/spotify/components/spotify-sheet.tsx:74 msgid "Connect your Spotify account" msgstr "Conecta tu cuenta de Spotify" @@ -796,7 +853,8 @@ msgstr "Crear equipo" msgid "Create Tournament" msgstr "Crear torneo" -#: src/features/activities/components/activities-table.tsx:168 +#: src/features/activities/components/activities-table.tsx:198 +#: src/features/telemetry/components/client-events-table.tsx:149 msgid "Created" msgstr "Creado" @@ -825,6 +883,15 @@ msgstr "Vasos anotados" msgid "Custom Hex Code" msgstr "Código hex personalizado" +#: src/features/admin/components/dashboard/usage-card.tsx:41 +#: src/features/telemetry/components/usage-screen.tsx:159 +msgid "Daily active players" +msgstr "" + +#: src/features/admin/components/dashboard/usage-card.tsx:47 +msgid "Daily page views" +msgstr "" + #: src/features/settings/components/color-scheme-picker.tsx:39 msgid "Dark" msgstr "Oscuro" @@ -833,7 +900,7 @@ msgstr "Oscuro" msgid "Dark Academia" msgstr "Academia Oscura" -#: src/features/activities/components/activities-table.tsx:356 +#: src/features/activities/components/activities-table.tsx:119 msgid "Date" msgstr "Fecha" @@ -845,6 +912,10 @@ msgstr "Mar Profundo" msgid "Default" msgstr "Predeterminado" +#: src/lib/telemetry/alerts.server.ts:166 +msgid "Denied access attempt" +msgstr "" + #: src/features/tournaments/components/tournament-form.tsx:150 msgid "Description" msgstr "Descripción" @@ -853,11 +924,11 @@ msgstr "Descripción" msgid "deviceId is required for transfer action" msgstr "Se requiere deviceId para la acción de transferencia" -#: src/features/spotify/components/spotify-sheet.tsx:188 +#: src/features/spotify/components/spotify-sheet.tsx:187 msgid "Disconnect Spotify" msgstr "Desconectar Spotify" -#: src/components/ios-install-prompt.tsx:65 +#: src/components/ios-install-prompt.tsx:70 msgid "Dismiss" msgstr "Cerrar" @@ -871,28 +942,25 @@ msgstr "¿Se ve bien así?" msgid "Downstream matches were reset because the correction changed who advances." msgstr "Los partidos posteriores se reiniciaron porque la corrección cambió quién avanza." -#: src/features/activities/components/activities-table.tsx:161 -#: src/features/activities/components/activities-table.tsx:372 +#: src/features/activities/components/activities-table.tsx:120 +#: src/features/activities/components/activities-table.tsx:190 msgid "Duration" msgstr "Duración" +#: src/features/telemetry/components/usage-screen.tsx:233 +msgid "Duration p50 / p95" +msgstr "" + #: src/features/tournaments/components/tournament-stats.tsx:176 msgid "Earlier regional formats aren't supported in the app and order of matches or displayed winners may be unreliable." msgstr "Los formatos regionales anteriores no son compatibles con la app, así que el orden de los partidos o los ganadores mostrados podrían no ser confiables." #. placeholder {0}: team.name #: src/features/teams/components/manage-teams.tsx:35 +#: src/features/tournaments/components/enrollment/team-section.tsx:96 msgid "Edit {0}" msgstr "Editar {0}" -#: src/features/tournaments/components/manage-tournament.tsx:117 -msgid "Edit Enrolled Teams" -msgstr "Editar equipos inscritos" - -#: src/features/tournaments/components/manage-tournament.tsx:63 -msgid "Edit Enrollments" -msgstr "Editar inscripciones" - #: src/features/bracket/components/match-card.tsx:392 msgid "Edit Match" msgstr "Editar partido" @@ -906,17 +974,18 @@ msgstr "Editar marcador del partido" msgid "Edit Prediction" msgstr "Editar predicción" -#: src/features/tournaments/components/manage-tournament.tsx:58 -#: src/features/tournaments/components/manage-tournament.tsx:109 +#: src/features/tournaments/components/manage-tournament.tsx:65 +#: src/features/tournaments/components/manage-tournament.tsx:92 msgid "Edit Rules" msgstr "Editar reglas" #: src/features/teams/components/manage-teams.tsx:35 +#: src/features/tournaments/components/enrollment/team-section.tsx:96 msgid "Edit Team" msgstr "Editar equipo" -#: src/features/tournaments/components/manage-tournament.tsx:53 -#: src/features/tournaments/components/manage-tournament.tsx:89 +#: src/features/tournaments/components/manage-tournament.tsx:60 +#: src/features/tournaments/components/manage-tournament.tsx:72 msgid "Edit Tournament" msgstr "Editar torneo" @@ -940,6 +1009,10 @@ msgstr "Fecha de fin (opcional)" msgid "End Match" msgstr "Terminar partido" +#: src/features/telemetry/components/usage-screen.tsx:156 +msgid "Engagement" +msgstr "" + #: src/features/tournaments/components/upcoming-tournament/enroll-free-agent.tsx:26 msgid "Enroll" msgstr "Inscribirse" @@ -968,12 +1041,13 @@ msgstr "Inscribe a tu equipo" msgid "Enrolled" msgstr "Inscrito" -#: src/features/tournaments/components/edit-enrolled-players.tsx:132 +#: src/features/tournaments/components/enrollment/free-agent-section.tsx:155 #: src/features/tournaments/components/upcoming-tournament/enrolled-players-list-button.tsx:39 msgid "Enrolled Players" msgstr "Jugadores inscritos" -#: src/features/tournaments/components/edit-enrolled-teams.tsx:151 +#: src/features/tournaments/components/enrollment/enrollment-stats.tsx:28 +#: src/features/tournaments/components/enrollment/team-section.tsx:240 #: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:145 #: src/features/tournaments/components/upcoming-tournament/team-list-button.tsx:31 msgid "Enrolled Teams" @@ -983,16 +1057,22 @@ msgstr "Equipos inscritos" msgid "Enrolling as a free agent adds you to a pool of players looking for teammates." msgstr "Inscribirte como agente libre te agrega a un grupo de jugadores buscando compañeros de equipo." +#: src/app/routes/_authed/admin/tournaments/$id/enrollment.tsx:33 +#: src/features/admin/components/dashboard/tournament-card.tsx:61 #: src/features/tournaments/components/upcoming-tournament/enroll-free-agent.tsx:29 -#: src/features/tournaments/components/upcoming-tournament/index.tsx:68 +#: src/features/tournaments/components/upcoming-tournament/index.tsx:69 msgid "Enrollment" msgstr "Inscripción" +#: src/features/tournaments/components/manage-tournament.tsx:55 +msgid "Enrollment & Teams" +msgstr "" + #: src/features/tournaments/components/tournament-form.tsx:196 msgid "Enrollment Due" msgstr "Fecha límite de inscripción" -#: src/features/tournaments/components/upcoming-tournament/index.tsx:80 +#: src/features/tournaments/components/upcoming-tournament/index.tsx:81 msgid "Enrollment has been closed for this tournament." msgstr "La inscripción para este torneo ya está cerrada." @@ -1013,14 +1093,39 @@ msgstr "Ingresa tu nombre" msgid "Enter your phone number" msgstr "Ingresa tu número de teléfono" -#: src/features/activities/components/activities-table.tsx:187 +#: src/features/telemetry/components/error-browser/error-group-row.tsx:29 +msgid "Error group reopened" +msgstr "" + +#: src/features/telemetry/components/error-browser/error-group-row.tsx:29 +msgid "Error group resolved" +msgstr "" + +#: src/features/activities/components/activities-table.tsx:207 msgid "Error Message" msgstr "Mensaje de error" -#: src/components/DefaultCatchBoundary.tsx:96 +#: src/components/DefaultCatchBoundary.tsx:98 msgid "Error: {errorMessage}" msgstr "Error: {errorMessage}" +#: src/app/routes/_authed/admin/telemetry.tsx:51 +#: src/features/telemetry/components/usage-screen.tsx:200 +msgid "Errors" +msgstr "" + +#: src/features/telemetry/components/client-events-table.tsx:113 +msgid "Event Details" +msgstr "" + +#: src/features/telemetry/components/client-events-table.tsx:70 +msgid "Event name" +msgstr "" + +#: src/app/routes/_authed/admin/telemetry.tsx:48 +msgid "Events" +msgstr "" + #: src/features/tournaments/components/group-stage-view.tsx:368 msgid "Expand standings" msgstr "Expandir tabla de posiciones" @@ -1029,16 +1134,12 @@ msgstr "Expandir tabla de posiciones" msgid "eyes" msgstr "ojos" -#: src/features/activities/components/activities-table.tsx:145 +#: src/features/activities/components/activities-table.tsx:181 msgid "Failed" msgstr "Fallido" -#: src/features/admin/components/award-badges.tsx:41 -msgid "Failed to award badge" -msgstr "No se pudo otorgar la insignia" - #: src/app/routes/api/spotify/capture.ts:54 -#: src/contexts/spotify-context.tsx:427 +#: src/contexts/spotify-context.tsx:426 msgid "Failed to capture playback state" msgstr "No se pudo capturar el estado de reproducción" @@ -1054,7 +1155,7 @@ msgstr "No se pudo crear el equipo" msgid "Failed to create tournament" msgstr "No se pudo crear el torneo" -#: src/contexts/spotify-context.tsx:335 +#: src/contexts/spotify-context.tsx:334 msgid "Failed to get devices" msgstr "No se pudieron obtener los dispositivos" @@ -1068,19 +1169,19 @@ msgid "Failed to load team" msgstr "No se pudo cargar el equipo" #: src/app/routes/api/spotify/resume.ts:54 -#: src/contexts/spotify-context.tsx:447 +#: src/contexts/spotify-context.tsx:446 msgid "Failed to resume playback state" msgstr "No se pudo reanudar el estado de reproducción" -#: src/contexts/spotify-context.tsx:360 +#: src/contexts/spotify-context.tsx:359 msgid "Failed to set active device" msgstr "No se pudo establecer el dispositivo activo" -#: src/contexts/spotify-context.tsx:314 +#: src/contexts/spotify-context.tsx:313 msgid "Failed to set volume" msgstr "No se pudo ajustar el volumen" -#: src/features/admin/components/admin-page.tsx:42 +#: src/features/admin/components/dashboard/tools-list.tsx:41 msgid "Failed to start badge recalculation" msgstr "No se pudo iniciar el recálculo de insignias" @@ -1092,19 +1193,19 @@ msgstr "No se pudo actualizar el equipo" msgid "Failed to update tournament" msgstr "No se pudo actualizar el torneo" -#: src/app/routes/api/teams/upload-logo.ts:140 -#: src/app/routes/api/tournaments/upload-logo.ts:138 +#: src/app/routes/api/teams/upload-logo.ts:141 +#: src/app/routes/api/tournaments/upload-logo.ts:139 #: src/features/teams/components/team-form/index.tsx:148 #: src/features/tournaments/components/tournament-form.tsx:88 msgid "Failed to upload logo" msgstr "No se pudo subir el logo" -#: src/features/activities/components/activities-table.tsx:330 +#: src/features/activities/components/activities-table.tsx:94 msgid "Failure" msgstr "Fallo" -#: src/app/routes/api/teams/upload-logo.ts:79 -#: src/app/routes/api/tournaments/upload-logo.ts:80 +#: src/app/routes/api/teams/upload-logo.ts:80 +#: src/app/routes/api/tournaments/upload-logo.ts:81 msgid "File too large. Maximum size is 10MB." msgstr "El archivo es demasiado grande. El tamaño máximo es 10MB." @@ -1112,7 +1213,7 @@ msgstr "El archivo es demasiado grande. El tamaño máximo es 10MB." msgid "Final" msgstr "Final" -#: src/features/tournaments/components/started-tournament/index.tsx:114 +#: src/features/tournaments/components/started-tournament/index.tsx:115 msgid "Final Results" msgstr "Resultados finales" @@ -1150,11 +1251,19 @@ msgstr "Inscripción de agente libre" msgid "Free Agents" msgstr "Agentes libres" -#: src/features/activities/components/activities-table.tsx:124 +#: src/features/telemetry/components/usage-screen.tsx:226 +msgid "Function" +msgstr "" + +#: src/features/activities/components/activities-table.tsx:165 msgid "Function Name" msgstr "Nombre de la función" -#: src/app/routes/_authed/admin/tournaments/$id/assign-partners.tsx:148 +#: src/app/routes/_authed/admin/telemetry.tsx:45 +msgid "Functions" +msgstr "" + +#: src/app/routes/_authed/admin/tournaments/$id/assign-partners.tsx:146 msgid "Generate Random Pairings" msgstr "Generar emparejamientos aleatorios" @@ -1163,11 +1272,15 @@ msgstr "Generar emparejamientos aleatorios" msgid "Go back" msgstr "Regresar" -#: src/components/DefaultCatchBoundary.tsx:67 -#: src/components/DefaultCatchBoundary.tsx:132 +#: src/components/DefaultCatchBoundary.tsx:69 +#: src/components/DefaultCatchBoundary.tsx:134 msgid "Go Back" msgstr "Regresar" +#: src/features/telemetry/components/usage-screen.tsx:54 +msgid "Good" +msgstr "" + #: src/themes/seventies/manifest.ts:6 msgid "Groovy" msgstr "Setentero" @@ -1229,13 +1342,13 @@ msgstr "¿Ya has jugado antes?" msgid "Head to Head" msgstr "Cara a cara" -#: src/components/DefaultCatchBoundary.tsx:103 +#: src/components/DefaultCatchBoundary.tsx:105 msgid "Hide details" msgstr "Ocultar detalles" -#: src/components/DefaultCatchBoundary.tsx:74 -#: src/components/DefaultCatchBoundary.tsx:125 -#: src/features/core/hooks/use-links.ts:11 +#: src/components/DefaultCatchBoundary.tsx:76 +#: src/components/DefaultCatchBoundary.tsx:127 +#: src/features/core/hooks/use-links.ts:12 msgid "Home" msgstr "Inicio" @@ -1263,7 +1376,7 @@ msgstr "Cómo funciona la puntuación de predicciones" #. placeholder {0}: response.status #. placeholder {1}: response.statusText -#: src/contexts/spotify-context.tsx:65 +#: src/contexts/spotify-context.tsx:64 msgid "HTTP {0}: {1}" msgstr "HTTP {0}: {1}" @@ -1275,6 +1388,10 @@ msgstr "Si es tu primera vez participando, selecciona <0>Nuevo jugador; si n msgid "Important: Enrolling as a free agent does not guarantee a tournament spot. To secure a spot, one team member must register through the app and select a walkout song." msgstr "Importante: inscribirte como agente libre no garantiza un lugar en el torneo. Para asegurar un lugar, un integrante del equipo debe registrarse en la app y elegir una canción de entrada." +#: src/features/telemetry/components/error-browser/error-browser.tsx:56 +msgid "Include resolved" +msgstr "" + #: src/features/login/components/code-prompt.tsx:43 msgid "Incorrect code" msgstr "Código incorrecto" @@ -1283,8 +1400,8 @@ msgstr "Código incorrecto" msgid "Invalid action" msgstr "Acción no válida" -#: src/app/routes/api/teams/upload-logo.ts:65 -#: src/app/routes/api/tournaments/upload-logo.ts:66 +#: src/app/routes/api/teams/upload-logo.ts:66 +#: src/app/routes/api/tournaments/upload-logo.ts:67 msgid "Invalid file type. Only JPEG, PNG and GIF are allowed." msgstr "Tipo de archivo no válido. Solo se permiten JPEG, PNG y GIF." @@ -1292,8 +1409,8 @@ msgstr "Tipo de archivo no válido. Solo se permiten JPEG, PNG y GIF." msgid "Invalid hex color format" msgstr "Formato de color hexadecimal no válido" -#: src/app/routes/api/teams/upload-logo.ts:33 -#: src/app/routes/api/tournaments/upload-logo.ts:34 +#: src/app/routes/api/teams/upload-logo.ts:34 +#: src/app/routes/api/tournaments/upload-logo.ts:35 msgid "Invalid input" msgstr "Entrada no válida" @@ -1302,10 +1419,11 @@ msgid "Invalid request" msgstr "Solicitud no válida" #: src/features/players/components/players-activity-table.tsx:41 +#: src/features/telemetry/components/shared/relative-time.tsx:14 msgid "Just now" msgstr "Justo ahora" -#: src/app/routes/_authed/admin/tournaments/run.$id.tsx:166 +#: src/app/routes/_authed/admin/tournaments/run.$id.tsx:107 msgid "Knockout Bracket" msgstr "Bracket eliminatorio" @@ -1347,7 +1465,7 @@ msgid "Last name must be 3-20 characters long and contain only letters" msgstr "El apellido debe tener entre 3 y 20 caracteres y contener solo letras" #. placeholder {0}: migrationStatus.result.totalBadgesEarned -#: src/features/admin/components/admin-page.tsx:74 +#: src/features/admin/components/dashboard/tools-list.tsx:73 msgid "Last recalculation: {0, plural, one {# badge earned} other {# badges earned}}" msgstr "Último recálculo: {0, plural, one {# insignia obtenida} other {# insignias obtenidas}}" @@ -1359,7 +1477,7 @@ msgstr "Claro" msgid "lip" msgstr "labios" -#: src/features/tournaments/components/started-tournament/index.tsx:121 +#: src/features/tournaments/components/started-tournament/index.tsx:122 msgid "Live Matches" msgstr "Partidos en vivo" @@ -1381,8 +1499,8 @@ msgstr "La ubicación es obligatoria" msgid "Logo" msgstr "Logo" -#: src/app/routes/api/teams/upload-logo.ts:46 -#: src/app/routes/api/tournaments/upload-logo.ts:47 +#: src/app/routes/api/teams/upload-logo.ts:47 +#: src/app/routes/api/tournaments/upload-logo.ts:48 msgid "Logo file is required" msgstr "El archivo del logo es obligatorio" @@ -1420,26 +1538,24 @@ msgstr "Hacer predicción" msgid "Make Your Prediction" msgstr "Haz tu predicción" +#: src/features/admin/components/dashboard/tournament-card.tsx:68 #: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:89 -#: src/features/tournaments/components/upcoming-tournament/index.tsx:143 +#: src/features/tournaments/components/upcoming-tournament/index.tsx:144 msgid "Manage" msgstr "Administrar" -#: src/app/routes/_authed/admin/tournaments/$id/assign-partners.tsx:28 +#: src/app/routes/_authed/admin/tournaments/$id/assign-partners.tsx:29 #: src/app/routes/_authed/admin/tournaments/$id/index.tsx:23 msgid "Manage {name}" msgstr "Administrar {name}" -#: src/features/tournaments/components/manage-tournament.tsx:117 -msgid "Manage Enrollments" -msgstr "Administrar inscripciones" - -#: src/features/tournaments/components/manage-tournament.tsx:76 +#: src/features/tournaments/components/enrollment/team-section.tsx:234 msgid "Manage Team Songs/Logos" msgstr "Administrar canciones/logos del equipo" #: src/app/routes/_authed/admin/tournaments/index.tsx:17 -#: src/features/admin/components/admin-page.tsx:50 +#: src/features/admin/components/dashboard/tools-list.tsx:49 +#: src/features/admin/components/dashboard/tournament-card.tsx:25 msgid "Manage Tournaments" msgstr "Administrar torneos" @@ -1450,6 +1566,10 @@ msgstr "Administrar torneos" msgid "margin" msgstr "margen" +#: src/features/telemetry/components/error-browser/error-group-row.tsx:98 +msgid "Mark resolved" +msgstr "" + #. placeholder {0}: match.order #. placeholder {1}: match.round + 1 #: src/features/bracket/components/match-dock.tsx:126 @@ -1514,11 +1634,15 @@ msgstr "Enfrentamiento" msgid "Matchup details" msgstr "Detalles del enfrentamiento" +#: src/features/telemetry/components/usage-screen.tsx:169 +msgid "MAU" +msgstr "" + #: src/features/players/components/league-head-to-head.tsx:120 msgid "Me" msgstr "Yo" -#: src/features/admin/components/admin-page.tsx:60 +#: src/features/admin/components/dashboard/tools-list.tsx:59 msgid "Migrate Badge Progress" msgstr "Migrar progreso de insignias" @@ -1532,6 +1656,7 @@ msgid "MMR Calculation:" msgstr "Cálculo de MMR:" #: src/features/teams/components/team-form/index.tsx:200 +#: src/features/telemetry/components/client-events-table.tsx:119 #: src/features/tournaments/components/tournament-form.tsx:137 msgid "Name" msgstr "Nombre" @@ -1545,7 +1670,8 @@ msgstr "El nombre es obligatorio" msgid "Name updated successfully!" msgstr "¡Nombre actualizado con éxito!" -#: src/app/routes/_authed/admin/tournaments/$id/assign-partners.tsx:132 +#: src/app/routes/_authed/admin/tournaments/$id/assign-partners.tsx:130 +#: src/features/tournaments/components/enrollment/enrollment-stats.tsx:35 msgid "Need at least 2 players to create teams" msgstr "Se necesitan al menos 2 jugadores para crear equipos" @@ -1554,6 +1680,10 @@ msgstr "Se necesitan al menos 2 jugadores para crear equipos" msgid "Need at least 4 teams to create a group stage format. Current team count: {0}" msgstr "Se necesitan al menos 4 equipos para crear un formato de fase de grupos. Equipos actuales: {0}" +#: src/features/telemetry/components/usage-screen.tsx:56 +msgid "Needs improvement" +msgstr "" + #: src/features/reactions/components/emoji-picker.tsx:23 msgid "nerd" msgstr "nerd" @@ -1567,6 +1697,7 @@ msgid "Neumorphic" msgstr "Neumórfico" #: src/features/players/components/players-activity-table.tsx:27 +#: src/features/telemetry/components/shared/relative-time.tsx:6 msgid "Never" msgstr "Nunca" @@ -1574,6 +1705,10 @@ msgstr "Nunca" msgid "Never active" msgstr "Nunca activo" +#: src/lib/telemetry/alerts.server.ts:124 +msgid "New client error" +msgstr "" + #: src/features/login/components/player-prompt/new-player-button.tsx:18 msgid "New Player" msgstr "Nuevo jugador" @@ -1582,7 +1717,7 @@ msgstr "Nuevo jugador" msgid "Next match" msgstr "Siguiente partido" -#: src/features/spotify/components/spotify-controls-bar.tsx:130 +#: src/features/spotify/components/spotify-controls-bar.tsx:129 msgid "Next track" msgstr "Siguiente canción" @@ -1597,11 +1732,11 @@ msgstr "No se encontró el token de acceso" msgid "No active device found. Please select a device first." msgstr "No se encontró ningún dispositivo activo. Selecciona un dispositivo primero." -#: src/features/spotify/components/spotify-controls-bar.tsx:204 +#: src/features/spotify/components/spotify-controls-bar.tsx:203 msgid "No active device. Please select a device in settings." msgstr "No hay ningún dispositivo activo. Selecciona un dispositivo en configuración." -#: src/features/tournaments/components/started-tournament/index.tsx:142 +#: src/features/tournaments/components/started-tournament/index.tsx:143 msgid "No active matches" msgstr "No hay partidos activos" @@ -1609,18 +1744,26 @@ msgstr "No hay partidos activos" msgid "No active playback to capture" msgstr "No hay reproducción activa para capturar" -#: src/features/activities/components/activities-table.tsx:234 +#: src/features/activities/components/activities-table.tsx:132 msgid "No Activities Found" msgstr "No se encontraron actividades" +#: src/features/admin/components/dashboard/activity-feed.tsx:78 +msgid "No activity yet" +msgstr "" + #: src/features/badges/components/badge-stats-table.tsx:45 msgid "No Badges Available" msgstr "No hay insignias disponibles" -#: src/features/spotify/components/spotify-sheet.tsx:155 +#: src/features/spotify/components/spotify-sheet.tsx:154 msgid "No devices found." msgstr "No se encontraron dispositivos." +#: src/features/telemetry/components/client-events-table.tsx:94 +msgid "No Events Found" +msgstr "" + #: src/features/tournaments/components/group-stage-view.tsx:294 msgid "No groups have been created yet" msgstr "Aún no se han creado grupos" @@ -1637,7 +1780,7 @@ msgstr "No se encontró actividad de jugadores" msgid "No players available" msgstr "No hay jugadores disponibles" -#: src/features/tournaments/components/edit-enrolled-players.tsx:145 +#: src/features/tournaments/components/enrollment/free-agent-section.tsx:168 #: src/features/tournaments/components/upcoming-tournament/enrolled-players-list-button.tsx:42 msgid "No players enrolled yet" msgstr "Aún no hay jugadores inscritos" @@ -1668,7 +1811,7 @@ msgstr "No se proporcionó ninguna instantánea" msgid "No song selected" msgstr "No hay canción seleccionada" -#: src/features/spotify/components/spotify-controls-bar.tsx:157 +#: src/features/spotify/components/spotify-controls-bar.tsx:156 msgid "No state captured" msgstr "No se capturó ningún estado" @@ -1689,7 +1832,7 @@ msgstr "No hay equipos asignados" msgid "No Teams Enrolled" msgstr "No hay equipos inscritos" -#: src/features/tournaments/components/edit-enrolled-teams.tsx:164 +#: src/features/tournaments/components/enrollment/team-section.tsx:253 msgid "No teams enrolled yet" msgstr "Aún no hay equipos inscritos" @@ -1701,10 +1844,19 @@ msgstr "No se encontraron equipos que coincidan con \"{search}\"" msgid "No tournaments found" msgstr "No se encontraron torneos" +#: src/features/admin/components/dashboard/tournament-card.tsx:18 +msgid "No tournaments yet" +msgstr "" + #: src/features/tournaments/components/tournament-card-list.tsx:40 msgid "No Tournaments Yet" msgstr "Aún no hay torneos" +#: src/features/admin/components/dashboard/errors-card.tsx:53 +#: src/features/telemetry/components/error-browser/error-browser.tsx:27 +msgid "No unresolved errors" +msgstr "" + #: src/features/predictions/components/prediction-leaderboard.tsx:136 msgid "Nobody made a prediction for this tournament." msgstr "Nadie hizo una predicción para este torneo." @@ -1717,6 +1869,10 @@ msgstr "Incorrecto" msgid "Not right — enter our score" msgstr "Incorrecto — ingresa nuestro marcador" +#: src/features/telemetry/components/error-browser/error-browser.tsx:28 +msgid "Nothing has broken in the last 30 days. Keep it up." +msgstr "" + #: src/features/settings/components/notifications-section.tsx:18 #: src/features/settings/components/notifications-section.tsx:59 msgid "Notifications" @@ -1738,7 +1894,7 @@ msgstr "Notificaciones desactivadas en este dispositivo" msgid "Notifications enabled on this device" msgstr "Notificaciones activadas en este dispositivo" -#: src/features/spotify/components/spotify-sheet.tsx:96 +#: src/features/spotify/components/spotify-sheet.tsx:95 msgid "Now Playing" msgstr "Sonando ahora" @@ -1758,7 +1914,15 @@ msgstr "Una vez inscrito, puedes ver a otros agentes libres y su número de tel msgid "One time code" msgstr "Código de un solo uso" -#: src/features/admin/components/admin-page.tsx:95 +#: src/features/admin/components/dashboard/now-strip.tsx:44 +msgid "Online now" +msgstr "" + +#: src/features/admin/components/dashboard/now-strip.tsx:46 +msgid "Open errors" +msgstr "" + +#: src/features/admin/components/dashboard/tools-list.tsx:89 msgid "Open Pocketbase" msgstr "Abrir Pocketbase" @@ -1767,7 +1931,7 @@ msgid "Opponent" msgstr "Rival" #: src/features/tournaments/components/upcoming-tournament/enroll-team/team-selection-view.tsx:60 -#: src/features/tournaments/components/upcoming-tournament/index.tsx:92 +#: src/features/tournaments/components/upcoming-tournament/index.tsx:93 msgid "or" msgstr "o" @@ -1800,7 +1964,23 @@ msgstr "Resumen" msgid "owo" msgstr "owo" -#: src/app/routes/_authed/admin/tournaments/$id/assign-partners.tsx:157 +#: src/features/telemetry/components/usage-screen.tsx:97 +msgid "p50" +msgstr "" + +#: src/features/telemetry/components/usage-screen.tsx:113 +msgid "p95" +msgstr "" + +#: src/features/admin/components/dashboard/usage-card.tsx:45 +msgid "Page views" +msgstr "" + +#: src/features/telemetry/components/usage-screen.tsx:177 +msgid "Page views by route" +msgstr "" + +#: src/app/routes/_authed/admin/tournaments/$id/assign-partners.tsx:155 msgid "Partner Assignments" msgstr "Asignación de compañeros" @@ -1808,7 +1988,11 @@ msgstr "Asignación de compañeros" msgid "Partners will be randomly assigned when enrollment closes" msgstr "Los compañeros se asignarán al azar cuando cierre la inscripción" -#: src/features/spotify/components/spotify-controls-bar.tsx:117 +#: src/features/telemetry/components/client-events-table.tsx:122 +msgid "Path" +msgstr "" + +#: src/features/spotify/components/spotify-controls-bar.tsx:116 msgid "Pause" msgstr "Pausar" @@ -1839,7 +2023,7 @@ msgstr "Elige primero los partidos anteriores — estos equipos todavía no est msgid "PICKS" msgstr "PICKS" -#: src/features/spotify/components/spotify-controls-bar.tsx:117 +#: src/features/spotify/components/spotify-controls-bar.tsx:116 msgid "Play" msgstr "Reproducir" @@ -1847,23 +2031,22 @@ msgstr "Reproducir" msgid "Playback control failed" msgstr "Falló el control de reproducción" -#: src/features/activities/components/activities-table.tsx:154 -#: src/features/admin/components/award-badges.tsx:35 +#: src/features/activities/components/activities-table.tsx:98 +#: src/features/activities/components/activities-table.tsx:188 +#: src/features/admin/components/award-badges.tsx:31 +#: src/features/telemetry/components/client-events-table.tsx:73 +#: src/features/telemetry/components/client-events-table.tsx:120 msgid "Player" msgstr "Jugador" -#: src/features/teams/components/team-form/players-picker.tsx:45 +#: src/features/teams/components/team-form/players-picker.tsx:46 msgid "Player 1" msgstr "Jugador 1" -#: src/features/teams/components/team-form/players-picker.tsx:53 +#: src/features/teams/components/team-form/players-picker.tsx:54 msgid "Player 2" msgstr "Jugador 2" -#: src/app/routes/_authed/admin/activities.tsx:59 -msgid "Player Activity" -msgstr "Actividad de jugadores" - #: src/features/login/components/player-prompt/index.tsx:46 msgid "Player already exists" msgstr "El jugador ya existe" @@ -1880,6 +2063,7 @@ msgstr "Jugador eliminado con éxito" msgid "Player Stats" msgstr "Estadísticas de jugador" +#: src/app/routes/_authed/admin/telemetry.tsx:54 #: src/features/teams/components/team-profile/index.tsx:29 msgid "Players" msgstr "Jugadores" @@ -1889,10 +2073,14 @@ msgid "Players are required" msgstr "Los jugadores son obligatorios" #. placeholder {0}: activeDevice.name -#: src/features/spotify/components/spotify-controls-bar.tsx:175 +#: src/features/spotify/components/spotify-controls-bar.tsx:174 msgid "Playing on {0}" msgstr "Reproduciendo en {0}" +#: src/features/telemetry/components/usage-screen.tsx:58 +msgid "Poor" +msgstr "" + #: src/features/tournaments/components/group-stage-view.tsx:314 msgid "Populate Knockout Bracket" msgstr "Llenar el bracket eliminatorio" @@ -1919,11 +2107,11 @@ msgstr "Las predicciones están abiertas" msgid "Previous match" msgstr "Partido anterior" -#: src/features/spotify/components/spotify-controls-bar.tsx:105 +#: src/features/spotify/components/spotify-controls-bar.tsx:104 msgid "Previous track" msgstr "Pista anterior" -#: src/features/core/hooks/use-links.ts:27 +#: src/features/core/hooks/use-links.ts:28 msgid "Profile" msgstr "Perfil" @@ -1931,6 +2119,10 @@ msgstr "Perfil" msgid "Progress " msgstr "Progreso " +#: src/features/telemetry/components/client-events-table.tsx:136 +msgid "Props" +msgstr "" + #: src/app/routes/_authed/tournaments/$id.predictions_.$playerId.tsx:106 #: src/features/predictions/components/prediction-leaderboard.tsx:261 msgid "PTS" @@ -1944,7 +2136,7 @@ msgstr "C" msgid "Quest Log" msgstr "Misiones" -#: src/app/routes/_authed/admin/tournaments/$id/assign-partners.tsx:167 +#: src/app/routes/_authed/admin/tournaments/$id/assign-partners.tsx:165 msgid "Re-roll" msgstr "Volver a sortear" @@ -1954,10 +2146,14 @@ msgstr "Reacciones" #. placeholder {0}: migrationStatus.playersProcessed #. placeholder {1}: migrationStatus.totalPlayers -#: src/features/admin/components/admin-page.tsx:67 +#: src/features/admin/components/dashboard/tools-list.tsx:66 msgid "Recalculating badges in the background ({0}/{1})" msgstr "Recalculando insignias en segundo plano ({0}/{1})" +#: src/features/admin/components/dashboard/activity-feed.tsx:34 +msgid "Recent activity" +msgstr "" + #: src/features/tournaments/components/started-tournament/sections/recent-results.tsx:34 msgid "Recently finished" msgstr "Recién terminados" @@ -1972,6 +2168,10 @@ msgstr "Regional" msgid "Regional Tournament" msgstr "Torneo regional" +#: src/features/telemetry/components/error-browser/error-group-row.tsx:98 +msgid "Reopen" +msgstr "" + #: src/features/bracket/components/match-report.tsx:127 msgid "Report cleared" msgstr "Reporte eliminado" @@ -1981,11 +2181,15 @@ msgstr "Reporte eliminado" msgid "Report Score" msgstr "Reportar marcador" -#: src/contexts/spotify-context.tsx:60 +#: src/contexts/spotify-context.tsx:59 msgid "Request failed" msgstr "Falló la solicitud" -#: src/features/spotify/components/spotify-controls-bar.tsx:157 +#: src/features/telemetry/components/error-browser/error-group-row.tsx:63 +msgid "Resolved" +msgstr "" + +#: src/features/spotify/components/spotify-controls-bar.tsx:156 msgid "Restore captured state" msgstr "Restaurar estado capturado" @@ -1993,7 +2197,7 @@ msgstr "Restaurar estado capturado" msgid "Results" msgstr "Resultados" -#: src/components/DefaultCatchBoundary.tsx:117 +#: src/components/DefaultCatchBoundary.tsx:119 msgid "Retry" msgstr "Reintentar" @@ -2010,16 +2214,21 @@ msgstr "carcajada" msgid "Round {0}" msgstr "Ronda {0}" +#: src/features/telemetry/components/client-events-table.tsx:125 +msgid "Route" +msgstr "" + #: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:142 #: src/features/tournaments/components/upcoming-tournament/rules-list-button.tsx:23 msgid "Rules" msgstr "Reglas" -#: src/app/routes/_authed/admin/tournaments/run.$id.tsx:38 +#: src/app/routes/_authed/admin/tournaments/run.$id.tsx:36 msgid "Run {name}" msgstr "Ejecutar {name}" -#: src/features/tournaments/components/manage-tournament.tsx:82 +#: src/features/admin/components/dashboard/tournament-card.tsx:53 +#: src/features/tournaments/components/manage-tournament.tsx:49 msgid "Run Tournament" msgstr "Iniciar torneo" @@ -2053,7 +2262,7 @@ msgstr "grito" msgid "Search for a Song" msgstr "Buscar una canción" -#: src/features/tournaments/components/edit-enrolled-players.tsx:120 +#: src/features/tournaments/components/enrollment/free-agent-section.tsx:133 msgid "Search for players to enroll..." msgstr "Buscar jugadores para inscribir..." @@ -2061,7 +2270,7 @@ msgstr "Buscar jugadores para inscribir..." msgid "Search for songs..." msgstr "Buscar canciones..." -#: src/features/tournaments/components/edit-enrolled-teams.tsx:139 +#: src/features/tournaments/components/enrollment/team-section.tsx:216 msgid "Search for teams to enroll..." msgstr "Buscar equipos para inscribir..." @@ -2095,11 +2304,11 @@ msgstr "El segmento no debe durar más de 15 segundos" msgid "Segment must be within song duration" msgstr "El segmento debe estar dentro de la duración de la canción" -#: src/features/admin/components/award-badges.tsx:71 +#: src/features/admin/components/award-badges.tsx:65 msgid "Select a badge" msgstr "Selecciona una insignia" -#: src/features/spotify/components/spotify-sheet.tsx:145 +#: src/features/spotify/components/spotify-sheet.tsx:144 msgid "Select a device..." msgstr "Selecciona un dispositivo..." @@ -2121,7 +2330,7 @@ msgstr "Selecciona una hora" msgid "Select Color" msgstr "Selecciona un color" -#: src/features/spotify/components/spotify-sheet.tsx:127 +#: src/features/spotify/components/spotify-sheet.tsx:126 msgid "Select Device" msgstr "Selecciona un dispositivo" @@ -2141,7 +2350,7 @@ msgstr "Selecciona fecha límite de inscripción" msgid "Select one of your existing teams" msgstr "Selecciona uno de tus equipos existentes" -#: src/features/admin/components/award-badges.tsx:86 +#: src/features/admin/components/award-badges.tsx:80 msgid "Select Player" msgstr "Selecciona jugador" @@ -2174,20 +2383,32 @@ msgstr "Enviar notificación de prueba" msgid "Series is tied" msgstr "La serie está empatada" -#: src/app/routes/_authed/admin/activities.tsx:58 -msgid "Server Functions" -msgstr "Funciones del servidor" +#: src/lib/telemetry/alerts.server.ts:153 +msgid "Server function failing" +msgstr "" -#: src/features/activities/components/activities-table.tsx:309 +#: src/features/telemetry/components/usage-screen.tsx:186 +msgid "Server functions" +msgstr "" + +#: src/features/activities/components/activities-table.tsx:82 msgid "serverFn name" msgstr "nombre de serverFn" +#: src/features/telemetry/components/client-events-table.tsx:128 +msgid "Session" +msgstr "" + +#: src/features/telemetry/components/usage-screen.tsx:172 +msgid "Sessions" +msgstr "" + #: src/app/routes/_authed/settings.tsx:16 #: src/features/core/components/settings-button.tsx:46 msgid "Settings" msgstr "Configuración" -#: src/components/DefaultCatchBoundary.tsx:103 +#: src/components/DefaultCatchBoundary.tsx:105 msgid "Show details" msgstr "Mostrar detalles" @@ -2213,7 +2434,11 @@ msgstr "Cuaderno" msgid "Skeuomorphic" msgstr "Esqueuomórfico" -#: src/components/DefaultCatchBoundary.tsx:89 +#: src/features/activities/components/activities-table.tsx:113 +msgid "Slow >1s (this page)" +msgstr "" + +#: src/components/DefaultCatchBoundary.tsx:91 msgid "Something went wrong" msgstr "Algo salió mal" @@ -2241,8 +2466,8 @@ msgstr "El segmento de la canción no debe durar más de 15 segundos" msgid "Song start time is required" msgstr "Se requiere la hora de inicio de la canción" -#: src/features/activities/components/activities-table.tsx:345 #: src/features/players/components/player-stats-table.tsx:340 +#: src/features/telemetry/components/shared/search-filter-bar.tsx:102 msgid "Sort:" msgstr "Ordenar:" @@ -2250,7 +2475,7 @@ msgstr "Ordenar:" msgid "Sorted by win percentage" msgstr "Ordenado por porcentaje de victorias" -#: src/features/spotify/components/spotify-sheet.tsx:68 +#: src/features/spotify/components/spotify-sheet.tsx:67 msgid "Spotify Controls" msgstr "Controles de Spotify" @@ -2262,7 +2487,7 @@ msgstr "Se requiere Spotify Premium para controlar la reproducción." msgid "Spotify premium required" msgstr "Se requiere Spotify Premium" -#: src/features/spotify/components/spotify-controls-bar.tsx:180 +#: src/features/spotify/components/spotify-controls-bar.tsx:179 msgid "Spotify settings" msgstr "Configuración de Spotify" @@ -2292,6 +2517,10 @@ msgstr "Se requiere la hora de inicio" msgid "Start typing to search..." msgstr "Empieza a escribir para buscar..." +#: src/features/admin/components/dashboard/tournament-card.tsx:43 +msgid "Starts in" +msgstr "" + #: src/features/players/components/player-stats-table.tsx:372 msgid "Stat abbreviations and MMR info" msgstr "Abreviaturas de estadísticas e información de MMR" @@ -2300,7 +2529,7 @@ msgstr "Abreviaturas de estadísticas e información de MMR" msgid "Stat Abbreviations:" msgstr "Abreviaturas de estadísticas:" -#: src/features/core/hooks/use-links.ts:16 +#: src/features/core/hooks/use-links.ts:17 #: src/features/players/components/profile/index.tsx:26 #: src/features/teams/components/team-profile/index.tsx:34 msgid "Statistics" @@ -2315,8 +2544,8 @@ msgstr "Estadísticas" msgid "Stats Comparison" msgstr "Comparación de estadísticas" -#: src/features/activities/components/activities-table.tsx:131 -#: src/features/activities/components/activities-table.tsx:321 +#: src/features/activities/components/activities-table.tsx:85 +#: src/features/activities/components/activities-table.tsx:167 msgid "Status" msgstr "Estado" @@ -2328,8 +2557,8 @@ msgstr "Estado" msgid "Submit" msgstr "Enviar" -#: src/features/activities/components/activities-table.tsx:138 -#: src/features/activities/components/activities-table.tsx:329 +#: src/features/activities/components/activities-table.tsx:93 +#: src/features/activities/components/activities-table.tsx:174 msgid "Success" msgstr "Éxito" @@ -2337,8 +2566,7 @@ msgstr "Éxito" msgid "Successfully logged in. Welcome back!" msgstr "Inicio de sesión exitoso. ¡Bienvenido de vuelta!" -#: src/features/activities/components/activities-table.tsx:42 -#: src/features/activities/components/activities-table.tsx:112 +#: src/features/activities/components/activities-table.tsx:53 #: src/features/settings/components/color-scheme-picker.tsx:47 msgid "System" msgstr "Sistema" @@ -2351,11 +2579,11 @@ msgstr "T" msgid "Tap an opponent below to compare" msgstr "Toca un oponente abajo para comparar" -#: src/components/ios-install-prompt.tsx:43 +#: src/components/ios-install-prompt.tsx:48 msgid "Tap Menu (⋮) → Add to Home screen" msgstr "Toca Menú (⋮) → Agregar a pantalla de inicio" -#: src/components/ios-install-prompt.tsx:42 +#: src/components/ios-install-prompt.tsx:47 msgid "Tap Share → Add to Home Screen" msgstr "Toca Compartir → Agregar a pantalla de inicio" @@ -2385,7 +2613,7 @@ msgstr "Detalles del equipo" msgid "Team enrolled successfully!" msgstr "¡Equipo inscrito exitosamente!" -#: src/app/routes/api/teams/upload-logo.ts:92 +#: src/app/routes/api/teams/upload-logo.ts:93 #: src/features/teams/components/team-profile/index.tsx:21 msgid "Team not found" msgstr "Equipo no encontrado" @@ -2419,11 +2647,15 @@ msgstr "Equipos" msgid "Teams created successfully!" msgstr "¡Equipos creados exitosamente!" +#: src/app/routes/_authed/admin/telemetry.tsx:20 +msgid "Telemetry" +msgstr "" + #: src/themes/terminal/manifest.ts:6 msgid "Terminal" msgstr "Terminal" -#: src/app/routes/api/push/test.ts:29 +#: src/app/routes/api/push/test.ts:30 msgid "Test notification — push is working on this device." msgstr "Notificación de prueba: las notificaciones push funcionan en este dispositivo." @@ -2498,6 +2730,10 @@ msgstr "{0, plural, one {El mejor equipo de cada grupo avanzará} other {Los mej msgid "Top {n} advance" msgstr "Los mejores {n} avanzan" +#: src/features/telemetry/components/usage-screen.tsx:191 +msgid "Top functions by call count" +msgstr "" + #: src/features/matches/components/team-head-to-head-sheet.tsx:158 #: src/features/players/components/player-head-to-head-sheet.tsx:193 msgid "Total Cups" @@ -2517,7 +2753,7 @@ msgstr "Torneo creado, pero falló la carga del logo: {0}" msgid "Tournament created successfully!" msgstr "¡Torneo creado exitosamente!" -#: src/app/routes/api/tournaments/upload-logo.ts:93 +#: src/app/routes/api/tournaments/upload-logo.ts:94 msgid "Tournament not found" msgstr "Torneo no encontrado" @@ -2536,7 +2772,7 @@ msgid "Tournament updated successfully!" msgstr "¡Torneo actualizado con éxito!" #: src/app/routes/_authed/tournaments/index.tsx:17 -#: src/features/core/hooks/use-links.ts:21 +#: src/features/core/hooks/use-links.ts:22 #: src/features/teams/components/team-profile/index.tsx:53 #: src/features/teams/components/team-profile/skeleton.tsx:80 msgid "Tournaments" @@ -2550,6 +2786,10 @@ msgstr "Se requiere trackId para la acción playTrack" msgid "Try the new themes" msgstr "Prueba los nuevos temas" +#: src/features/admin/components/dashboard/quiet-boundary.tsx:26 +msgid "Unavailable right now" +msgstr "" + #: src/features/tournaments/components/upcoming-tournament/unenroll-free-agent.tsx:26 #: src/features/tournaments/components/upcoming-tournament/unenroll-team.tsx:34 msgid "Unenroll" @@ -2563,12 +2803,12 @@ msgstr "¿Cancelar inscripción del torneo?" msgid "Unenroll Team" msgstr "Cancelar inscripción del equipo" -#: src/components/DefaultCatchBoundary.tsx:39 +#: src/components/DefaultCatchBoundary.tsx:40 msgid "Unknown error" msgstr "Error desconocido" -#: src/app/routes/api/teams/upload-logo.ts:141 -#: src/app/routes/api/tournaments/upload-logo.ts:139 +#: src/app/routes/api/teams/upload-logo.ts:142 +#: src/app/routes/api/tournaments/upload-logo.ts:140 msgid "Unknown error occurred" msgstr "Ocurrió un error desconocido" @@ -2585,7 +2825,7 @@ msgstr "Jugador desconocido" msgid "Unnamed Player" msgstr "Jugador sin nombre" -#: src/features/tournaments/components/started-tournament/index.tsx:128 +#: src/features/tournaments/components/started-tournament/index.tsx:129 msgid "Upcoming" msgstr "Próximos" @@ -2607,10 +2847,23 @@ msgstr "Actualizar equipo" msgid "Update Tournament" msgstr "Actualizar torneo" -#: src/features/activities/components/activities-table.tsx:176 +#: src/app/routes/_authed/admin/telemetry.tsx:57 +msgid "Usage" +msgstr "" + +#: src/features/admin/components/dashboard/usage-card.tsx:34 +msgid "Usage, 7 days" +msgstr "" + +#: src/features/activities/components/activities-table.tsx:202 +#: src/features/telemetry/components/client-events-table.tsx:146 msgid "User Agent" msgstr "User Agent" +#: src/features/telemetry/components/client-events-table.tsx:131 +msgid "Value" +msgstr "" + #: src/features/bracket/components/match-card.tsx:201 #: src/features/bracket/components/match-card.tsx:226 msgid "versus" @@ -2645,6 +2898,10 @@ msgstr "Ver predicciones" msgid "Viewport" msgstr "Viewport" +#: src/features/telemetry/components/usage-screen.tsx:211 +msgid "Vitals" +msgstr "" + #: src/app/routes/api/spotify/playback.ts:70 msgid "volumePercent must be a number" msgstr "volumePercent debe ser un número" @@ -2680,6 +2937,10 @@ msgstr "Segmento de entrada ({segmentDuration}s)" msgid "Walkout Song" msgstr "Canción de entrada" +#: src/features/telemetry/components/usage-screen.tsx:165 +msgid "WAU" +msgstr "" + #: src/features/tournaments/components/group-stage-view.tsx:407 msgid "WC" msgstr "COM" @@ -2731,7 +2992,7 @@ msgstr "Tú y 1 más" msgid "You are not authorized to perform this action" msgstr "No tienes autorización para realizar esta acción" -#: src/components/DefaultCatchBoundary.tsx:60 +#: src/components/DefaultCatchBoundary.tsx:62 msgid "You don't have permission to access this page." msgstr "No tienes permiso para acceder a esta página." @@ -2755,7 +3016,7 @@ msgstr "¡Eres el primer agente libre!" msgid "You've been added as a free agent!" msgstr "¡Te agregamos como agente libre!" -#: src/components/DefaultCatchBoundary.tsx:46 +#: src/components/DefaultCatchBoundary.tsx:48 msgid "You've been logged out" msgstr "Se cerró tu sesión" diff --git a/src/locales/ja/messages.po b/src/locales/ja/messages.po index d63374b..9136b9e 100644 --- a/src/locales/ja/messages.po +++ b/src/locales/ja/messages.po @@ -18,7 +18,7 @@ msgid " (Losers)" msgstr " (敗者側)" #. placeholder {0}: activeDevice.volume_percent -#: src/features/spotify/components/spotify-sheet.tsx:163 +#: src/features/spotify/components/spotify-sheet.tsx:162 msgid " (Volume: {0}%)" msgstr " (音量:{0}%)" @@ -26,7 +26,7 @@ msgstr " (音量:{0}%)" msgid "(713) 867-5309" msgstr "(713) 867-5309" -#: src/features/spotify/components/spotify-sheet.tsx:148 +#: src/features/spotify/components/spotify-sheet.tsx:147 msgid "(Active)" msgstr "(アクティブ)" @@ -38,14 +38,14 @@ msgstr "{0, plural, one {#ブラケット参加} other {#ブラケット参加}} #. placeholder {0}: enrolledPlayers.length #. placeholder {0}: players.length #: src/features/players/components/players-activity-table.tsx:100 -#: src/features/tournaments/components/edit-enrolled-players.tsx:135 +#: src/features/tournaments/components/enrollment/free-agent-section.tsx:158 msgid "{0, plural, one {# player} other {# players}}" msgstr "{0, plural, one {#人のプレイヤー} other {#人のプレイヤー}}" #. placeholder {0}: enrolledTeams.length #. placeholder {0}: group.teams.length #. placeholder {0}: orderedTeams.length -#: src/features/tournaments/components/edit-enrolled-teams.tsx:154 +#: src/features/tournaments/components/enrollment/team-section.tsx:243 #: src/features/tournaments/components/group-preview.tsx:27 #: src/features/tournaments/components/seed-tournament.tsx:116 #: src/features/tournaments/components/setup-group-stage.tsx:224 @@ -53,10 +53,15 @@ msgid "{0, plural, one {# team} other {# teams}}" msgstr "{0, plural, one {#チーム} other {#チーム}}" #. placeholder {0}: result.totalItems -#: src/features/activities/components/activities-table.tsx:341 +#: src/features/activities/components/activities-table.tsx:125 msgid "{0, plural, one {# total activity} other {# total activities}}" msgstr "{0, plural, one {アクティビティ合計#件} other {アクティビティ合計#件}}" +#. placeholder {0}: result.totalItems +#: src/features/telemetry/components/client-events-table.tsx:87 +msgid "{0, plural, one {# total event} other {# total events}}" +msgstr "" + #. placeholder {0}: selectedConfig.total_group_matches #: src/features/tournaments/components/setup-group-stage.tsx:249 msgid "{0, plural, one {# total group stage match} other {# total group stage matches}}" @@ -73,7 +78,9 @@ msgid "{0, plural, one {# wildcard} other {# wildcards}}" msgstr "{0, plural, one {ワイルドカード#枠} other {ワイルドカード#枠}}" #. placeholder {0}: freeAgents.length -#: src/app/routes/_authed/admin/tournaments/$id/assign-partners.tsx:126 +#. placeholder {0}: props.count +#: src/app/routes/_authed/admin/tournaments/$id/assign-partners.tsx:124 +#: src/features/tournaments/components/enrollment/enrollment-stats.tsx:22 msgid "{0, plural, one {player enrolled} other {players enrolled}}" msgstr "{0, plural, one {人のプレイヤーが登録済み} other {人のプレイヤーが登録済み}}" @@ -88,6 +95,11 @@ msgstr "{0} {1}" msgid "{0} Badge Holders" msgstr "{0}バッジ保有者" +#. placeholder {0}: recentErrors.totalItems +#: src/lib/telemetry/alerts.server.ts:104 +msgid "{0} client errors in the last 15 minutes" +msgstr "" + #. placeholder {0}: match.away?.name #. placeholder {0}: match.home?.name #: src/features/bracket/components/match-form.tsx:90 @@ -131,8 +143,9 @@ msgid "{0}d" msgstr "{0}日" #. placeholder {0}: activity.duration -#: src/features/activities/components/activities-table.tsx:83 -#: src/features/activities/components/activities-table.tsx:163 +#. placeholder {0}: selectedActivity.duration +#: src/features/activities/components/activities-table.tsx:148 +#: src/features/activities/components/activities-table.tsx:193 msgid "{0}ms" msgstr "{0}ms" @@ -145,15 +158,22 @@ msgstr "{count, plural, one {プレイヤー(#)} other {プレイヤー(# msgid "{count, plural, one {Teams (#)} other {Teams (#)}}" msgstr "{count, plural, one {チーム(#)} other {チーム(#)}}" +#: src/lib/telemetry/alerts.server.ts:167 +msgid "{deniedCount} denied admin access attempts in the last 15 minutes" +msgstr "" + #: src/features/players/components/players-activity-table.tsx:44 +#: src/features/telemetry/components/shared/relative-time.tsx:17 msgid "{diffDays}d ago" msgstr "{diffDays}日前" #: src/features/players/components/players-activity-table.tsx:43 +#: src/features/telemetry/components/shared/relative-time.tsx:16 msgid "{diffHours}h ago" msgstr "{diffHours}時間前" #: src/features/players/components/players-activity-table.tsx:42 +#: src/features/telemetry/components/shared/relative-time.tsx:15 msgid "{diffMins}m ago" msgstr "{diffMins}分前" @@ -177,7 +197,11 @@ msgstr "{n, plural, one {#試合保証} other {#試合保証}}" msgid "{n, plural, one {# wildcard} other {# wildcards}}" msgstr "{n, plural, one {#ワイルドカード} other {#ワイルドカード}}" -#: src/app/routes/_authed/admin/tournaments/$id/teams.tsx:23 +#: src/lib/telemetry/alerts.server.ts:154 +msgid "{name} failed {count} times in the last 15 minutes" +msgstr "" + +#: src/app/routes/_authed/admin/tournaments/$id/teams.tsx:24 msgid "{name} Teams" msgstr "{name}のチーム" @@ -331,6 +355,10 @@ msgstr "2. 早い提出" msgid "2nd place" msgstr "2位" +#: src/features/telemetry/components/usage-screen.tsx:256 +msgid "30d" +msgstr "" + #: src/features/teams/components/team-form/duration-picker.tsx:22 msgid "30s" msgstr "30秒" @@ -343,6 +371,10 @@ msgstr "3位" msgid "5s" msgstr "5秒" +#: src/features/telemetry/components/usage-screen.tsx:255 +msgid "7d" +msgstr "" + #. placeholder {0}: number?.slice(0, 3) #. placeholder {1}: number?.slice(3, 6) #. placeholder {2}: number?.slice(6) @@ -350,6 +382,11 @@ msgstr "5秒" msgid "A code was sent to +1 ({0}) {1}-{2}" msgstr "認証コードを +1 ({0}) {1}-{2} に送信しました" +#. placeholder {0}: info.message.slice(0, 120) +#: src/lib/telemetry/alerts.server.ts:125 +msgid "A new error appeared: {0}" +msgstr "" + #: src/features/bracket/components/match-report.tsx:304 msgid "a team" msgstr "チーム" @@ -370,7 +407,7 @@ msgstr "アクセントカラー" msgid "Access denied" msgstr "アクセスが拒否されました" -#: src/components/DefaultCatchBoundary.tsx:58 +#: src/components/DefaultCatchBoundary.tsx:60 msgid "Access Denied" msgstr "アクセス拒否" @@ -379,34 +416,33 @@ msgid "Account created successfully!" msgstr "アカウントが作成されました!" #. placeholder {0}: activeDevice.name -#: src/features/spotify/components/spotify-sheet.tsx:161 +#: src/features/spotify/components/spotify-sheet.tsx:160 msgid "Active device: {0}" msgstr "アクティブなデバイス: {0}" -#: src/app/routes/_authed/admin/activities.tsx:21 -#: src/features/admin/components/admin-page.tsx:90 -msgid "Activities" -msgstr "アクティビティ" +#: src/features/admin/components/dashboard/usage-card.tsx:39 +msgid "Active players" +msgstr "" -#: src/features/activities/components/activities-table.tsx:120 +#: src/features/activities/components/activities-table.tsx:159 msgid "Activity Details" msgstr "アクティビティの詳細" -#: src/components/ios-install-prompt.tsx:58 +#: src/components/ios-install-prompt.tsx:63 msgid "Add FLXN to your home screen for a smoother experience" msgstr "FLXNをホーム画面に追加すると、もっと快適に使えます" -#: src/features/tournaments/components/edit-enrolled-players.tsx:117 +#: src/features/tournaments/components/enrollment/free-agent-section.tsx:130 msgid "Add Player" msgstr "プレイヤーを追加" -#: src/features/tournaments/components/edit-enrolled-teams.tsx:136 +#: src/features/tournaments/components/enrollment/team-section.tsx:213 msgid "Add Team" msgstr "チームを追加" -#: src/app/routes/_authed/admin.tsx:14 -#: src/app/routes/_authed/admin/index.tsx:9 -#: src/features/core/hooks/use-links.ts:36 +#: src/app/routes/_authed/admin.tsx:15 +#: src/app/routes/_authed/admin/index.tsx:22 +#: src/features/core/hooks/use-links.ts:37 msgid "Admin" msgstr "管理者" @@ -415,7 +451,7 @@ msgid "Aero" msgstr "エアロ" #: src/app/routes/_authed/stats.tsx:59 -#: src/features/activities/components/activities-table.tsx:328 +#: src/features/activities/components/activities-table.tsx:92 #: src/features/players/components/profile/index.tsx:28 msgid "All" msgstr "すべて" @@ -432,7 +468,7 @@ msgstr "グループステージの全試合が終了しました!勝ち上が msgid "ALM" msgstr "ALM" -#: src/components/DefaultCatchBoundary.tsx:92 +#: src/components/DefaultCatchBoundary.tsx:94 msgid "An error occurred while loading this page." msgstr "このページの読み込み中にエラーが発生しました。" @@ -448,6 +484,10 @@ msgstr "予期しないエラーが発生しました。もう一度お試しく msgid "Announce matchup" msgstr "対戦カードを発表" +#: src/features/telemetry/components/client-events-table.tsx:49 +msgid "Anonymous" +msgstr "" + #: src/app/routes/_authed/settings.tsx:35 msgid "Appearance" msgstr "外観" @@ -456,7 +496,7 @@ msgstr "外観" msgid "Are you sure you want to unenroll from this tournament? You can enroll again at any point before the deadline." msgstr "このトーナメントから登録を解除してもよろしいですか?締め切りまではいつでも再登録できます。" -#: src/features/activities/components/activities-table.tsx:200 +#: src/features/activities/components/activities-table.tsx:221 msgid "Arguments" msgstr "引数" @@ -464,7 +504,7 @@ msgstr "引数" msgid "Art Deco" msgstr "アールデコ" -#: src/features/tournaments/components/manage-tournament.tsx:69 +#: src/features/tournaments/components/enrollment/free-agent-section.tsx:149 msgid "Assign Partners" msgstr "パートナーを割り当て" @@ -473,12 +513,12 @@ msgstr "パートナーを割り当て" msgid "At least one team must have 10 cups" msgstr "少なくとも一方のチームは10カップである必要があります" -#: src/contexts/spotify-context.tsx:158 +#: src/contexts/spotify-context.tsx:157 msgid "Authentication failed: {error}" msgstr "認証に失敗しました: {error}" #. placeholder {0}: decodeURIComponent(details) -#: src/contexts/spotify-context.tsx:157 +#: src/contexts/spotify-context.tsx:156 msgid "Authentication failed: {error} - {0}" msgstr "認証に失敗しました: {error} - {0}" @@ -520,15 +560,15 @@ msgstr "平均敗差" msgid "Avg Win Margin" msgstr "平均勝差" -#: src/features/admin/components/award-badges.tsx:104 +#: src/features/admin/components/award-badges.tsx:98 msgid "Award Badge" msgstr "バッジを付与" -#: src/features/admin/components/admin-page.tsx:55 +#: src/features/admin/components/dashboard/tools-list.tsx:54 msgid "Award Badges" msgstr "バッジを付与" -#: src/features/admin/components/award-badges.tsx:65 +#: src/features/admin/components/award-badges.tsx:59 msgid "Award Manual Badge" msgstr "手動でバッジを付与" @@ -554,23 +594,23 @@ msgstr "AWM" msgid "Back" msgstr "戻る" -#: src/features/admin/components/award-badges.tsx:37 +#: src/features/admin/components/award-badges.tsx:33 msgid "Badge awarded to {playerName}" msgstr "{playerName}にバッジを付与しました" -#: src/features/admin/components/admin-page.tsx:86 +#: src/features/admin/components/dashboard/tools-list.tsx:85 msgid "Badge recalculation failed. Check the server logs." msgstr "バッジの再計算に失敗しました。サーバーログを確認してください。" -#: src/features/admin/components/admin-page.tsx:39 +#: src/features/admin/components/dashboard/tools-list.tsx:38 msgid "Badge recalculation is already running" msgstr "バッジの再計算はすでに実行中です" -#: src/features/admin/components/admin-page.tsx:38 +#: src/features/admin/components/dashboard/tools-list.tsx:37 msgid "Badge recalculation started in the background" msgstr "バックグラウンドでバッジの再計算を開始しました" -#: src/features/admin/components/award-badges.tsx:70 +#: src/features/admin/components/award-badges.tsx:64 msgid "Badge Type" msgstr "バッジの種類" @@ -592,12 +632,12 @@ msgid "Both teams cannot have 10 cups" msgstr "両チーム同時に10カップにはなりません" #: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:106 -#: src/features/tournaments/components/upcoming-tournament/index.tsx:150 +#: src/features/tournaments/components/upcoming-tournament/index.tsx:151 msgid "Bracket" msgstr "ブラケット" #: src/app/routes/_authed/admin/preview.tsx:13 -#: src/features/admin/components/admin-page.tsx:102 +#: src/features/admin/components/dashboard/tools-list.tsx:96 msgid "Bracket Preview" msgstr "ブラケットプレビュー" @@ -614,6 +654,10 @@ msgstr "ブルータル" msgid "CA" msgstr "CA" +#: src/features/admin/components/dashboard/now-strip.tsx:52 +msgid "Calls today" +msgstr "" + #: src/components/sheet/slide-panel/slide-panel.tsx:41 #: src/components/sheet/slide-panel/slide-panel.tsx:190 #: src/features/bracket/components/match-form.tsx:160 @@ -633,11 +677,12 @@ msgstr "キャンセル" msgid "Cannot create groups" msgstr "グループを作成できません" -#: src/app/routes/_authed/admin/tournaments/$id/assign-partners.tsx:138 +#: src/app/routes/_authed/admin/tournaments/$id/assign-partners.tsx:136 +#: src/features/tournaments/components/enrollment/enrollment-stats.tsx:41 msgid "Cannot create teams with an odd number of players. Please have one player unenroll." msgstr "参加者が奇数のためチームを作成できません。どなたか1人、参加を取り消してください。" -#: src/features/spotify/components/spotify-controls-bar.tsx:144 +#: src/features/spotify/components/spotify-controls-bar.tsx:143 msgid "Capture current state" msgstr "現在の状態をキャプチャ" @@ -659,7 +704,7 @@ msgstr "チャンピオン" msgid "Change Logo" msgstr "ロゴを変更" -#: src/features/admin/components/award-badges.tsx:87 +#: src/features/admin/components/award-badges.tsx:81 msgid "Choose a player" msgstr "プレイヤーを選択" @@ -671,6 +716,18 @@ msgstr "クレイ" msgid "Clear selected player" msgstr "選択したプレイヤーを解除" +#: src/lib/telemetry/alerts.server.ts:103 +msgid "Client error spike" +msgstr "" + +#: src/features/admin/components/dashboard/errors-card.tsx:21 +msgid "Client errors" +msgstr "" + +#: src/features/telemetry/components/usage-screen.tsx:203 +msgid "Client errors per day" +msgstr "" + #: src/components/sheet/modal.tsx:35 #: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:169 #: src/features/tournaments/components/upcoming-tournament/rules-list-button.tsx:36 @@ -717,7 +774,7 @@ msgstr "確認" msgid "Confirm {scoreText}" msgstr "{scoreText}を確認" -#: src/app/routes/_authed/admin/tournaments/$id/assign-partners.tsx:175 +#: src/app/routes/_authed/admin/tournaments/$id/assign-partners.tsx:173 msgid "Confirm & Create Teams" msgstr "確認してチームを作成" @@ -738,15 +795,15 @@ msgstr "設定を確認" msgid "Confirm Unenrollment" msgstr "参加取り消しを確認" -#: src/features/spotify/components/spotify-controls-bar.tsx:52 +#: src/features/spotify/components/spotify-controls-bar.tsx:51 msgid "Connect Spotify" msgstr "Spotifyと連携" -#: src/features/spotify/components/spotify-sheet.tsx:87 +#: src/features/spotify/components/spotify-sheet.tsx:86 msgid "Connect with Spotify" msgstr "Spotifyと連携する" -#: src/features/spotify/components/spotify-sheet.tsx:75 +#: src/features/spotify/components/spotify-sheet.tsx:74 msgid "Connect your Spotify account" msgstr "Spotifyアカウントを連携しましょう" @@ -796,7 +853,8 @@ msgstr "チームを作成" msgid "Create Tournament" msgstr "トーナメントを作成" -#: src/features/activities/components/activities-table.tsx:168 +#: src/features/activities/components/activities-table.tsx:198 +#: src/features/telemetry/components/client-events-table.tsx:149 msgid "Created" msgstr "作成日" @@ -825,6 +883,15 @@ msgstr "決めたカップ" msgid "Custom Hex Code" msgstr "カスタムHEXコード" +#: src/features/admin/components/dashboard/usage-card.tsx:41 +#: src/features/telemetry/components/usage-screen.tsx:159 +msgid "Daily active players" +msgstr "" + +#: src/features/admin/components/dashboard/usage-card.tsx:47 +msgid "Daily page views" +msgstr "" + #: src/features/settings/components/color-scheme-picker.tsx:39 msgid "Dark" msgstr "ダーク" @@ -833,7 +900,7 @@ msgstr "ダーク" msgid "Dark Academia" msgstr "ダークアカデミア" -#: src/features/activities/components/activities-table.tsx:356 +#: src/features/activities/components/activities-table.tsx:119 msgid "Date" msgstr "日付" @@ -845,6 +912,10 @@ msgstr "深海" msgid "Default" msgstr "デフォルト" +#: src/lib/telemetry/alerts.server.ts:166 +msgid "Denied access attempt" +msgstr "" + #: src/features/tournaments/components/tournament-form.tsx:150 msgid "Description" msgstr "説明" @@ -853,11 +924,11 @@ msgstr "説明" msgid "deviceId is required for transfer action" msgstr "転送操作にはdeviceIdが必要です" -#: src/features/spotify/components/spotify-sheet.tsx:188 +#: src/features/spotify/components/spotify-sheet.tsx:187 msgid "Disconnect Spotify" msgstr "Spotifyの連携を解除" -#: src/components/ios-install-prompt.tsx:65 +#: src/components/ios-install-prompt.tsx:70 msgid "Dismiss" msgstr "閉じる" @@ -871,28 +942,25 @@ msgstr "これで合っていますか?" msgid "Downstream matches were reset because the correction changed who advances." msgstr "修正により勝ち上がりの相手が変わったため、以降の試合はリセットされました。" -#: src/features/activities/components/activities-table.tsx:161 -#: src/features/activities/components/activities-table.tsx:372 +#: src/features/activities/components/activities-table.tsx:120 +#: src/features/activities/components/activities-table.tsx:190 msgid "Duration" msgstr "所要時間" +#: src/features/telemetry/components/usage-screen.tsx:233 +msgid "Duration p50 / p95" +msgstr "" + #: src/features/tournaments/components/tournament-stats.tsx:176 msgid "Earlier regional formats aren't supported in the app and order of matches or displayed winners may be unreliable." msgstr "以前の地域フォーマットはアプリでサポートされていないため、試合の順序や表示される勝者が正しくない場合があります。" #. placeholder {0}: team.name #: src/features/teams/components/manage-teams.tsx:35 +#: src/features/tournaments/components/enrollment/team-section.tsx:96 msgid "Edit {0}" msgstr "{0}を編集" -#: src/features/tournaments/components/manage-tournament.tsx:117 -msgid "Edit Enrolled Teams" -msgstr "参加チームを編集" - -#: src/features/tournaments/components/manage-tournament.tsx:63 -msgid "Edit Enrollments" -msgstr "参加登録を編集" - #: src/features/bracket/components/match-card.tsx:392 msgid "Edit Match" msgstr "試合を編集" @@ -906,17 +974,18 @@ msgstr "試合スコアを編集" msgid "Edit Prediction" msgstr "予想を編集" -#: src/features/tournaments/components/manage-tournament.tsx:58 -#: src/features/tournaments/components/manage-tournament.tsx:109 +#: src/features/tournaments/components/manage-tournament.tsx:65 +#: src/features/tournaments/components/manage-tournament.tsx:92 msgid "Edit Rules" msgstr "ルールを編集" #: src/features/teams/components/manage-teams.tsx:35 +#: src/features/tournaments/components/enrollment/team-section.tsx:96 msgid "Edit Team" msgstr "チームを編集" -#: src/features/tournaments/components/manage-tournament.tsx:53 -#: src/features/tournaments/components/manage-tournament.tsx:89 +#: src/features/tournaments/components/manage-tournament.tsx:60 +#: src/features/tournaments/components/manage-tournament.tsx:72 msgid "Edit Tournament" msgstr "トーナメントを編集" @@ -940,6 +1009,10 @@ msgstr "終了日(任意)" msgid "End Match" msgstr "試合を終了" +#: src/features/telemetry/components/usage-screen.tsx:156 +msgid "Engagement" +msgstr "" + #: src/features/tournaments/components/upcoming-tournament/enroll-free-agent.tsx:26 msgid "Enroll" msgstr "参加登録" @@ -968,12 +1041,13 @@ msgstr "チームを登録" msgid "Enrolled" msgstr "登録済み" -#: src/features/tournaments/components/edit-enrolled-players.tsx:132 +#: src/features/tournaments/components/enrollment/free-agent-section.tsx:155 #: src/features/tournaments/components/upcoming-tournament/enrolled-players-list-button.tsx:39 msgid "Enrolled Players" msgstr "登録済みプレイヤー" -#: src/features/tournaments/components/edit-enrolled-teams.tsx:151 +#: src/features/tournaments/components/enrollment/enrollment-stats.tsx:28 +#: src/features/tournaments/components/enrollment/team-section.tsx:240 #: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:145 #: src/features/tournaments/components/upcoming-tournament/team-list-button.tsx:31 msgid "Enrolled Teams" @@ -983,16 +1057,22 @@ msgstr "登録済みチーム" msgid "Enrolling as a free agent adds you to a pool of players looking for teammates." msgstr "フリーエージェントとして登録すると、チームメイトを探しているプレイヤーのプールに追加されます。" +#: src/app/routes/_authed/admin/tournaments/$id/enrollment.tsx:33 +#: src/features/admin/components/dashboard/tournament-card.tsx:61 #: src/features/tournaments/components/upcoming-tournament/enroll-free-agent.tsx:29 -#: src/features/tournaments/components/upcoming-tournament/index.tsx:68 +#: src/features/tournaments/components/upcoming-tournament/index.tsx:69 msgid "Enrollment" msgstr "参加登録" +#: src/features/tournaments/components/manage-tournament.tsx:55 +msgid "Enrollment & Teams" +msgstr "" + #: src/features/tournaments/components/tournament-form.tsx:196 msgid "Enrollment Due" msgstr "登録締切" -#: src/features/tournaments/components/upcoming-tournament/index.tsx:80 +#: src/features/tournaments/components/upcoming-tournament/index.tsx:81 msgid "Enrollment has been closed for this tournament." msgstr "このトーナメントの参加登録は締め切られました。" @@ -1013,14 +1093,39 @@ msgstr "名前を入力" msgid "Enter your phone number" msgstr "電話番号を入力" -#: src/features/activities/components/activities-table.tsx:187 +#: src/features/telemetry/components/error-browser/error-group-row.tsx:29 +msgid "Error group reopened" +msgstr "" + +#: src/features/telemetry/components/error-browser/error-group-row.tsx:29 +msgid "Error group resolved" +msgstr "" + +#: src/features/activities/components/activities-table.tsx:207 msgid "Error Message" msgstr "エラーメッセージ" -#: src/components/DefaultCatchBoundary.tsx:96 +#: src/components/DefaultCatchBoundary.tsx:98 msgid "Error: {errorMessage}" msgstr "エラー: {errorMessage}" +#: src/app/routes/_authed/admin/telemetry.tsx:51 +#: src/features/telemetry/components/usage-screen.tsx:200 +msgid "Errors" +msgstr "" + +#: src/features/telemetry/components/client-events-table.tsx:113 +msgid "Event Details" +msgstr "" + +#: src/features/telemetry/components/client-events-table.tsx:70 +msgid "Event name" +msgstr "" + +#: src/app/routes/_authed/admin/telemetry.tsx:48 +msgid "Events" +msgstr "" + #: src/features/tournaments/components/group-stage-view.tsx:368 msgid "Expand standings" msgstr "順位表を展開" @@ -1029,16 +1134,12 @@ msgstr "順位表を展開" msgid "eyes" msgstr "目" -#: src/features/activities/components/activities-table.tsx:145 +#: src/features/activities/components/activities-table.tsx:181 msgid "Failed" msgstr "失敗" -#: src/features/admin/components/award-badges.tsx:41 -msgid "Failed to award badge" -msgstr "バッジの付与に失敗しました" - #: src/app/routes/api/spotify/capture.ts:54 -#: src/contexts/spotify-context.tsx:427 +#: src/contexts/spotify-context.tsx:426 msgid "Failed to capture playback state" msgstr "再生状態の取得に失敗しました" @@ -1054,7 +1155,7 @@ msgstr "チームの作成に失敗しました" msgid "Failed to create tournament" msgstr "トーナメントの作成に失敗しました" -#: src/contexts/spotify-context.tsx:335 +#: src/contexts/spotify-context.tsx:334 msgid "Failed to get devices" msgstr "デバイスの取得に失敗しました" @@ -1068,19 +1169,19 @@ msgid "Failed to load team" msgstr "チームの読み込みに失敗しました" #: src/app/routes/api/spotify/resume.ts:54 -#: src/contexts/spotify-context.tsx:447 +#: src/contexts/spotify-context.tsx:446 msgid "Failed to resume playback state" msgstr "再生状態の再開に失敗しました" -#: src/contexts/spotify-context.tsx:360 +#: src/contexts/spotify-context.tsx:359 msgid "Failed to set active device" msgstr "アクティブなデバイスの設定に失敗しました" -#: src/contexts/spotify-context.tsx:314 +#: src/contexts/spotify-context.tsx:313 msgid "Failed to set volume" msgstr "音量の設定に失敗しました" -#: src/features/admin/components/admin-page.tsx:42 +#: src/features/admin/components/dashboard/tools-list.tsx:41 msgid "Failed to start badge recalculation" msgstr "バッジの再計算を開始できませんでした" @@ -1092,19 +1193,19 @@ msgstr "チームの更新に失敗しました" msgid "Failed to update tournament" msgstr "トーナメントの更新に失敗しました" -#: src/app/routes/api/teams/upload-logo.ts:140 -#: src/app/routes/api/tournaments/upload-logo.ts:138 +#: src/app/routes/api/teams/upload-logo.ts:141 +#: src/app/routes/api/tournaments/upload-logo.ts:139 #: src/features/teams/components/team-form/index.tsx:148 #: src/features/tournaments/components/tournament-form.tsx:88 msgid "Failed to upload logo" msgstr "ロゴのアップロードに失敗しました" -#: src/features/activities/components/activities-table.tsx:330 +#: src/features/activities/components/activities-table.tsx:94 msgid "Failure" msgstr "失敗" -#: src/app/routes/api/teams/upload-logo.ts:79 -#: src/app/routes/api/tournaments/upload-logo.ts:80 +#: src/app/routes/api/teams/upload-logo.ts:80 +#: src/app/routes/api/tournaments/upload-logo.ts:81 msgid "File too large. Maximum size is 10MB." msgstr "ファイルサイズが大きすぎます。最大サイズは10MBです。" @@ -1112,7 +1213,7 @@ msgstr "ファイルサイズが大きすぎます。最大サイズは10MBで msgid "Final" msgstr "決勝" -#: src/features/tournaments/components/started-tournament/index.tsx:114 +#: src/features/tournaments/components/started-tournament/index.tsx:115 msgid "Final Results" msgstr "最終結果" @@ -1150,11 +1251,19 @@ msgstr "フリーエージェント登録" msgid "Free Agents" msgstr "フリーエージェント" -#: src/features/activities/components/activities-table.tsx:124 +#: src/features/telemetry/components/usage-screen.tsx:226 +msgid "Function" +msgstr "" + +#: src/features/activities/components/activities-table.tsx:165 msgid "Function Name" msgstr "関数名" -#: src/app/routes/_authed/admin/tournaments/$id/assign-partners.tsx:148 +#: src/app/routes/_authed/admin/telemetry.tsx:45 +msgid "Functions" +msgstr "" + +#: src/app/routes/_authed/admin/tournaments/$id/assign-partners.tsx:146 msgid "Generate Random Pairings" msgstr "ランダムペアリングを生成" @@ -1163,11 +1272,15 @@ msgstr "ランダムペアリングを生成" msgid "Go back" msgstr "戻る" -#: src/components/DefaultCatchBoundary.tsx:67 -#: src/components/DefaultCatchBoundary.tsx:132 +#: src/components/DefaultCatchBoundary.tsx:69 +#: src/components/DefaultCatchBoundary.tsx:134 msgid "Go Back" msgstr "戻る" +#: src/features/telemetry/components/usage-screen.tsx:54 +msgid "Good" +msgstr "" + #: src/themes/seventies/manifest.ts:6 msgid "Groovy" msgstr "グルーヴィー" @@ -1229,13 +1342,13 @@ msgstr "参加したことはありますか?" msgid "Head to Head" msgstr "対戦成績" -#: src/components/DefaultCatchBoundary.tsx:103 +#: src/components/DefaultCatchBoundary.tsx:105 msgid "Hide details" msgstr "詳細を隠す" -#: src/components/DefaultCatchBoundary.tsx:74 -#: src/components/DefaultCatchBoundary.tsx:125 -#: src/features/core/hooks/use-links.ts:11 +#: src/components/DefaultCatchBoundary.tsx:76 +#: src/components/DefaultCatchBoundary.tsx:127 +#: src/features/core/hooks/use-links.ts:12 msgid "Home" msgstr "ホーム" @@ -1263,7 +1376,7 @@ msgstr "予想のスコアの仕組み" #. placeholder {0}: response.status #. placeholder {1}: response.statusText -#: src/contexts/spotify-context.tsx:65 +#: src/contexts/spotify-context.tsx:64 msgid "HTTP {0}: {1}" msgstr "HTTP {0}: {1}" @@ -1275,6 +1388,10 @@ msgstr "初めて参加する場合は<0>新しいプレイヤーを、そ msgid "Important: Enrolling as a free agent does not guarantee a tournament spot. To secure a spot, one team member must register through the app and select a walkout song." msgstr "重要: フリーエージェントとして登録しても、トーナメントへの参加枠は保証されません。参加枠を確保するには、チームメンバーの1人がアプリから登録し、ウォークアウトソングを選択する必要があります。" +#: src/features/telemetry/components/error-browser/error-browser.tsx:56 +msgid "Include resolved" +msgstr "" + #: src/features/login/components/code-prompt.tsx:43 msgid "Incorrect code" msgstr "コードが正しくありません" @@ -1283,8 +1400,8 @@ msgstr "コードが正しくありません" msgid "Invalid action" msgstr "無効な操作です" -#: src/app/routes/api/teams/upload-logo.ts:65 -#: src/app/routes/api/tournaments/upload-logo.ts:66 +#: src/app/routes/api/teams/upload-logo.ts:66 +#: src/app/routes/api/tournaments/upload-logo.ts:67 msgid "Invalid file type. Only JPEG, PNG and GIF are allowed." msgstr "無効なファイル形式です。JPEG、PNG、GIFのみ使用できます。" @@ -1292,8 +1409,8 @@ msgstr "無効なファイル形式です。JPEG、PNG、GIFのみ使用でき msgid "Invalid hex color format" msgstr "無効なHEXカラー形式です" -#: src/app/routes/api/teams/upload-logo.ts:33 -#: src/app/routes/api/tournaments/upload-logo.ts:34 +#: src/app/routes/api/teams/upload-logo.ts:34 +#: src/app/routes/api/tournaments/upload-logo.ts:35 msgid "Invalid input" msgstr "無効な入力です" @@ -1302,10 +1419,11 @@ msgid "Invalid request" msgstr "無効なリクエストです" #: src/features/players/components/players-activity-table.tsx:41 +#: src/features/telemetry/components/shared/relative-time.tsx:14 msgid "Just now" msgstr "たった今" -#: src/app/routes/_authed/admin/tournaments/run.$id.tsx:166 +#: src/app/routes/_authed/admin/tournaments/run.$id.tsx:107 msgid "Knockout Bracket" msgstr "ノックアウトブラケット" @@ -1347,7 +1465,7 @@ msgid "Last name must be 3-20 characters long and contain only letters" msgstr "姓は3〜20文字の英字のみで入力してください" #. placeholder {0}: migrationStatus.result.totalBadgesEarned -#: src/features/admin/components/admin-page.tsx:74 +#: src/features/admin/components/dashboard/tools-list.tsx:73 msgid "Last recalculation: {0, plural, one {# badge earned} other {# badges earned}}" msgstr "前回の再計算:{0, plural, one {# 個のバッジを獲得} other {# 個のバッジを獲得}}" @@ -1359,7 +1477,7 @@ msgstr "ライト" msgid "lip" msgstr "唇" -#: src/features/tournaments/components/started-tournament/index.tsx:121 +#: src/features/tournaments/components/started-tournament/index.tsx:122 msgid "Live Matches" msgstr "ライブ試合" @@ -1381,8 +1499,8 @@ msgstr "場所は必須です" msgid "Logo" msgstr "ロゴ" -#: src/app/routes/api/teams/upload-logo.ts:46 -#: src/app/routes/api/tournaments/upload-logo.ts:47 +#: src/app/routes/api/teams/upload-logo.ts:47 +#: src/app/routes/api/tournaments/upload-logo.ts:48 msgid "Logo file is required" msgstr "ロゴファイルは必須です" @@ -1420,26 +1538,24 @@ msgstr "予想する" msgid "Make Your Prediction" msgstr "予想を入力" +#: src/features/admin/components/dashboard/tournament-card.tsx:68 #: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:89 -#: src/features/tournaments/components/upcoming-tournament/index.tsx:143 +#: src/features/tournaments/components/upcoming-tournament/index.tsx:144 msgid "Manage" msgstr "管理" -#: src/app/routes/_authed/admin/tournaments/$id/assign-partners.tsx:28 +#: src/app/routes/_authed/admin/tournaments/$id/assign-partners.tsx:29 #: src/app/routes/_authed/admin/tournaments/$id/index.tsx:23 msgid "Manage {name}" msgstr "{name}を管理" -#: src/features/tournaments/components/manage-tournament.tsx:117 -msgid "Manage Enrollments" -msgstr "登録を管理" - -#: src/features/tournaments/components/manage-tournament.tsx:76 +#: src/features/tournaments/components/enrollment/team-section.tsx:234 msgid "Manage Team Songs/Logos" msgstr "チームの曲・ロゴを管理" #: src/app/routes/_authed/admin/tournaments/index.tsx:17 -#: src/features/admin/components/admin-page.tsx:50 +#: src/features/admin/components/dashboard/tools-list.tsx:49 +#: src/features/admin/components/dashboard/tournament-card.tsx:25 msgid "Manage Tournaments" msgstr "トーナメント管理" @@ -1450,6 +1566,10 @@ msgstr "トーナメント管理" msgid "margin" msgstr "得点差" +#: src/features/telemetry/components/error-browser/error-group-row.tsx:98 +msgid "Mark resolved" +msgstr "" + #. placeholder {0}: match.order #. placeholder {1}: match.round + 1 #: src/features/bracket/components/match-dock.tsx:126 @@ -1514,11 +1634,15 @@ msgstr "対戦カード" msgid "Matchup details" msgstr "対戦カードの詳細" +#: src/features/telemetry/components/usage-screen.tsx:169 +msgid "MAU" +msgstr "" + #: src/features/players/components/league-head-to-head.tsx:120 msgid "Me" msgstr "自分" -#: src/features/admin/components/admin-page.tsx:60 +#: src/features/admin/components/dashboard/tools-list.tsx:59 msgid "Migrate Badge Progress" msgstr "バッジ進捗を移行" @@ -1532,6 +1656,7 @@ msgid "MMR Calculation:" msgstr "MMR計算:" #: src/features/teams/components/team-form/index.tsx:200 +#: src/features/telemetry/components/client-events-table.tsx:119 #: src/features/tournaments/components/tournament-form.tsx:137 msgid "Name" msgstr "名前" @@ -1545,7 +1670,8 @@ msgstr "名前を入力してください" msgid "Name updated successfully!" msgstr "名前を更新しました!" -#: src/app/routes/_authed/admin/tournaments/$id/assign-partners.tsx:132 +#: src/app/routes/_authed/admin/tournaments/$id/assign-partners.tsx:130 +#: src/features/tournaments/components/enrollment/enrollment-stats.tsx:35 msgid "Need at least 2 players to create teams" msgstr "チームを作成するには2人以上のプレイヤーが必要です" @@ -1554,6 +1680,10 @@ msgstr "チームを作成するには2人以上のプレイヤーが必要で msgid "Need at least 4 teams to create a group stage format. Current team count: {0}" msgstr "グループステージ形式を作成するには4チーム以上が必要です。現在のチーム数: {0}" +#: src/features/telemetry/components/usage-screen.tsx:56 +msgid "Needs improvement" +msgstr "" + #: src/features/reactions/components/emoji-picker.tsx:23 msgid "nerd" msgstr "オタク" @@ -1567,6 +1697,7 @@ msgid "Neumorphic" msgstr "ネウモーフィズム" #: src/features/players/components/players-activity-table.tsx:27 +#: src/features/telemetry/components/shared/relative-time.tsx:6 msgid "Never" msgstr "なし" @@ -1574,6 +1705,10 @@ msgstr "なし" msgid "Never active" msgstr "アクティビティなし" +#: src/lib/telemetry/alerts.server.ts:124 +msgid "New client error" +msgstr "" + #: src/features/login/components/player-prompt/new-player-button.tsx:18 msgid "New Player" msgstr "新しいプレイヤー" @@ -1582,7 +1717,7 @@ msgstr "新しいプレイヤー" msgid "Next match" msgstr "次の試合" -#: src/features/spotify/components/spotify-controls-bar.tsx:130 +#: src/features/spotify/components/spotify-controls-bar.tsx:129 msgid "Next track" msgstr "次のトラック" @@ -1597,11 +1732,11 @@ msgstr "アクセストークンが見つかりません" msgid "No active device found. Please select a device first." msgstr "アクティブなデバイスが見つかりません。先にデバイスを選択してください。" -#: src/features/spotify/components/spotify-controls-bar.tsx:204 +#: src/features/spotify/components/spotify-controls-bar.tsx:203 msgid "No active device. Please select a device in settings." msgstr "アクティブなデバイスがありません。設定でデバイスを選択してください。" -#: src/features/tournaments/components/started-tournament/index.tsx:142 +#: src/features/tournaments/components/started-tournament/index.tsx:143 msgid "No active matches" msgstr "進行中の試合はありません" @@ -1609,18 +1744,26 @@ msgstr "進行中の試合はありません" msgid "No active playback to capture" msgstr "キャプチャする再生中のプレイバックがありません" -#: src/features/activities/components/activities-table.tsx:234 +#: src/features/activities/components/activities-table.tsx:132 msgid "No Activities Found" msgstr "アクティビティが見つかりません" +#: src/features/admin/components/dashboard/activity-feed.tsx:78 +msgid "No activity yet" +msgstr "" + #: src/features/badges/components/badge-stats-table.tsx:45 msgid "No Badges Available" msgstr "利用可能なバッジがありません" -#: src/features/spotify/components/spotify-sheet.tsx:155 +#: src/features/spotify/components/spotify-sheet.tsx:154 msgid "No devices found." msgstr "デバイスが見つかりません。" +#: src/features/telemetry/components/client-events-table.tsx:94 +msgid "No Events Found" +msgstr "" + #: src/features/tournaments/components/group-stage-view.tsx:294 msgid "No groups have been created yet" msgstr "まだグループが作成されていません" @@ -1637,7 +1780,7 @@ msgstr "プレイヤーのアクティビティが見つかりません" msgid "No players available" msgstr "利用可能なプレイヤーがいません" -#: src/features/tournaments/components/edit-enrolled-players.tsx:145 +#: src/features/tournaments/components/enrollment/free-agent-section.tsx:168 #: src/features/tournaments/components/upcoming-tournament/enrolled-players-list-button.tsx:42 msgid "No players enrolled yet" msgstr "まだプレイヤーが登録されていません" @@ -1668,7 +1811,7 @@ msgstr "スナップショットが提供されていません" msgid "No song selected" msgstr "曲が選択されていません" -#: src/features/spotify/components/spotify-controls-bar.tsx:157 +#: src/features/spotify/components/spotify-controls-bar.tsx:156 msgid "No state captured" msgstr "キャプチャされた状態がありません" @@ -1689,7 +1832,7 @@ msgstr "割り当てられたチームがありません" msgid "No Teams Enrolled" msgstr "登録されたチームがありません" -#: src/features/tournaments/components/edit-enrolled-teams.tsx:164 +#: src/features/tournaments/components/enrollment/team-section.tsx:253 msgid "No teams enrolled yet" msgstr "まだチームが登録されていません" @@ -1701,10 +1844,19 @@ msgstr "「{search}」に一致するチームが見つかりません" msgid "No tournaments found" msgstr "トーナメントが見つかりません" +#: src/features/admin/components/dashboard/tournament-card.tsx:18 +msgid "No tournaments yet" +msgstr "" + #: src/features/tournaments/components/tournament-card-list.tsx:40 msgid "No Tournaments Yet" msgstr "トーナメントはまだありません" +#: src/features/admin/components/dashboard/errors-card.tsx:53 +#: src/features/telemetry/components/error-browser/error-browser.tsx:27 +msgid "No unresolved errors" +msgstr "" + #: src/features/predictions/components/prediction-leaderboard.tsx:136 msgid "Nobody made a prediction for this tournament." msgstr "このトーナメントで予想をした人はいません。" @@ -1717,6 +1869,10 @@ msgstr "違う" msgid "Not right — enter our score" msgstr "違う — スコアを入力" +#: src/features/telemetry/components/error-browser/error-browser.tsx:28 +msgid "Nothing has broken in the last 30 days. Keep it up." +msgstr "" + #: src/features/settings/components/notifications-section.tsx:18 #: src/features/settings/components/notifications-section.tsx:59 msgid "Notifications" @@ -1738,7 +1894,7 @@ msgstr "このデバイスでは通知が無効になっています" msgid "Notifications enabled on this device" msgstr "このデバイスでは通知が有効になっています" -#: src/features/spotify/components/spotify-sheet.tsx:96 +#: src/features/spotify/components/spotify-sheet.tsx:95 msgid "Now Playing" msgstr "再生中" @@ -1758,7 +1914,15 @@ msgstr "登録が完了すると、他のフリーエージェントとその電 msgid "One time code" msgstr "ワンタイムコード" -#: src/features/admin/components/admin-page.tsx:95 +#: src/features/admin/components/dashboard/now-strip.tsx:44 +msgid "Online now" +msgstr "" + +#: src/features/admin/components/dashboard/now-strip.tsx:46 +msgid "Open errors" +msgstr "" + +#: src/features/admin/components/dashboard/tools-list.tsx:89 msgid "Open Pocketbase" msgstr "Pocketbaseを開く" @@ -1767,7 +1931,7 @@ msgid "Opponent" msgstr "対戦相手" #: src/features/tournaments/components/upcoming-tournament/enroll-team/team-selection-view.tsx:60 -#: src/features/tournaments/components/upcoming-tournament/index.tsx:92 +#: src/features/tournaments/components/upcoming-tournament/index.tsx:93 msgid "or" msgstr "または" @@ -1800,7 +1964,23 @@ msgstr "概要" msgid "owo" msgstr "owo" -#: src/app/routes/_authed/admin/tournaments/$id/assign-partners.tsx:157 +#: src/features/telemetry/components/usage-screen.tsx:97 +msgid "p50" +msgstr "" + +#: src/features/telemetry/components/usage-screen.tsx:113 +msgid "p95" +msgstr "" + +#: src/features/admin/components/dashboard/usage-card.tsx:45 +msgid "Page views" +msgstr "" + +#: src/features/telemetry/components/usage-screen.tsx:177 +msgid "Page views by route" +msgstr "" + +#: src/app/routes/_authed/admin/tournaments/$id/assign-partners.tsx:155 msgid "Partner Assignments" msgstr "パートナー割り当て" @@ -1808,7 +1988,11 @@ msgstr "パートナー割り当て" msgid "Partners will be randomly assigned when enrollment closes" msgstr "登録が締め切られると、パートナーはランダムに割り当てられます" -#: src/features/spotify/components/spotify-controls-bar.tsx:117 +#: src/features/telemetry/components/client-events-table.tsx:122 +msgid "Path" +msgstr "" + +#: src/features/spotify/components/spotify-controls-bar.tsx:116 msgid "Pause" msgstr "一時停止" @@ -1839,7 +2023,7 @@ msgstr "先に前の試合を選んでください — これらのチームは msgid "PICKS" msgstr "PICKS" -#: src/features/spotify/components/spotify-controls-bar.tsx:117 +#: src/features/spotify/components/spotify-controls-bar.tsx:116 msgid "Play" msgstr "再生" @@ -1847,23 +2031,22 @@ msgstr "再生" msgid "Playback control failed" msgstr "再生コントロールに失敗しました" -#: src/features/activities/components/activities-table.tsx:154 -#: src/features/admin/components/award-badges.tsx:35 +#: src/features/activities/components/activities-table.tsx:98 +#: src/features/activities/components/activities-table.tsx:188 +#: src/features/admin/components/award-badges.tsx:31 +#: src/features/telemetry/components/client-events-table.tsx:73 +#: src/features/telemetry/components/client-events-table.tsx:120 msgid "Player" msgstr "プレイヤー" -#: src/features/teams/components/team-form/players-picker.tsx:45 +#: src/features/teams/components/team-form/players-picker.tsx:46 msgid "Player 1" msgstr "プレイヤー1" -#: src/features/teams/components/team-form/players-picker.tsx:53 +#: src/features/teams/components/team-form/players-picker.tsx:54 msgid "Player 2" msgstr "プレイヤー2" -#: src/app/routes/_authed/admin/activities.tsx:59 -msgid "Player Activity" -msgstr "プレイヤーアクティビティ" - #: src/features/login/components/player-prompt/index.tsx:46 msgid "Player already exists" msgstr "プレイヤーは既に存在します" @@ -1880,6 +2063,7 @@ msgstr "プレイヤーを削除しました" msgid "Player Stats" msgstr "プレイヤースタッツ" +#: src/app/routes/_authed/admin/telemetry.tsx:54 #: src/features/teams/components/team-profile/index.tsx:29 msgid "Players" msgstr "プレイヤー" @@ -1889,10 +2073,14 @@ msgid "Players are required" msgstr "プレイヤーの選択が必要です" #. placeholder {0}: activeDevice.name -#: src/features/spotify/components/spotify-controls-bar.tsx:175 +#: src/features/spotify/components/spotify-controls-bar.tsx:174 msgid "Playing on {0}" msgstr "{0}で再生中" +#: src/features/telemetry/components/usage-screen.tsx:58 +msgid "Poor" +msgstr "" + #: src/features/tournaments/components/group-stage-view.tsx:314 msgid "Populate Knockout Bracket" msgstr "ノックアウトブラケットを作成" @@ -1919,11 +2107,11 @@ msgstr "予想受付中" msgid "Previous match" msgstr "前の試合" -#: src/features/spotify/components/spotify-controls-bar.tsx:105 +#: src/features/spotify/components/spotify-controls-bar.tsx:104 msgid "Previous track" msgstr "前のトラック" -#: src/features/core/hooks/use-links.ts:27 +#: src/features/core/hooks/use-links.ts:28 msgid "Profile" msgstr "プロフィール" @@ -1931,6 +2119,10 @@ msgstr "プロフィール" msgid "Progress " msgstr "進行状況 " +#: src/features/telemetry/components/client-events-table.tsx:136 +msgid "Props" +msgstr "" + #: src/app/routes/_authed/tournaments/$id.predictions_.$playerId.tsx:106 #: src/features/predictions/components/prediction-leaderboard.tsx:261 msgid "PTS" @@ -1944,7 +2136,7 @@ msgstr "Q" msgid "Quest Log" msgstr "クエスト" -#: src/app/routes/_authed/admin/tournaments/$id/assign-partners.tsx:167 +#: src/app/routes/_authed/admin/tournaments/$id/assign-partners.tsx:165 msgid "Re-roll" msgstr "再抽選" @@ -1954,10 +2146,14 @@ msgstr "リアクション" #. placeholder {0}: migrationStatus.playersProcessed #. placeholder {1}: migrationStatus.totalPlayers -#: src/features/admin/components/admin-page.tsx:67 +#: src/features/admin/components/dashboard/tools-list.tsx:66 msgid "Recalculating badges in the background ({0}/{1})" msgstr "バックグラウンドでバッジを再計算中({0}/{1})" +#: src/features/admin/components/dashboard/activity-feed.tsx:34 +msgid "Recent activity" +msgstr "" + #: src/features/tournaments/components/started-tournament/sections/recent-results.tsx:34 msgid "Recently finished" msgstr "最近終了した試合" @@ -1972,6 +2168,10 @@ msgstr "地域" msgid "Regional Tournament" msgstr "地域トーナメント" +#: src/features/telemetry/components/error-browser/error-group-row.tsx:98 +msgid "Reopen" +msgstr "" + #: src/features/bracket/components/match-report.tsx:127 msgid "Report cleared" msgstr "報告を取り消しました" @@ -1981,11 +2181,15 @@ msgstr "報告を取り消しました" msgid "Report Score" msgstr "スコアを報告" -#: src/contexts/spotify-context.tsx:60 +#: src/contexts/spotify-context.tsx:59 msgid "Request failed" msgstr "リクエストに失敗しました" -#: src/features/spotify/components/spotify-controls-bar.tsx:157 +#: src/features/telemetry/components/error-browser/error-group-row.tsx:63 +msgid "Resolved" +msgstr "" + +#: src/features/spotify/components/spotify-controls-bar.tsx:156 msgid "Restore captured state" msgstr "キャプチャした状態を復元" @@ -1993,7 +2197,7 @@ msgstr "キャプチャした状態を復元" msgid "Results" msgstr "結果" -#: src/components/DefaultCatchBoundary.tsx:117 +#: src/components/DefaultCatchBoundary.tsx:119 msgid "Retry" msgstr "再試行" @@ -2010,16 +2214,21 @@ msgstr "大笑い" msgid "Round {0}" msgstr "ラウンド{0}" +#: src/features/telemetry/components/client-events-table.tsx:125 +msgid "Route" +msgstr "" + #: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:142 #: src/features/tournaments/components/upcoming-tournament/rules-list-button.tsx:23 msgid "Rules" msgstr "ルール" -#: src/app/routes/_authed/admin/tournaments/run.$id.tsx:38 +#: src/app/routes/_authed/admin/tournaments/run.$id.tsx:36 msgid "Run {name}" msgstr "{name}を実行" -#: src/features/tournaments/components/manage-tournament.tsx:82 +#: src/features/admin/components/dashboard/tournament-card.tsx:53 +#: src/features/tournaments/components/manage-tournament.tsx:49 msgid "Run Tournament" msgstr "トーナメントを実行" @@ -2053,7 +2262,7 @@ msgstr "絶叫" msgid "Search for a Song" msgstr "曲を検索" -#: src/features/tournaments/components/edit-enrolled-players.tsx:120 +#: src/features/tournaments/components/enrollment/free-agent-section.tsx:133 msgid "Search for players to enroll..." msgstr "登録するプレイヤーを検索..." @@ -2061,7 +2270,7 @@ msgstr "登録するプレイヤーを検索..." msgid "Search for songs..." msgstr "曲を検索..." -#: src/features/tournaments/components/edit-enrolled-teams.tsx:139 +#: src/features/tournaments/components/enrollment/team-section.tsx:216 msgid "Search for teams to enroll..." msgstr "登録するチームを検索..." @@ -2095,11 +2304,11 @@ msgstr "セグメントは15秒以内にしてください" msgid "Segment must be within song duration" msgstr "セグメントは曲の長さ内に収めてください" -#: src/features/admin/components/award-badges.tsx:71 +#: src/features/admin/components/award-badges.tsx:65 msgid "Select a badge" msgstr "バッジを選択" -#: src/features/spotify/components/spotify-sheet.tsx:145 +#: src/features/spotify/components/spotify-sheet.tsx:144 msgid "Select a device..." msgstr "デバイスを選択..." @@ -2121,7 +2330,7 @@ msgstr "時間を選択" msgid "Select Color" msgstr "カラーを選択" -#: src/features/spotify/components/spotify-sheet.tsx:127 +#: src/features/spotify/components/spotify-sheet.tsx:126 msgid "Select Device" msgstr "デバイスを選択" @@ -2141,7 +2350,7 @@ msgstr "登録締切日を選択" msgid "Select one of your existing teams" msgstr "既存のチームから選択してください" -#: src/features/admin/components/award-badges.tsx:86 +#: src/features/admin/components/award-badges.tsx:80 msgid "Select Player" msgstr "プレイヤーを選択" @@ -2174,20 +2383,32 @@ msgstr "テスト通知を送信" msgid "Series is tied" msgstr "シリーズはタイです" -#: src/app/routes/_authed/admin/activities.tsx:58 -msgid "Server Functions" -msgstr "サーバー関数" +#: src/lib/telemetry/alerts.server.ts:153 +msgid "Server function failing" +msgstr "" -#: src/features/activities/components/activities-table.tsx:309 +#: src/features/telemetry/components/usage-screen.tsx:186 +msgid "Server functions" +msgstr "" + +#: src/features/activities/components/activities-table.tsx:82 msgid "serverFn name" msgstr "serverFn名" +#: src/features/telemetry/components/client-events-table.tsx:128 +msgid "Session" +msgstr "" + +#: src/features/telemetry/components/usage-screen.tsx:172 +msgid "Sessions" +msgstr "" + #: src/app/routes/_authed/settings.tsx:16 #: src/features/core/components/settings-button.tsx:46 msgid "Settings" msgstr "設定" -#: src/components/DefaultCatchBoundary.tsx:103 +#: src/components/DefaultCatchBoundary.tsx:105 msgid "Show details" msgstr "詳細を表示" @@ -2213,7 +2434,11 @@ msgstr "スケッチ" msgid "Skeuomorphic" msgstr "スキューモーフィズム" -#: src/components/DefaultCatchBoundary.tsx:89 +#: src/features/activities/components/activities-table.tsx:113 +msgid "Slow >1s (this page)" +msgstr "" + +#: src/components/DefaultCatchBoundary.tsx:91 msgid "Something went wrong" msgstr "問題が発生しました" @@ -2241,8 +2466,8 @@ msgstr "曲のセグメントは15秒以内にしてください" msgid "Song start time is required" msgstr "曲の開始時間を入力してください" -#: src/features/activities/components/activities-table.tsx:345 #: src/features/players/components/player-stats-table.tsx:340 +#: src/features/telemetry/components/shared/search-filter-bar.tsx:102 msgid "Sort:" msgstr "並び替え:" @@ -2250,7 +2475,7 @@ msgstr "並び替え:" msgid "Sorted by win percentage" msgstr "勝率順に並び替え" -#: src/features/spotify/components/spotify-sheet.tsx:68 +#: src/features/spotify/components/spotify-sheet.tsx:67 msgid "Spotify Controls" msgstr "Spotifyコントロール" @@ -2262,7 +2487,7 @@ msgstr "再生を操作するにはSpotify Premiumが必要です。" msgid "Spotify premium required" msgstr "Spotify Premiumが必要です" -#: src/features/spotify/components/spotify-controls-bar.tsx:180 +#: src/features/spotify/components/spotify-controls-bar.tsx:179 msgid "Spotify settings" msgstr "Spotify設定" @@ -2292,6 +2517,10 @@ msgstr "開始時間を入力してください" msgid "Start typing to search..." msgstr "入力して検索..." +#: src/features/admin/components/dashboard/tournament-card.tsx:43 +msgid "Starts in" +msgstr "" + #: src/features/players/components/player-stats-table.tsx:372 msgid "Stat abbreviations and MMR info" msgstr "スタッツ略語とMMR情報" @@ -2300,7 +2529,7 @@ msgstr "スタッツ略語とMMR情報" msgid "Stat Abbreviations:" msgstr "スタッツ略語:" -#: src/features/core/hooks/use-links.ts:16 +#: src/features/core/hooks/use-links.ts:17 #: src/features/players/components/profile/index.tsx:26 #: src/features/teams/components/team-profile/index.tsx:34 msgid "Statistics" @@ -2315,8 +2544,8 @@ msgstr "スタッツ" msgid "Stats Comparison" msgstr "スタッツ比較" -#: src/features/activities/components/activities-table.tsx:131 -#: src/features/activities/components/activities-table.tsx:321 +#: src/features/activities/components/activities-table.tsx:85 +#: src/features/activities/components/activities-table.tsx:167 msgid "Status" msgstr "ステータス" @@ -2328,8 +2557,8 @@ msgstr "ステータス" msgid "Submit" msgstr "送信" -#: src/features/activities/components/activities-table.tsx:138 -#: src/features/activities/components/activities-table.tsx:329 +#: src/features/activities/components/activities-table.tsx:93 +#: src/features/activities/components/activities-table.tsx:174 msgid "Success" msgstr "成功" @@ -2337,8 +2566,7 @@ msgstr "成功" msgid "Successfully logged in. Welcome back!" msgstr "ログインしました。おかえりなさい!" -#: src/features/activities/components/activities-table.tsx:42 -#: src/features/activities/components/activities-table.tsx:112 +#: src/features/activities/components/activities-table.tsx:53 #: src/features/settings/components/color-scheme-picker.tsx:47 msgid "System" msgstr "システム" @@ -2351,11 +2579,11 @@ msgstr "T" msgid "Tap an opponent below to compare" msgstr "下の対戦相手をタップして比較" -#: src/components/ios-install-prompt.tsx:43 +#: src/components/ios-install-prompt.tsx:48 msgid "Tap Menu (⋮) → Add to Home screen" msgstr "メニュー (⋮) をタップ →「ホーム画面に追加」" -#: src/components/ios-install-prompt.tsx:42 +#: src/components/ios-install-prompt.tsx:47 msgid "Tap Share → Add to Home Screen" msgstr "共有をタップ →「ホーム画面に追加」" @@ -2385,7 +2613,7 @@ msgstr "チーム詳細" msgid "Team enrolled successfully!" msgstr "チームを登録しました!" -#: src/app/routes/api/teams/upload-logo.ts:92 +#: src/app/routes/api/teams/upload-logo.ts:93 #: src/features/teams/components/team-profile/index.tsx:21 msgid "Team not found" msgstr "チームが見つかりません" @@ -2419,11 +2647,15 @@ msgstr "チーム" msgid "Teams created successfully!" msgstr "チームを作成しました!" +#: src/app/routes/_authed/admin/telemetry.tsx:20 +msgid "Telemetry" +msgstr "" + #: src/themes/terminal/manifest.ts:6 msgid "Terminal" msgstr "ターミナル" -#: src/app/routes/api/push/test.ts:29 +#: src/app/routes/api/push/test.ts:30 msgid "Test notification — push is working on this device." msgstr "テスト通知 — このデバイスでプッシュ通知が機能しています。" @@ -2498,6 +2730,10 @@ msgstr "各グループの上位{0, plural, one {#チーム} other {#チーム}} msgid "Top {n} advance" msgstr "上位{n}チームが勝ち上がり" +#: src/features/telemetry/components/usage-screen.tsx:191 +msgid "Top functions by call count" +msgstr "" + #: src/features/matches/components/team-head-to-head-sheet.tsx:158 #: src/features/players/components/player-head-to-head-sheet.tsx:193 msgid "Total Cups" @@ -2517,7 +2753,7 @@ msgstr "トーナメントを作成しましたが、ロゴのアップロード msgid "Tournament created successfully!" msgstr "トーナメントを作成しました!" -#: src/app/routes/api/tournaments/upload-logo.ts:93 +#: src/app/routes/api/tournaments/upload-logo.ts:94 msgid "Tournament not found" msgstr "トーナメントが見つかりません" @@ -2536,7 +2772,7 @@ msgid "Tournament updated successfully!" msgstr "トーナメントを更新しました!" #: src/app/routes/_authed/tournaments/index.tsx:17 -#: src/features/core/hooks/use-links.ts:21 +#: src/features/core/hooks/use-links.ts:22 #: src/features/teams/components/team-profile/index.tsx:53 #: src/features/teams/components/team-profile/skeleton.tsx:80 msgid "Tournaments" @@ -2550,6 +2786,10 @@ msgstr "playTrackアクションにはtrackIdが必要です" msgid "Try the new themes" msgstr "新しいテーマを試してみましょう" +#: src/features/admin/components/dashboard/quiet-boundary.tsx:26 +msgid "Unavailable right now" +msgstr "" + #: src/features/tournaments/components/upcoming-tournament/unenroll-free-agent.tsx:26 #: src/features/tournaments/components/upcoming-tournament/unenroll-team.tsx:34 msgid "Unenroll" @@ -2563,12 +2803,12 @@ msgstr "トーナメントから登録解除しますか?" msgid "Unenroll Team" msgstr "チームを登録解除" -#: src/components/DefaultCatchBoundary.tsx:39 +#: src/components/DefaultCatchBoundary.tsx:40 msgid "Unknown error" msgstr "不明なエラー" -#: src/app/routes/api/teams/upload-logo.ts:141 -#: src/app/routes/api/tournaments/upload-logo.ts:139 +#: src/app/routes/api/teams/upload-logo.ts:142 +#: src/app/routes/api/tournaments/upload-logo.ts:140 msgid "Unknown error occurred" msgstr "不明なエラーが発生しました" @@ -2585,7 +2825,7 @@ msgstr "不明なプレイヤー" msgid "Unnamed Player" msgstr "名前未設定のプレイヤー" -#: src/features/tournaments/components/started-tournament/index.tsx:128 +#: src/features/tournaments/components/started-tournament/index.tsx:129 msgid "Upcoming" msgstr "開催予定" @@ -2607,10 +2847,23 @@ msgstr "チームを更新" msgid "Update Tournament" msgstr "トーナメントを更新" -#: src/features/activities/components/activities-table.tsx:176 +#: src/app/routes/_authed/admin/telemetry.tsx:57 +msgid "Usage" +msgstr "" + +#: src/features/admin/components/dashboard/usage-card.tsx:34 +msgid "Usage, 7 days" +msgstr "" + +#: src/features/activities/components/activities-table.tsx:202 +#: src/features/telemetry/components/client-events-table.tsx:146 msgid "User Agent" msgstr "ユーザーエージェント" +#: src/features/telemetry/components/client-events-table.tsx:131 +msgid "Value" +msgstr "" + #: src/features/bracket/components/match-card.tsx:201 #: src/features/bracket/components/match-card.tsx:226 msgid "versus" @@ -2645,6 +2898,10 @@ msgstr "予想を見る" msgid "Viewport" msgstr "ビューポート" +#: src/features/telemetry/components/usage-screen.tsx:211 +msgid "Vitals" +msgstr "" + #: src/app/routes/api/spotify/playback.ts:70 msgid "volumePercent must be a number" msgstr "volumePercentは数値である必要があります" @@ -2680,6 +2937,10 @@ msgstr "ウォークアウト区間({segmentDuration}秒)" msgid "Walkout Song" msgstr "ウォークアウトソング" +#: src/features/telemetry/components/usage-screen.tsx:165 +msgid "WAU" +msgstr "" + #: src/features/tournaments/components/group-stage-view.tsx:407 msgid "WC" msgstr "WC" @@ -2731,7 +2992,7 @@ msgstr "あなたと他1人" msgid "You are not authorized to perform this action" msgstr "この操作を実行する権限がありません" -#: src/components/DefaultCatchBoundary.tsx:60 +#: src/components/DefaultCatchBoundary.tsx:62 msgid "You don't have permission to access this page." msgstr "このページにアクセスする権限がありません。" @@ -2755,7 +3016,7 @@ msgstr "あなたが最初のフリーエージェントです!" msgid "You've been added as a free agent!" msgstr "フリーエージェントとして追加されました!" -#: src/components/DefaultCatchBoundary.tsx:46 +#: src/components/DefaultCatchBoundary.tsx:48 msgid "You've been logged out" msgstr "ログアウトしました" -- 2.54.0 From 603d5a6f7fb5839c15982777b3195960e407a4d2 Mon Sep 17 00:00:00 2001 From: yohlo Date: Tue, 25 Aug 2026 21:58:59 -0700 Subject: [PATCH 06/33] i18n: translate telemetry and admin strings for de/es/ja --- src/locales/de/messages.po | 130 ++++++++++++++++++------------------- src/locales/es/messages.po | 130 ++++++++++++++++++------------------- src/locales/ja/messages.po | 130 ++++++++++++++++++------------------- 3 files changed, 195 insertions(+), 195 deletions(-) diff --git a/src/locales/de/messages.po b/src/locales/de/messages.po index a7eaae2..8e97ad2 100644 --- a/src/locales/de/messages.po +++ b/src/locales/de/messages.po @@ -60,7 +60,7 @@ msgstr "{0, plural, one {# Aktivität insgesamt} other {# Aktivitäten insgesamt #. placeholder {0}: result.totalItems #: src/features/telemetry/components/client-events-table.tsx:87 msgid "{0, plural, one {# total event} other {# total events}}" -msgstr "" +msgstr "{0, plural, one {# Ereignis insgesamt} other {# Ereignisse insgesamt}}" #. placeholder {0}: selectedConfig.total_group_matches #: src/features/tournaments/components/setup-group-stage.tsx:249 @@ -98,7 +98,7 @@ msgstr "{0} Abzeichen-Inhaber" #. placeholder {0}: recentErrors.totalItems #: src/lib/telemetry/alerts.server.ts:104 msgid "{0} client errors in the last 15 minutes" -msgstr "" +msgstr "{0} Client-Fehler in den letzten 15 Minuten" #. placeholder {0}: match.away?.name #. placeholder {0}: match.home?.name @@ -160,7 +160,7 @@ msgstr "{count, plural, one {Teams (#)} other {Teams (#)}}" #: src/lib/telemetry/alerts.server.ts:167 msgid "{deniedCount} denied admin access attempts in the last 15 minutes" -msgstr "" +msgstr "{deniedCount} abgelehnte Admin-Zugriffsversuche in den letzten 15 Minuten" #: src/features/players/components/players-activity-table.tsx:44 #: src/features/telemetry/components/shared/relative-time.tsx:17 @@ -199,7 +199,7 @@ msgstr "{n, plural, one {# Wildcard} other {# Wildcards}}" #: src/lib/telemetry/alerts.server.ts:154 msgid "{name} failed {count} times in the last 15 minutes" -msgstr "" +msgstr "{name} ist in den letzten 15 Minuten {count} Mal fehlgeschlagen" #: src/app/routes/_authed/admin/tournaments/$id/teams.tsx:24 msgid "{name} Teams" @@ -357,7 +357,7 @@ msgstr "2. Platz" #: src/features/telemetry/components/usage-screen.tsx:256 msgid "30d" -msgstr "" +msgstr "30 T" #: src/features/teams/components/team-form/duration-picker.tsx:22 msgid "30s" @@ -373,7 +373,7 @@ msgstr "5 s" #: src/features/telemetry/components/usage-screen.tsx:255 msgid "7d" -msgstr "" +msgstr "7 T" #. placeholder {0}: number?.slice(0, 3) #. placeholder {1}: number?.slice(3, 6) @@ -385,7 +385,7 @@ msgstr "Ein Code wurde an +1 ({0}) {1}-{2} gesendet" #. placeholder {0}: info.message.slice(0, 120) #: src/lib/telemetry/alerts.server.ts:125 msgid "A new error appeared: {0}" -msgstr "" +msgstr "Ein neuer Fehler ist aufgetreten: {0}" #: src/features/bracket/components/match-report.tsx:304 msgid "a team" @@ -422,7 +422,7 @@ msgstr "Aktives Gerät: {0}" #: src/features/admin/components/dashboard/usage-card.tsx:39 msgid "Active players" -msgstr "" +msgstr "Aktive Spieler" #: src/features/activities/components/activities-table.tsx:159 msgid "Activity Details" @@ -486,7 +486,7 @@ msgstr "Matchup ansagen" #: src/features/telemetry/components/client-events-table.tsx:49 msgid "Anonymous" -msgstr "" +msgstr "Anonym" #: src/app/routes/_authed/settings.tsx:35 msgid "Appearance" @@ -656,7 +656,7 @@ msgstr "CA" #: src/features/admin/components/dashboard/now-strip.tsx:52 msgid "Calls today" -msgstr "" +msgstr "Aufrufe heute" #: src/components/sheet/slide-panel/slide-panel.tsx:41 #: src/components/sheet/slide-panel/slide-panel.tsx:190 @@ -718,15 +718,15 @@ msgstr "Ausgewählten Spieler entfernen" #: src/lib/telemetry/alerts.server.ts:103 msgid "Client error spike" -msgstr "" +msgstr "Anstieg von Client-Fehlern" #: src/features/admin/components/dashboard/errors-card.tsx:21 msgid "Client errors" -msgstr "" +msgstr "Client-Fehler" #: src/features/telemetry/components/usage-screen.tsx:203 msgid "Client errors per day" -msgstr "" +msgstr "Client-Fehler pro Tag" #: src/components/sheet/modal.tsx:35 #: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:169 @@ -886,11 +886,11 @@ msgstr "Benutzerdefinierter Hex-Code" #: src/features/admin/components/dashboard/usage-card.tsx:41 #: src/features/telemetry/components/usage-screen.tsx:159 msgid "Daily active players" -msgstr "" +msgstr "Täglich aktive Spieler" #: src/features/admin/components/dashboard/usage-card.tsx:47 msgid "Daily page views" -msgstr "" +msgstr "Tägliche Seitenaufrufe" #: src/features/settings/components/color-scheme-picker.tsx:39 msgid "Dark" @@ -914,7 +914,7 @@ msgstr "Standard" #: src/lib/telemetry/alerts.server.ts:166 msgid "Denied access attempt" -msgstr "" +msgstr "Abgelehnter Zugriffsversuch" #: src/features/tournaments/components/tournament-form.tsx:150 msgid "Description" @@ -949,7 +949,7 @@ msgstr "Dauer" #: src/features/telemetry/components/usage-screen.tsx:233 msgid "Duration p50 / p95" -msgstr "" +msgstr "Dauer p50 / p95" #: src/features/tournaments/components/tournament-stats.tsx:176 msgid "Earlier regional formats aren't supported in the app and order of matches or displayed winners may be unreliable." @@ -1011,7 +1011,7 @@ msgstr "Match beenden" #: src/features/telemetry/components/usage-screen.tsx:156 msgid "Engagement" -msgstr "" +msgstr "Engagement" #: src/features/tournaments/components/upcoming-tournament/enroll-free-agent.tsx:26 msgid "Enroll" @@ -1066,7 +1066,7 @@ msgstr "Anmeldung" #: src/features/tournaments/components/manage-tournament.tsx:55 msgid "Enrollment & Teams" -msgstr "" +msgstr "Anmeldungen & Teams" #: src/features/tournaments/components/tournament-form.tsx:196 msgid "Enrollment Due" @@ -1095,11 +1095,11 @@ msgstr "Gib deine Telefonnummer ein" #: src/features/telemetry/components/error-browser/error-group-row.tsx:29 msgid "Error group reopened" -msgstr "" +msgstr "Fehlergruppe wieder geöffnet" #: src/features/telemetry/components/error-browser/error-group-row.tsx:29 msgid "Error group resolved" -msgstr "" +msgstr "Fehlergruppe behoben" #: src/features/activities/components/activities-table.tsx:207 msgid "Error Message" @@ -1112,19 +1112,19 @@ msgstr "Fehler: {errorMessage}" #: src/app/routes/_authed/admin/telemetry.tsx:51 #: src/features/telemetry/components/usage-screen.tsx:200 msgid "Errors" -msgstr "" +msgstr "Fehler" #: src/features/telemetry/components/client-events-table.tsx:113 msgid "Event Details" -msgstr "" +msgstr "Ereignisdetails" #: src/features/telemetry/components/client-events-table.tsx:70 msgid "Event name" -msgstr "" +msgstr "Ereignisname" #: src/app/routes/_authed/admin/telemetry.tsx:48 msgid "Events" -msgstr "" +msgstr "Ereignisse" #: src/features/tournaments/components/group-stage-view.tsx:368 msgid "Expand standings" @@ -1253,7 +1253,7 @@ msgstr "Free Agents" #: src/features/telemetry/components/usage-screen.tsx:226 msgid "Function" -msgstr "" +msgstr "Funktion" #: src/features/activities/components/activities-table.tsx:165 msgid "Function Name" @@ -1261,7 +1261,7 @@ msgstr "Funktionsname" #: src/app/routes/_authed/admin/telemetry.tsx:45 msgid "Functions" -msgstr "" +msgstr "Funktionen" #: src/app/routes/_authed/admin/tournaments/$id/assign-partners.tsx:146 msgid "Generate Random Pairings" @@ -1279,7 +1279,7 @@ msgstr "Zurück" #: src/features/telemetry/components/usage-screen.tsx:54 msgid "Good" -msgstr "" +msgstr "Gut" #: src/themes/seventies/manifest.ts:6 msgid "Groovy" @@ -1390,7 +1390,7 @@ msgstr "Wichtig: Die Anmeldung als Free Agent garantiert keinen Turnierplatz. Um #: src/features/telemetry/components/error-browser/error-browser.tsx:56 msgid "Include resolved" -msgstr "" +msgstr "Behobene einschließen" #: src/features/login/components/code-prompt.tsx:43 msgid "Incorrect code" @@ -1568,7 +1568,7 @@ msgstr "Differenz" #: src/features/telemetry/components/error-browser/error-group-row.tsx:98 msgid "Mark resolved" -msgstr "" +msgstr "Als behoben markieren" #. placeholder {0}: match.order #. placeholder {1}: match.round + 1 @@ -1636,7 +1636,7 @@ msgstr "Matchup-Details" #: src/features/telemetry/components/usage-screen.tsx:169 msgid "MAU" -msgstr "" +msgstr "MAU" #: src/features/players/components/league-head-to-head.tsx:120 msgid "Me" @@ -1682,7 +1682,7 @@ msgstr "Mindestens 4 Teams nötig, um eine Gruppenphase zu erstellen. Aktuelle T #: src/features/telemetry/components/usage-screen.tsx:56 msgid "Needs improvement" -msgstr "" +msgstr "Verbesserungswürdig" #: src/features/reactions/components/emoji-picker.tsx:23 msgid "nerd" @@ -1707,7 +1707,7 @@ msgstr "Nie aktiv" #: src/lib/telemetry/alerts.server.ts:124 msgid "New client error" -msgstr "" +msgstr "Neuer Client-Fehler" #: src/features/login/components/player-prompt/new-player-button.tsx:18 msgid "New Player" @@ -1750,7 +1750,7 @@ msgstr "Keine Aktivitäten gefunden" #: src/features/admin/components/dashboard/activity-feed.tsx:78 msgid "No activity yet" -msgstr "" +msgstr "Noch keine Aktivität" #: src/features/badges/components/badge-stats-table.tsx:45 msgid "No Badges Available" @@ -1762,7 +1762,7 @@ msgstr "Keine Geräte gefunden." #: src/features/telemetry/components/client-events-table.tsx:94 msgid "No Events Found" -msgstr "" +msgstr "Keine Ereignisse gefunden" #: src/features/tournaments/components/group-stage-view.tsx:294 msgid "No groups have been created yet" @@ -1846,7 +1846,7 @@ msgstr "Keine Turniere gefunden" #: src/features/admin/components/dashboard/tournament-card.tsx:18 msgid "No tournaments yet" -msgstr "" +msgstr "Noch keine Turniere" #: src/features/tournaments/components/tournament-card-list.tsx:40 msgid "No Tournaments Yet" @@ -1855,7 +1855,7 @@ msgstr "Noch keine Turniere" #: src/features/admin/components/dashboard/errors-card.tsx:53 #: src/features/telemetry/components/error-browser/error-browser.tsx:27 msgid "No unresolved errors" -msgstr "" +msgstr "Keine offenen Fehler" #: src/features/predictions/components/prediction-leaderboard.tsx:136 msgid "Nobody made a prediction for this tournament." @@ -1871,7 +1871,7 @@ msgstr "Stimmt nicht — unser Ergebnis eingeben" #: src/features/telemetry/components/error-browser/error-browser.tsx:28 msgid "Nothing has broken in the last 30 days. Keep it up." -msgstr "" +msgstr "In den letzten 30 Tagen ist nichts kaputtgegangen. Weiter so!" #: src/features/settings/components/notifications-section.tsx:18 #: src/features/settings/components/notifications-section.tsx:59 @@ -1916,11 +1916,11 @@ msgstr "Einmalcode" #: src/features/admin/components/dashboard/now-strip.tsx:44 msgid "Online now" -msgstr "" +msgstr "Gerade online" #: src/features/admin/components/dashboard/now-strip.tsx:46 msgid "Open errors" -msgstr "" +msgstr "Offene Fehler" #: src/features/admin/components/dashboard/tools-list.tsx:89 msgid "Open Pocketbase" @@ -1966,19 +1966,19 @@ msgstr "owo" #: src/features/telemetry/components/usage-screen.tsx:97 msgid "p50" -msgstr "" +msgstr "p50" #: src/features/telemetry/components/usage-screen.tsx:113 msgid "p95" -msgstr "" +msgstr "p95" #: src/features/admin/components/dashboard/usage-card.tsx:45 msgid "Page views" -msgstr "" +msgstr "Seitenaufrufe" #: src/features/telemetry/components/usage-screen.tsx:177 msgid "Page views by route" -msgstr "" +msgstr "Seitenaufrufe nach Route" #: src/app/routes/_authed/admin/tournaments/$id/assign-partners.tsx:155 msgid "Partner Assignments" @@ -1990,7 +1990,7 @@ msgstr "Partner werden nach Anmeldeschluss zufällig zugewiesen" #: src/features/telemetry/components/client-events-table.tsx:122 msgid "Path" -msgstr "" +msgstr "Pfad" #: src/features/spotify/components/spotify-controls-bar.tsx:116 msgid "Pause" @@ -2079,7 +2079,7 @@ msgstr "Wird auf {0} abgespielt" #: src/features/telemetry/components/usage-screen.tsx:58 msgid "Poor" -msgstr "" +msgstr "Schlecht" #: src/features/tournaments/components/group-stage-view.tsx:314 msgid "Populate Knockout Bracket" @@ -2121,7 +2121,7 @@ msgstr "Fortschritt " #: src/features/telemetry/components/client-events-table.tsx:136 msgid "Props" -msgstr "" +msgstr "Props" #: src/app/routes/_authed/tournaments/$id.predictions_.$playerId.tsx:106 #: src/features/predictions/components/prediction-leaderboard.tsx:261 @@ -2152,7 +2152,7 @@ msgstr "Abzeichen werden im Hintergrund neu berechnet ({0}/{1})" #: src/features/admin/components/dashboard/activity-feed.tsx:34 msgid "Recent activity" -msgstr "" +msgstr "Letzte Aktivität" #: src/features/tournaments/components/started-tournament/sections/recent-results.tsx:34 msgid "Recently finished" @@ -2170,7 +2170,7 @@ msgstr "Regionalturnier" #: src/features/telemetry/components/error-browser/error-group-row.tsx:98 msgid "Reopen" -msgstr "" +msgstr "Wieder öffnen" #: src/features/bracket/components/match-report.tsx:127 msgid "Report cleared" @@ -2187,7 +2187,7 @@ msgstr "Anfrage fehlgeschlagen" #: src/features/telemetry/components/error-browser/error-group-row.tsx:63 msgid "Resolved" -msgstr "" +msgstr "Behoben" #: src/features/spotify/components/spotify-controls-bar.tsx:156 msgid "Restore captured state" @@ -2216,7 +2216,7 @@ msgstr "Runde {0}" #: src/features/telemetry/components/client-events-table.tsx:125 msgid "Route" -msgstr "" +msgstr "Route" #: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:142 #: src/features/tournaments/components/upcoming-tournament/rules-list-button.tsx:23 @@ -2385,11 +2385,11 @@ msgstr "Serie steht unentschieden" #: src/lib/telemetry/alerts.server.ts:153 msgid "Server function failing" -msgstr "" +msgstr "Serverfunktion schlägt fehl" #: src/features/telemetry/components/usage-screen.tsx:186 msgid "Server functions" -msgstr "" +msgstr "Serverfunktionen" #: src/features/activities/components/activities-table.tsx:82 msgid "serverFn name" @@ -2397,11 +2397,11 @@ msgstr "serverFn-Name" #: src/features/telemetry/components/client-events-table.tsx:128 msgid "Session" -msgstr "" +msgstr "Sitzung" #: src/features/telemetry/components/usage-screen.tsx:172 msgid "Sessions" -msgstr "" +msgstr "Sitzungen" #: src/app/routes/_authed/settings.tsx:16 #: src/features/core/components/settings-button.tsx:46 @@ -2436,7 +2436,7 @@ msgstr "Skeuomorph" #: src/features/activities/components/activities-table.tsx:113 msgid "Slow >1s (this page)" -msgstr "" +msgstr "Langsam >1s (diese Seite)" #: src/components/DefaultCatchBoundary.tsx:91 msgid "Something went wrong" @@ -2519,7 +2519,7 @@ msgstr "Tippen, um zu suchen …" #: src/features/admin/components/dashboard/tournament-card.tsx:43 msgid "Starts in" -msgstr "" +msgstr "Beginnt in" #: src/features/players/components/player-stats-table.tsx:372 msgid "Stat abbreviations and MMR info" @@ -2649,7 +2649,7 @@ msgstr "Teams erfolgreich erstellt!" #: src/app/routes/_authed/admin/telemetry.tsx:20 msgid "Telemetry" -msgstr "" +msgstr "Telemetrie" #: src/themes/terminal/manifest.ts:6 msgid "Terminal" @@ -2732,7 +2732,7 @@ msgstr "Top {n} kommen weiter" #: src/features/telemetry/components/usage-screen.tsx:191 msgid "Top functions by call count" -msgstr "" +msgstr "Top-Funktionen nach Aufrufanzahl" #: src/features/matches/components/team-head-to-head-sheet.tsx:158 #: src/features/players/components/player-head-to-head-sheet.tsx:193 @@ -2788,7 +2788,7 @@ msgstr "Probier die neuen Designs aus" #: src/features/admin/components/dashboard/quiet-boundary.tsx:26 msgid "Unavailable right now" -msgstr "" +msgstr "Gerade nicht verfügbar" #: src/features/tournaments/components/upcoming-tournament/unenroll-free-agent.tsx:26 #: src/features/tournaments/components/upcoming-tournament/unenroll-team.tsx:34 @@ -2849,11 +2849,11 @@ msgstr "Turnier aktualisieren" #: src/app/routes/_authed/admin/telemetry.tsx:57 msgid "Usage" -msgstr "" +msgstr "Nutzung" #: src/features/admin/components/dashboard/usage-card.tsx:34 msgid "Usage, 7 days" -msgstr "" +msgstr "Nutzung, 7 Tage" #: src/features/activities/components/activities-table.tsx:202 #: src/features/telemetry/components/client-events-table.tsx:146 @@ -2862,7 +2862,7 @@ msgstr "User-Agent" #: src/features/telemetry/components/client-events-table.tsx:131 msgid "Value" -msgstr "" +msgstr "Wert" #: src/features/bracket/components/match-card.tsx:201 #: src/features/bracket/components/match-card.tsx:226 @@ -2900,7 +2900,7 @@ msgstr "Viewport" #: src/features/telemetry/components/usage-screen.tsx:211 msgid "Vitals" -msgstr "" +msgstr "Vitals" #: src/app/routes/api/spotify/playback.ts:70 msgid "volumePercent must be a number" @@ -2939,7 +2939,7 @@ msgstr "Walkout-Song" #: src/features/telemetry/components/usage-screen.tsx:165 msgid "WAU" -msgstr "" +msgstr "WAU" #: src/features/tournaments/components/group-stage-view.tsx:407 msgid "WC" diff --git a/src/locales/es/messages.po b/src/locales/es/messages.po index 2ecd106..29f2cea 100644 --- a/src/locales/es/messages.po +++ b/src/locales/es/messages.po @@ -60,7 +60,7 @@ msgstr "{0, plural, one {# actividad total} other {# actividades totales}}" #. placeholder {0}: result.totalItems #: src/features/telemetry/components/client-events-table.tsx:87 msgid "{0, plural, one {# total event} other {# total events}}" -msgstr "" +msgstr "{0, plural, one {# evento total} other {# eventos totales}}" #. placeholder {0}: selectedConfig.total_group_matches #: src/features/tournaments/components/setup-group-stage.tsx:249 @@ -98,7 +98,7 @@ msgstr "Poseedores de la insignia {0}" #. placeholder {0}: recentErrors.totalItems #: src/lib/telemetry/alerts.server.ts:104 msgid "{0} client errors in the last 15 minutes" -msgstr "" +msgstr "{0} errores de cliente en los últimos 15 minutos" #. placeholder {0}: match.away?.name #. placeholder {0}: match.home?.name @@ -160,7 +160,7 @@ msgstr "{count, plural, one {Equipos (#)} other {Equipos (#)}}" #: src/lib/telemetry/alerts.server.ts:167 msgid "{deniedCount} denied admin access attempts in the last 15 minutes" -msgstr "" +msgstr "{deniedCount} intentos de acceso de administrador denegados en los últimos 15 minutos" #: src/features/players/components/players-activity-table.tsx:44 #: src/features/telemetry/components/shared/relative-time.tsx:17 @@ -199,7 +199,7 @@ msgstr "{n, plural, one {# comodín} other {# comodines}}" #: src/lib/telemetry/alerts.server.ts:154 msgid "{name} failed {count} times in the last 15 minutes" -msgstr "" +msgstr "{name} falló {count} veces en los últimos 15 minutos" #: src/app/routes/_authed/admin/tournaments/$id/teams.tsx:24 msgid "{name} Teams" @@ -357,7 +357,7 @@ msgstr "2do lugar" #: src/features/telemetry/components/usage-screen.tsx:256 msgid "30d" -msgstr "" +msgstr "30d" #: src/features/teams/components/team-form/duration-picker.tsx:22 msgid "30s" @@ -373,7 +373,7 @@ msgstr "5s" #: src/features/telemetry/components/usage-screen.tsx:255 msgid "7d" -msgstr "" +msgstr "7d" #. placeholder {0}: number?.slice(0, 3) #. placeholder {1}: number?.slice(3, 6) @@ -385,7 +385,7 @@ msgstr "Se envió un código al +1 ({0}) {1}-{2}" #. placeholder {0}: info.message.slice(0, 120) #: src/lib/telemetry/alerts.server.ts:125 msgid "A new error appeared: {0}" -msgstr "" +msgstr "Apareció un nuevo error: {0}" #: src/features/bracket/components/match-report.tsx:304 msgid "a team" @@ -422,7 +422,7 @@ msgstr "Dispositivo activo: {0}" #: src/features/admin/components/dashboard/usage-card.tsx:39 msgid "Active players" -msgstr "" +msgstr "Jugadores activos" #: src/features/activities/components/activities-table.tsx:159 msgid "Activity Details" @@ -486,7 +486,7 @@ msgstr "Anunciar enfrentamiento" #: src/features/telemetry/components/client-events-table.tsx:49 msgid "Anonymous" -msgstr "" +msgstr "Anónimo" #: src/app/routes/_authed/settings.tsx:35 msgid "Appearance" @@ -656,7 +656,7 @@ msgstr "VC" #: src/features/admin/components/dashboard/now-strip.tsx:52 msgid "Calls today" -msgstr "" +msgstr "Llamadas hoy" #: src/components/sheet/slide-panel/slide-panel.tsx:41 #: src/components/sheet/slide-panel/slide-panel.tsx:190 @@ -718,15 +718,15 @@ msgstr "Quitar jugador seleccionado" #: src/lib/telemetry/alerts.server.ts:103 msgid "Client error spike" -msgstr "" +msgstr "Aumento de errores de cliente" #: src/features/admin/components/dashboard/errors-card.tsx:21 msgid "Client errors" -msgstr "" +msgstr "Errores de cliente" #: src/features/telemetry/components/usage-screen.tsx:203 msgid "Client errors per day" -msgstr "" +msgstr "Errores de cliente por día" #: src/components/sheet/modal.tsx:35 #: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:169 @@ -886,11 +886,11 @@ msgstr "Código hex personalizado" #: src/features/admin/components/dashboard/usage-card.tsx:41 #: src/features/telemetry/components/usage-screen.tsx:159 msgid "Daily active players" -msgstr "" +msgstr "Jugadores activos diarios" #: src/features/admin/components/dashboard/usage-card.tsx:47 msgid "Daily page views" -msgstr "" +msgstr "Vistas de página diarias" #: src/features/settings/components/color-scheme-picker.tsx:39 msgid "Dark" @@ -914,7 +914,7 @@ msgstr "Predeterminado" #: src/lib/telemetry/alerts.server.ts:166 msgid "Denied access attempt" -msgstr "" +msgstr "Intento de acceso denegado" #: src/features/tournaments/components/tournament-form.tsx:150 msgid "Description" @@ -949,7 +949,7 @@ msgstr "Duración" #: src/features/telemetry/components/usage-screen.tsx:233 msgid "Duration p50 / p95" -msgstr "" +msgstr "Duración p50 / p95" #: src/features/tournaments/components/tournament-stats.tsx:176 msgid "Earlier regional formats aren't supported in the app and order of matches or displayed winners may be unreliable." @@ -1011,7 +1011,7 @@ msgstr "Terminar partido" #: src/features/telemetry/components/usage-screen.tsx:156 msgid "Engagement" -msgstr "" +msgstr "Interacción" #: src/features/tournaments/components/upcoming-tournament/enroll-free-agent.tsx:26 msgid "Enroll" @@ -1066,7 +1066,7 @@ msgstr "Inscripción" #: src/features/tournaments/components/manage-tournament.tsx:55 msgid "Enrollment & Teams" -msgstr "" +msgstr "Inscripción y equipos" #: src/features/tournaments/components/tournament-form.tsx:196 msgid "Enrollment Due" @@ -1095,11 +1095,11 @@ msgstr "Ingresa tu número de teléfono" #: src/features/telemetry/components/error-browser/error-group-row.tsx:29 msgid "Error group reopened" -msgstr "" +msgstr "Grupo de errores reabierto" #: src/features/telemetry/components/error-browser/error-group-row.tsx:29 msgid "Error group resolved" -msgstr "" +msgstr "Grupo de errores resuelto" #: src/features/activities/components/activities-table.tsx:207 msgid "Error Message" @@ -1112,19 +1112,19 @@ msgstr "Error: {errorMessage}" #: src/app/routes/_authed/admin/telemetry.tsx:51 #: src/features/telemetry/components/usage-screen.tsx:200 msgid "Errors" -msgstr "" +msgstr "Errores" #: src/features/telemetry/components/client-events-table.tsx:113 msgid "Event Details" -msgstr "" +msgstr "Detalles del evento" #: src/features/telemetry/components/client-events-table.tsx:70 msgid "Event name" -msgstr "" +msgstr "Nombre del evento" #: src/app/routes/_authed/admin/telemetry.tsx:48 msgid "Events" -msgstr "" +msgstr "Eventos" #: src/features/tournaments/components/group-stage-view.tsx:368 msgid "Expand standings" @@ -1253,7 +1253,7 @@ msgstr "Agentes libres" #: src/features/telemetry/components/usage-screen.tsx:226 msgid "Function" -msgstr "" +msgstr "Función" #: src/features/activities/components/activities-table.tsx:165 msgid "Function Name" @@ -1261,7 +1261,7 @@ msgstr "Nombre de la función" #: src/app/routes/_authed/admin/telemetry.tsx:45 msgid "Functions" -msgstr "" +msgstr "Funciones" #: src/app/routes/_authed/admin/tournaments/$id/assign-partners.tsx:146 msgid "Generate Random Pairings" @@ -1279,7 +1279,7 @@ msgstr "Regresar" #: src/features/telemetry/components/usage-screen.tsx:54 msgid "Good" -msgstr "" +msgstr "Bueno" #: src/themes/seventies/manifest.ts:6 msgid "Groovy" @@ -1390,7 +1390,7 @@ msgstr "Importante: inscribirte como agente libre no garantiza un lugar en el to #: src/features/telemetry/components/error-browser/error-browser.tsx:56 msgid "Include resolved" -msgstr "" +msgstr "Incluir resueltos" #: src/features/login/components/code-prompt.tsx:43 msgid "Incorrect code" @@ -1568,7 +1568,7 @@ msgstr "margen" #: src/features/telemetry/components/error-browser/error-group-row.tsx:98 msgid "Mark resolved" -msgstr "" +msgstr "Marcar como resuelto" #. placeholder {0}: match.order #. placeholder {1}: match.round + 1 @@ -1636,7 +1636,7 @@ msgstr "Detalles del enfrentamiento" #: src/features/telemetry/components/usage-screen.tsx:169 msgid "MAU" -msgstr "" +msgstr "MAU" #: src/features/players/components/league-head-to-head.tsx:120 msgid "Me" @@ -1682,7 +1682,7 @@ msgstr "Se necesitan al menos 4 equipos para crear un formato de fase de grupos. #: src/features/telemetry/components/usage-screen.tsx:56 msgid "Needs improvement" -msgstr "" +msgstr "Necesita mejorar" #: src/features/reactions/components/emoji-picker.tsx:23 msgid "nerd" @@ -1707,7 +1707,7 @@ msgstr "Nunca activo" #: src/lib/telemetry/alerts.server.ts:124 msgid "New client error" -msgstr "" +msgstr "Nuevo error de cliente" #: src/features/login/components/player-prompt/new-player-button.tsx:18 msgid "New Player" @@ -1750,7 +1750,7 @@ msgstr "No se encontraron actividades" #: src/features/admin/components/dashboard/activity-feed.tsx:78 msgid "No activity yet" -msgstr "" +msgstr "Aún no hay actividad" #: src/features/badges/components/badge-stats-table.tsx:45 msgid "No Badges Available" @@ -1762,7 +1762,7 @@ msgstr "No se encontraron dispositivos." #: src/features/telemetry/components/client-events-table.tsx:94 msgid "No Events Found" -msgstr "" +msgstr "No se encontraron eventos" #: src/features/tournaments/components/group-stage-view.tsx:294 msgid "No groups have been created yet" @@ -1846,7 +1846,7 @@ msgstr "No se encontraron torneos" #: src/features/admin/components/dashboard/tournament-card.tsx:18 msgid "No tournaments yet" -msgstr "" +msgstr "Aún no hay torneos" #: src/features/tournaments/components/tournament-card-list.tsx:40 msgid "No Tournaments Yet" @@ -1855,7 +1855,7 @@ msgstr "Aún no hay torneos" #: src/features/admin/components/dashboard/errors-card.tsx:53 #: src/features/telemetry/components/error-browser/error-browser.tsx:27 msgid "No unresolved errors" -msgstr "" +msgstr "No hay errores sin resolver" #: src/features/predictions/components/prediction-leaderboard.tsx:136 msgid "Nobody made a prediction for this tournament." @@ -1871,7 +1871,7 @@ msgstr "Incorrecto — ingresa nuestro marcador" #: src/features/telemetry/components/error-browser/error-browser.tsx:28 msgid "Nothing has broken in the last 30 days. Keep it up." -msgstr "" +msgstr "Nada se ha roto en los últimos 30 días. Sigue así." #: src/features/settings/components/notifications-section.tsx:18 #: src/features/settings/components/notifications-section.tsx:59 @@ -1916,11 +1916,11 @@ msgstr "Código de un solo uso" #: src/features/admin/components/dashboard/now-strip.tsx:44 msgid "Online now" -msgstr "" +msgstr "En línea ahora" #: src/features/admin/components/dashboard/now-strip.tsx:46 msgid "Open errors" -msgstr "" +msgstr "Errores abiertos" #: src/features/admin/components/dashboard/tools-list.tsx:89 msgid "Open Pocketbase" @@ -1966,19 +1966,19 @@ msgstr "owo" #: src/features/telemetry/components/usage-screen.tsx:97 msgid "p50" -msgstr "" +msgstr "p50" #: src/features/telemetry/components/usage-screen.tsx:113 msgid "p95" -msgstr "" +msgstr "p95" #: src/features/admin/components/dashboard/usage-card.tsx:45 msgid "Page views" -msgstr "" +msgstr "Vistas de página" #: src/features/telemetry/components/usage-screen.tsx:177 msgid "Page views by route" -msgstr "" +msgstr "Vistas de página por ruta" #: src/app/routes/_authed/admin/tournaments/$id/assign-partners.tsx:155 msgid "Partner Assignments" @@ -1990,7 +1990,7 @@ msgstr "Los compañeros se asignarán al azar cuando cierre la inscripción" #: src/features/telemetry/components/client-events-table.tsx:122 msgid "Path" -msgstr "" +msgstr "Ruta" #: src/features/spotify/components/spotify-controls-bar.tsx:116 msgid "Pause" @@ -2079,7 +2079,7 @@ msgstr "Reproduciendo en {0}" #: src/features/telemetry/components/usage-screen.tsx:58 msgid "Poor" -msgstr "" +msgstr "Deficiente" #: src/features/tournaments/components/group-stage-view.tsx:314 msgid "Populate Knockout Bracket" @@ -2121,7 +2121,7 @@ msgstr "Progreso " #: src/features/telemetry/components/client-events-table.tsx:136 msgid "Props" -msgstr "" +msgstr "Props" #: src/app/routes/_authed/tournaments/$id.predictions_.$playerId.tsx:106 #: src/features/predictions/components/prediction-leaderboard.tsx:261 @@ -2152,7 +2152,7 @@ msgstr "Recalculando insignias en segundo plano ({0}/{1})" #: src/features/admin/components/dashboard/activity-feed.tsx:34 msgid "Recent activity" -msgstr "" +msgstr "Actividad reciente" #: src/features/tournaments/components/started-tournament/sections/recent-results.tsx:34 msgid "Recently finished" @@ -2170,7 +2170,7 @@ msgstr "Torneo regional" #: src/features/telemetry/components/error-browser/error-group-row.tsx:98 msgid "Reopen" -msgstr "" +msgstr "Reabrir" #: src/features/bracket/components/match-report.tsx:127 msgid "Report cleared" @@ -2187,7 +2187,7 @@ msgstr "Falló la solicitud" #: src/features/telemetry/components/error-browser/error-group-row.tsx:63 msgid "Resolved" -msgstr "" +msgstr "Resuelto" #: src/features/spotify/components/spotify-controls-bar.tsx:156 msgid "Restore captured state" @@ -2216,7 +2216,7 @@ msgstr "Ronda {0}" #: src/features/telemetry/components/client-events-table.tsx:125 msgid "Route" -msgstr "" +msgstr "Ruta" #: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:142 #: src/features/tournaments/components/upcoming-tournament/rules-list-button.tsx:23 @@ -2385,11 +2385,11 @@ msgstr "La serie está empatada" #: src/lib/telemetry/alerts.server.ts:153 msgid "Server function failing" -msgstr "" +msgstr "Función del servidor fallando" #: src/features/telemetry/components/usage-screen.tsx:186 msgid "Server functions" -msgstr "" +msgstr "Funciones del servidor" #: src/features/activities/components/activities-table.tsx:82 msgid "serverFn name" @@ -2397,11 +2397,11 @@ msgstr "nombre de serverFn" #: src/features/telemetry/components/client-events-table.tsx:128 msgid "Session" -msgstr "" +msgstr "Sesión" #: src/features/telemetry/components/usage-screen.tsx:172 msgid "Sessions" -msgstr "" +msgstr "Sesiones" #: src/app/routes/_authed/settings.tsx:16 #: src/features/core/components/settings-button.tsx:46 @@ -2436,7 +2436,7 @@ msgstr "Esqueuomórfico" #: src/features/activities/components/activities-table.tsx:113 msgid "Slow >1s (this page)" -msgstr "" +msgstr "Lento >1s (esta página)" #: src/components/DefaultCatchBoundary.tsx:91 msgid "Something went wrong" @@ -2519,7 +2519,7 @@ msgstr "Empieza a escribir para buscar..." #: src/features/admin/components/dashboard/tournament-card.tsx:43 msgid "Starts in" -msgstr "" +msgstr "Comienza en" #: src/features/players/components/player-stats-table.tsx:372 msgid "Stat abbreviations and MMR info" @@ -2649,7 +2649,7 @@ msgstr "¡Equipos creados exitosamente!" #: src/app/routes/_authed/admin/telemetry.tsx:20 msgid "Telemetry" -msgstr "" +msgstr "Telemetría" #: src/themes/terminal/manifest.ts:6 msgid "Terminal" @@ -2732,7 +2732,7 @@ msgstr "Los mejores {n} avanzan" #: src/features/telemetry/components/usage-screen.tsx:191 msgid "Top functions by call count" -msgstr "" +msgstr "Funciones principales por cantidad de llamadas" #: src/features/matches/components/team-head-to-head-sheet.tsx:158 #: src/features/players/components/player-head-to-head-sheet.tsx:193 @@ -2788,7 +2788,7 @@ msgstr "Prueba los nuevos temas" #: src/features/admin/components/dashboard/quiet-boundary.tsx:26 msgid "Unavailable right now" -msgstr "" +msgstr "No disponible por ahora" #: src/features/tournaments/components/upcoming-tournament/unenroll-free-agent.tsx:26 #: src/features/tournaments/components/upcoming-tournament/unenroll-team.tsx:34 @@ -2849,11 +2849,11 @@ msgstr "Actualizar torneo" #: src/app/routes/_authed/admin/telemetry.tsx:57 msgid "Usage" -msgstr "" +msgstr "Uso" #: src/features/admin/components/dashboard/usage-card.tsx:34 msgid "Usage, 7 days" -msgstr "" +msgstr "Uso, 7 días" #: src/features/activities/components/activities-table.tsx:202 #: src/features/telemetry/components/client-events-table.tsx:146 @@ -2862,7 +2862,7 @@ msgstr "User Agent" #: src/features/telemetry/components/client-events-table.tsx:131 msgid "Value" -msgstr "" +msgstr "Valor" #: src/features/bracket/components/match-card.tsx:201 #: src/features/bracket/components/match-card.tsx:226 @@ -2900,7 +2900,7 @@ msgstr "Viewport" #: src/features/telemetry/components/usage-screen.tsx:211 msgid "Vitals" -msgstr "" +msgstr "Vitales" #: src/app/routes/api/spotify/playback.ts:70 msgid "volumePercent must be a number" @@ -2939,7 +2939,7 @@ msgstr "Canción de entrada" #: src/features/telemetry/components/usage-screen.tsx:165 msgid "WAU" -msgstr "" +msgstr "WAU" #: src/features/tournaments/components/group-stage-view.tsx:407 msgid "WC" diff --git a/src/locales/ja/messages.po b/src/locales/ja/messages.po index 9136b9e..329fa3a 100644 --- a/src/locales/ja/messages.po +++ b/src/locales/ja/messages.po @@ -60,7 +60,7 @@ msgstr "{0, plural, one {アクティビティ合計#件} other {アクティビ #. placeholder {0}: result.totalItems #: src/features/telemetry/components/client-events-table.tsx:87 msgid "{0, plural, one {# total event} other {# total events}}" -msgstr "" +msgstr "{0, plural, one {イベント合計#件} other {イベント合計#件}}" #. placeholder {0}: selectedConfig.total_group_matches #: src/features/tournaments/components/setup-group-stage.tsx:249 @@ -98,7 +98,7 @@ msgstr "{0}バッジ保有者" #. placeholder {0}: recentErrors.totalItems #: src/lib/telemetry/alerts.server.ts:104 msgid "{0} client errors in the last 15 minutes" -msgstr "" +msgstr "過去15分間に{0}件のクライアントエラーが発生しました" #. placeholder {0}: match.away?.name #. placeholder {0}: match.home?.name @@ -160,7 +160,7 @@ msgstr "{count, plural, one {チーム(#)} other {チーム(#)}}" #: src/lib/telemetry/alerts.server.ts:167 msgid "{deniedCount} denied admin access attempts in the last 15 minutes" -msgstr "" +msgstr "過去15分間に{deniedCount}件の管理者アクセス拒否がありました" #: src/features/players/components/players-activity-table.tsx:44 #: src/features/telemetry/components/shared/relative-time.tsx:17 @@ -199,7 +199,7 @@ msgstr "{n, plural, one {#ワイルドカード} other {#ワイルドカード}} #: src/lib/telemetry/alerts.server.ts:154 msgid "{name} failed {count} times in the last 15 minutes" -msgstr "" +msgstr "{name}が過去15分間に{count}回失敗しました" #: src/app/routes/_authed/admin/tournaments/$id/teams.tsx:24 msgid "{name} Teams" @@ -357,7 +357,7 @@ msgstr "2位" #: src/features/telemetry/components/usage-screen.tsx:256 msgid "30d" -msgstr "" +msgstr "30日" #: src/features/teams/components/team-form/duration-picker.tsx:22 msgid "30s" @@ -373,7 +373,7 @@ msgstr "5秒" #: src/features/telemetry/components/usage-screen.tsx:255 msgid "7d" -msgstr "" +msgstr "7日" #. placeholder {0}: number?.slice(0, 3) #. placeholder {1}: number?.slice(3, 6) @@ -385,7 +385,7 @@ msgstr "認証コードを +1 ({0}) {1}-{2} に送信しました" #. placeholder {0}: info.message.slice(0, 120) #: src/lib/telemetry/alerts.server.ts:125 msgid "A new error appeared: {0}" -msgstr "" +msgstr "新しいエラーが発生しました: {0}" #: src/features/bracket/components/match-report.tsx:304 msgid "a team" @@ -422,7 +422,7 @@ msgstr "アクティブなデバイス: {0}" #: src/features/admin/components/dashboard/usage-card.tsx:39 msgid "Active players" -msgstr "" +msgstr "アクティブなプレイヤー" #: src/features/activities/components/activities-table.tsx:159 msgid "Activity Details" @@ -486,7 +486,7 @@ msgstr "対戦カードを発表" #: src/features/telemetry/components/client-events-table.tsx:49 msgid "Anonymous" -msgstr "" +msgstr "匿名" #: src/app/routes/_authed/settings.tsx:35 msgid "Appearance" @@ -656,7 +656,7 @@ msgstr "CA" #: src/features/admin/components/dashboard/now-strip.tsx:52 msgid "Calls today" -msgstr "" +msgstr "本日の呼び出し数" #: src/components/sheet/slide-panel/slide-panel.tsx:41 #: src/components/sheet/slide-panel/slide-panel.tsx:190 @@ -718,15 +718,15 @@ msgstr "選択したプレイヤーを解除" #: src/lib/telemetry/alerts.server.ts:103 msgid "Client error spike" -msgstr "" +msgstr "クライアントエラー急増" #: src/features/admin/components/dashboard/errors-card.tsx:21 msgid "Client errors" -msgstr "" +msgstr "クライアントエラー" #: src/features/telemetry/components/usage-screen.tsx:203 msgid "Client errors per day" -msgstr "" +msgstr "1日あたりのクライアントエラー数" #: src/components/sheet/modal.tsx:35 #: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:169 @@ -886,11 +886,11 @@ msgstr "カスタムHEXコード" #: src/features/admin/components/dashboard/usage-card.tsx:41 #: src/features/telemetry/components/usage-screen.tsx:159 msgid "Daily active players" -msgstr "" +msgstr "デイリーアクティブプレイヤー数" #: src/features/admin/components/dashboard/usage-card.tsx:47 msgid "Daily page views" -msgstr "" +msgstr "1日あたりのページビュー数" #: src/features/settings/components/color-scheme-picker.tsx:39 msgid "Dark" @@ -914,7 +914,7 @@ msgstr "デフォルト" #: src/lib/telemetry/alerts.server.ts:166 msgid "Denied access attempt" -msgstr "" +msgstr "アクセス拒否の試行" #: src/features/tournaments/components/tournament-form.tsx:150 msgid "Description" @@ -949,7 +949,7 @@ msgstr "所要時間" #: src/features/telemetry/components/usage-screen.tsx:233 msgid "Duration p50 / p95" -msgstr "" +msgstr "所要時間 p50 / p95" #: src/features/tournaments/components/tournament-stats.tsx:176 msgid "Earlier regional formats aren't supported in the app and order of matches or displayed winners may be unreliable." @@ -1011,7 +1011,7 @@ msgstr "試合を終了" #: src/features/telemetry/components/usage-screen.tsx:156 msgid "Engagement" -msgstr "" +msgstr "エンゲージメント" #: src/features/tournaments/components/upcoming-tournament/enroll-free-agent.tsx:26 msgid "Enroll" @@ -1066,7 +1066,7 @@ msgstr "参加登録" #: src/features/tournaments/components/manage-tournament.tsx:55 msgid "Enrollment & Teams" -msgstr "" +msgstr "参加登録とチーム" #: src/features/tournaments/components/tournament-form.tsx:196 msgid "Enrollment Due" @@ -1095,11 +1095,11 @@ msgstr "電話番号を入力" #: src/features/telemetry/components/error-browser/error-group-row.tsx:29 msgid "Error group reopened" -msgstr "" +msgstr "エラーグループを再オープンしました" #: src/features/telemetry/components/error-browser/error-group-row.tsx:29 msgid "Error group resolved" -msgstr "" +msgstr "エラーグループを解決済みにしました" #: src/features/activities/components/activities-table.tsx:207 msgid "Error Message" @@ -1112,19 +1112,19 @@ msgstr "エラー: {errorMessage}" #: src/app/routes/_authed/admin/telemetry.tsx:51 #: src/features/telemetry/components/usage-screen.tsx:200 msgid "Errors" -msgstr "" +msgstr "エラー" #: src/features/telemetry/components/client-events-table.tsx:113 msgid "Event Details" -msgstr "" +msgstr "イベント詳細" #: src/features/telemetry/components/client-events-table.tsx:70 msgid "Event name" -msgstr "" +msgstr "イベント名" #: src/app/routes/_authed/admin/telemetry.tsx:48 msgid "Events" -msgstr "" +msgstr "イベント" #: src/features/tournaments/components/group-stage-view.tsx:368 msgid "Expand standings" @@ -1253,7 +1253,7 @@ msgstr "フリーエージェント" #: src/features/telemetry/components/usage-screen.tsx:226 msgid "Function" -msgstr "" +msgstr "関数" #: src/features/activities/components/activities-table.tsx:165 msgid "Function Name" @@ -1261,7 +1261,7 @@ msgstr "関数名" #: src/app/routes/_authed/admin/telemetry.tsx:45 msgid "Functions" -msgstr "" +msgstr "関数" #: src/app/routes/_authed/admin/tournaments/$id/assign-partners.tsx:146 msgid "Generate Random Pairings" @@ -1279,7 +1279,7 @@ msgstr "戻る" #: src/features/telemetry/components/usage-screen.tsx:54 msgid "Good" -msgstr "" +msgstr "良好" #: src/themes/seventies/manifest.ts:6 msgid "Groovy" @@ -1390,7 +1390,7 @@ msgstr "重要: フリーエージェントとして登録しても、トーナ #: src/features/telemetry/components/error-browser/error-browser.tsx:56 msgid "Include resolved" -msgstr "" +msgstr "解決済みを含める" #: src/features/login/components/code-prompt.tsx:43 msgid "Incorrect code" @@ -1568,7 +1568,7 @@ msgstr "得点差" #: src/features/telemetry/components/error-browser/error-group-row.tsx:98 msgid "Mark resolved" -msgstr "" +msgstr "解決済みにする" #. placeholder {0}: match.order #. placeholder {1}: match.round + 1 @@ -1636,7 +1636,7 @@ msgstr "対戦カードの詳細" #: src/features/telemetry/components/usage-screen.tsx:169 msgid "MAU" -msgstr "" +msgstr "MAU" #: src/features/players/components/league-head-to-head.tsx:120 msgid "Me" @@ -1682,7 +1682,7 @@ msgstr "グループステージ形式を作成するには4チーム以上が #: src/features/telemetry/components/usage-screen.tsx:56 msgid "Needs improvement" -msgstr "" +msgstr "要改善" #: src/features/reactions/components/emoji-picker.tsx:23 msgid "nerd" @@ -1707,7 +1707,7 @@ msgstr "アクティビティなし" #: src/lib/telemetry/alerts.server.ts:124 msgid "New client error" -msgstr "" +msgstr "新しいクライアントエラー" #: src/features/login/components/player-prompt/new-player-button.tsx:18 msgid "New Player" @@ -1750,7 +1750,7 @@ msgstr "アクティビティが見つかりません" #: src/features/admin/components/dashboard/activity-feed.tsx:78 msgid "No activity yet" -msgstr "" +msgstr "まだアクティビティがありません" #: src/features/badges/components/badge-stats-table.tsx:45 msgid "No Badges Available" @@ -1762,7 +1762,7 @@ msgstr "デバイスが見つかりません。" #: src/features/telemetry/components/client-events-table.tsx:94 msgid "No Events Found" -msgstr "" +msgstr "イベントが見つかりません" #: src/features/tournaments/components/group-stage-view.tsx:294 msgid "No groups have been created yet" @@ -1846,7 +1846,7 @@ msgstr "トーナメントが見つかりません" #: src/features/admin/components/dashboard/tournament-card.tsx:18 msgid "No tournaments yet" -msgstr "" +msgstr "トーナメントはまだありません" #: src/features/tournaments/components/tournament-card-list.tsx:40 msgid "No Tournaments Yet" @@ -1855,7 +1855,7 @@ msgstr "トーナメントはまだありません" #: src/features/admin/components/dashboard/errors-card.tsx:53 #: src/features/telemetry/components/error-browser/error-browser.tsx:27 msgid "No unresolved errors" -msgstr "" +msgstr "未解決のエラーはありません" #: src/features/predictions/components/prediction-leaderboard.tsx:136 msgid "Nobody made a prediction for this tournament." @@ -1871,7 +1871,7 @@ msgstr "違う — スコアを入力" #: src/features/telemetry/components/error-browser/error-browser.tsx:28 msgid "Nothing has broken in the last 30 days. Keep it up." -msgstr "" +msgstr "過去30日間、何も壊れていません。この調子です。" #: src/features/settings/components/notifications-section.tsx:18 #: src/features/settings/components/notifications-section.tsx:59 @@ -1916,11 +1916,11 @@ msgstr "ワンタイムコード" #: src/features/admin/components/dashboard/now-strip.tsx:44 msgid "Online now" -msgstr "" +msgstr "現在オンライン" #: src/features/admin/components/dashboard/now-strip.tsx:46 msgid "Open errors" -msgstr "" +msgstr "未解決のエラー" #: src/features/admin/components/dashboard/tools-list.tsx:89 msgid "Open Pocketbase" @@ -1966,19 +1966,19 @@ msgstr "owo" #: src/features/telemetry/components/usage-screen.tsx:97 msgid "p50" -msgstr "" +msgstr "p50" #: src/features/telemetry/components/usage-screen.tsx:113 msgid "p95" -msgstr "" +msgstr "p95" #: src/features/admin/components/dashboard/usage-card.tsx:45 msgid "Page views" -msgstr "" +msgstr "ページビュー数" #: src/features/telemetry/components/usage-screen.tsx:177 msgid "Page views by route" -msgstr "" +msgstr "ルート別ページビュー数" #: src/app/routes/_authed/admin/tournaments/$id/assign-partners.tsx:155 msgid "Partner Assignments" @@ -1990,7 +1990,7 @@ msgstr "登録が締め切られると、パートナーはランダムに割り #: src/features/telemetry/components/client-events-table.tsx:122 msgid "Path" -msgstr "" +msgstr "パス" #: src/features/spotify/components/spotify-controls-bar.tsx:116 msgid "Pause" @@ -2079,7 +2079,7 @@ msgstr "{0}で再生中" #: src/features/telemetry/components/usage-screen.tsx:58 msgid "Poor" -msgstr "" +msgstr "不良" #: src/features/tournaments/components/group-stage-view.tsx:314 msgid "Populate Knockout Bracket" @@ -2121,7 +2121,7 @@ msgstr "進行状況 " #: src/features/telemetry/components/client-events-table.tsx:136 msgid "Props" -msgstr "" +msgstr "プロパティ" #: src/app/routes/_authed/tournaments/$id.predictions_.$playerId.tsx:106 #: src/features/predictions/components/prediction-leaderboard.tsx:261 @@ -2152,7 +2152,7 @@ msgstr "バックグラウンドでバッジを再計算中({0}/{1})" #: src/features/admin/components/dashboard/activity-feed.tsx:34 msgid "Recent activity" -msgstr "" +msgstr "最近のアクティビティ" #: src/features/tournaments/components/started-tournament/sections/recent-results.tsx:34 msgid "Recently finished" @@ -2170,7 +2170,7 @@ msgstr "地域トーナメント" #: src/features/telemetry/components/error-browser/error-group-row.tsx:98 msgid "Reopen" -msgstr "" +msgstr "再オープン" #: src/features/bracket/components/match-report.tsx:127 msgid "Report cleared" @@ -2187,7 +2187,7 @@ msgstr "リクエストに失敗しました" #: src/features/telemetry/components/error-browser/error-group-row.tsx:63 msgid "Resolved" -msgstr "" +msgstr "解決済み" #: src/features/spotify/components/spotify-controls-bar.tsx:156 msgid "Restore captured state" @@ -2216,7 +2216,7 @@ msgstr "ラウンド{0}" #: src/features/telemetry/components/client-events-table.tsx:125 msgid "Route" -msgstr "" +msgstr "ルート" #: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:142 #: src/features/tournaments/components/upcoming-tournament/rules-list-button.tsx:23 @@ -2385,11 +2385,11 @@ msgstr "シリーズはタイです" #: src/lib/telemetry/alerts.server.ts:153 msgid "Server function failing" -msgstr "" +msgstr "サーバー関数が失敗しています" #: src/features/telemetry/components/usage-screen.tsx:186 msgid "Server functions" -msgstr "" +msgstr "サーバー関数" #: src/features/activities/components/activities-table.tsx:82 msgid "serverFn name" @@ -2397,11 +2397,11 @@ msgstr "serverFn名" #: src/features/telemetry/components/client-events-table.tsx:128 msgid "Session" -msgstr "" +msgstr "セッション" #: src/features/telemetry/components/usage-screen.tsx:172 msgid "Sessions" -msgstr "" +msgstr "セッション数" #: src/app/routes/_authed/settings.tsx:16 #: src/features/core/components/settings-button.tsx:46 @@ -2436,7 +2436,7 @@ msgstr "スキューモーフィズム" #: src/features/activities/components/activities-table.tsx:113 msgid "Slow >1s (this page)" -msgstr "" +msgstr "1秒超の遅延(このページ)" #: src/components/DefaultCatchBoundary.tsx:91 msgid "Something went wrong" @@ -2519,7 +2519,7 @@ msgstr "入力して検索..." #: src/features/admin/components/dashboard/tournament-card.tsx:43 msgid "Starts in" -msgstr "" +msgstr "開始まで" #: src/features/players/components/player-stats-table.tsx:372 msgid "Stat abbreviations and MMR info" @@ -2649,7 +2649,7 @@ msgstr "チームを作成しました!" #: src/app/routes/_authed/admin/telemetry.tsx:20 msgid "Telemetry" -msgstr "" +msgstr "テレメトリー" #: src/themes/terminal/manifest.ts:6 msgid "Terminal" @@ -2732,7 +2732,7 @@ msgstr "上位{n}チームが勝ち上がり" #: src/features/telemetry/components/usage-screen.tsx:191 msgid "Top functions by call count" -msgstr "" +msgstr "呼び出し数の多い関数" #: src/features/matches/components/team-head-to-head-sheet.tsx:158 #: src/features/players/components/player-head-to-head-sheet.tsx:193 @@ -2788,7 +2788,7 @@ msgstr "新しいテーマを試してみましょう" #: src/features/admin/components/dashboard/quiet-boundary.tsx:26 msgid "Unavailable right now" -msgstr "" +msgstr "現在利用できません" #: src/features/tournaments/components/upcoming-tournament/unenroll-free-agent.tsx:26 #: src/features/tournaments/components/upcoming-tournament/unenroll-team.tsx:34 @@ -2849,11 +2849,11 @@ msgstr "トーナメントを更新" #: src/app/routes/_authed/admin/telemetry.tsx:57 msgid "Usage" -msgstr "" +msgstr "利用状況" #: src/features/admin/components/dashboard/usage-card.tsx:34 msgid "Usage, 7 days" -msgstr "" +msgstr "利用状況(7日間)" #: src/features/activities/components/activities-table.tsx:202 #: src/features/telemetry/components/client-events-table.tsx:146 @@ -2862,7 +2862,7 @@ msgstr "ユーザーエージェント" #: src/features/telemetry/components/client-events-table.tsx:131 msgid "Value" -msgstr "" +msgstr "値" #: src/features/bracket/components/match-card.tsx:201 #: src/features/bracket/components/match-card.tsx:226 @@ -2900,7 +2900,7 @@ msgstr "ビューポート" #: src/features/telemetry/components/usage-screen.tsx:211 msgid "Vitals" -msgstr "" +msgstr "バイタル" #: src/app/routes/api/spotify/playback.ts:70 msgid "volumePercent must be a number" @@ -2939,7 +2939,7 @@ msgstr "ウォークアウトソング" #: src/features/telemetry/components/usage-screen.tsx:165 msgid "WAU" -msgstr "" +msgstr "WAU" #: src/features/tournaments/components/group-stage-view.tsx:407 msgid "WC" -- 2.54.0 From 13481936f0d2e4ac45b85916ae1e446d0a3c3ffc Mon Sep 17 00:00:00 2001 From: yohlo Date: Tue, 25 Aug 2026 22:05:25 -0700 Subject: [PATCH 07/33] feat(start): csrf middleware for server functions --- src/app/start.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/app/start.ts b/src/app/start.ts index 7f79732..4e96775 100644 --- a/src/app/start.ts +++ b/src/app/start.ts @@ -1,8 +1,13 @@ -import { createStart } from "@tanstack/react-start"; +import { createCsrfMiddleware, createStart } from "@tanstack/react-start"; import { serverFnLoggingMiddleware } from "@/utils/activities"; +const csrfMiddleware = createCsrfMiddleware({ + filter: (ctx) => ctx.handlerType === "serverFn", +}); + // Global function middleware: every server fn (current and future) is audited // without opting in at the definition site. export const startInstance = createStart(() => ({ + requestMiddleware: [csrfMiddleware], functionMiddleware: [serverFnLoggingMiddleware], })); -- 2.54.0 From 2b02de219332f3b5186f9e85daa1671cd57fbad7 Mon Sep 17 00:00:00 2001 From: yohlo Date: Tue, 25 Aug 2026 22:09:08 -0700 Subject: [PATCH 08/33] fix(telemetry): start scheduler from admin dashboard too, not only health probes --- src/features/telemetry/server.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/features/telemetry/server.ts b/src/features/telemetry/server.ts index a94eb54..60969bc 100644 --- a/src/features/telemetry/server.ts +++ b/src/features/telemetry/server.ts @@ -7,7 +7,7 @@ import { transformClientError, transformClientEvent, } from "@/lib/pocketbase/util/transform-types"; -import { getSchedulerStatus } from "@/lib/telemetry/scheduler.server"; +import { ensureTelemetryScheduler, getSchedulerStatus } from "@/lib/telemetry/scheduler.server"; import { getHealthSnapshot } from "@/lib/telemetry/health.server"; import { getActiveConnectionCount } from "@/lib/events/emitter"; import type { @@ -161,6 +161,9 @@ export const getTelemetryRuntimeStatus = createServerFn() .middleware([superTokensAdminFunctionMiddleware]) .handler(async () => toServerResult(async () => { + // Dev has no k8s probes hitting /api/health, so opening the dashboard + // is the scheduler's fallback starter. + ensureTelemetryScheduler(); const health = await getHealthSnapshot(); return { health: { status: health.status, checks: health.checks }, -- 2.54.0 From 5e9d0302971b80561808a7f0db582b93fb334045 Mon Sep 17 00:00:00 2001 From: yohlo Date: Tue, 25 Aug 2026 22:11:09 -0700 Subject: [PATCH 09/33] fix(telemetry): enable client tracking in dev --- src/lib/telemetry/client/index.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/lib/telemetry/client/index.ts b/src/lib/telemetry/client/index.ts index 917beb1..eebcfc8 100644 --- a/src/lib/telemetry/client/index.ts +++ b/src/lib/telemetry/client/index.ts @@ -2,7 +2,8 @@ import type { AnyRouter } from "@tanstack/react-router"; // Provider-free by design: renders nothing and mounts no React context, so it // cannot disturb the LinguiProvider -> AuthProvider order. Browser-only; every -// entry point is a no-op during SSR and in dev unless VITE_TELEMETRY_DEV=1. +// entry point is a no-op during SSR. Runs in dev too - events go to the same +// PB the app is pointed at, which in dev is the local instance. type QueuedEvent = Record & { kind: "event" | "error" | "vital" }; @@ -19,9 +20,7 @@ let lastRouteId: string | undefined; let errorCount = 0; const recentErrors = new Map(); -const isEnabled = (): boolean => - typeof window !== "undefined" && - (import.meta.env.PROD || import.meta.env.VITE_TELEMETRY_DEV === "1"); +const isEnabled = (): boolean => typeof window !== "undefined"; const getSessionId = (): string => { try { -- 2.54.0 From 895737febc358fd4d7bfc427fb81dcc4eaa1230d Mon Sep 17 00:00:00 2001 From: yohlo Date: Tue, 25 Aug 2026 22:13:41 -0700 Subject: [PATCH 10/33] style: trim comments to load-bearing invariants only --- src/app/routes/api/health.ts | 3 +-- src/app/routes/api/telemetry/beacon.ts | 2 -- src/app/start.ts | 2 -- .../admin/components/dashboard/quiet-boundary.tsx | 1 - src/features/telemetry/server.ts | 4 ---- src/lib/pocketbase/services/telemetry.ts | 7 +------ src/lib/telemetry/api-logging.server.ts | 3 --- src/lib/telemetry/auth-events.server.ts | 4 ---- src/lib/telemetry/client/index.ts | 8 +------- src/lib/telemetry/error-hash.ts | 5 ----- src/lib/telemetry/health.server.ts | 2 -- src/lib/telemetry/request-context.server.ts | 3 --- src/lib/telemetry/rollup-core.ts | 1 - src/lib/telemetry/rollup.server.ts | 4 ---- src/lib/telemetry/scheduler.server.ts | 4 +--- src/utils/activities.ts | 7 +------ src/utils/supertokens.ts | 3 --- 17 files changed, 5 insertions(+), 58 deletions(-) diff --git a/src/app/routes/api/health.ts b/src/app/routes/api/health.ts index 0226fed..7f49fd1 100644 --- a/src/app/routes/api/health.ts +++ b/src/app/routes/api/health.ts @@ -9,8 +9,7 @@ export const Route = createFileRoute("/api/health")({ ensureTelemetryScheduler(); const snapshot = await getHealthSnapshot(); - // Always 200: a PB/SuperTokens outage must not fail the k8s probes; - // restarting the app pod cannot fix a dependency. + // Always 200: a dependency outage must not fail the k8s probes. return new Response( JSON.stringify({ status: snapshot.status, diff --git a/src/app/routes/api/telemetry/beacon.ts b/src/app/routes/api/telemetry/beacon.ts index 38981d8..9161560 100644 --- a/src/app/routes/api/telemetry/beacon.ts +++ b/src/app/routes/api/telemetry/beacon.ts @@ -45,8 +45,6 @@ const writeEvents = async ( const { pbAdmin } = await import("@/lib/pocketbase/client"); await pbAdmin.authPromise; - // Sequential on purpose: SQLite is single-writer; a burst of parallel - // creates just queues behind the lock. for (const event of events) { try { if (event.kind === "error") { diff --git a/src/app/start.ts b/src/app/start.ts index 4e96775..fc3ad01 100644 --- a/src/app/start.ts +++ b/src/app/start.ts @@ -5,8 +5,6 @@ const csrfMiddleware = createCsrfMiddleware({ filter: (ctx) => ctx.handlerType === "serverFn", }); -// Global function middleware: every server fn (current and future) is audited -// without opting in at the definition site. export const startInstance = createStart(() => ({ requestMiddleware: [csrfMiddleware], functionMiddleware: [serverFnLoggingMiddleware], diff --git a/src/features/admin/components/dashboard/quiet-boundary.tsx b/src/features/admin/components/dashboard/quiet-boundary.tsx index d0f6f49..2ab2d33 100644 --- a/src/features/admin/components/dashboard/quiet-boundary.tsx +++ b/src/features/admin/components/dashboard/quiet-boundary.tsx @@ -10,7 +10,6 @@ interface QuietBoundaryState { hasError: boolean; } -// One failing telemetry card must not blank the whole dashboard. export class QuietBoundary extends Component { state: QuietBoundaryState = { hasError: false }; diff --git a/src/features/telemetry/server.ts b/src/features/telemetry/server.ts index 60969bc..f239fa3 100644 --- a/src/features/telemetry/server.ts +++ b/src/features/telemetry/server.ts @@ -99,8 +99,6 @@ export const getClientErrorGroups = createServerFn() const sample = latest.items[0]; if (!sample) continue; - // A group counts as resolved when its most recent occurrence is - // resolved; a recurrence arrives resolved=false and surfaces again. const resolved = !!sample.resolved; if (resolved && !includeResolved) continue; @@ -161,8 +159,6 @@ export const getTelemetryRuntimeStatus = createServerFn() .middleware([superTokensAdminFunctionMiddleware]) .handler(async () => toServerResult(async () => { - // Dev has no k8s probes hitting /api/health, so opening the dashboard - // is the scheduler's fallback starter. ensureTelemetryScheduler(); const health = await getHealthSnapshot(); return { diff --git a/src/lib/pocketbase/services/telemetry.ts b/src/lib/pocketbase/services/telemetry.ts index c952163..a29f900 100644 --- a/src/lib/pocketbase/services/telemetry.ts +++ b/src/lib/pocketbase/services/telemetry.ts @@ -199,8 +199,7 @@ export function createTelemetryService(pb: PocketBase) { }; }, - // Idempotent by the (date, metric, dim) unique index; dim must be "" (never - // null/undefined) or the index stops deduplicating. + // dim must be "" (never null/undefined) or the unique index stops deduplicating. async upsertRollup(input: RollupInput): Promise { const data = { ...input, dim: input.dim ?? "" }; const filter = pbFilter(pb, "date = {:date} && metric = {:metric} && dim = {:dim}", { @@ -277,8 +276,6 @@ export function createTelemetryService(pb: PocketBase) { }; }, - // Bounded flip of a group's rows; new occurrences arrive resolved=false by - // design, so a partial flip on a huge group is acceptable. async resolveErrorsByGroup(groupHash: string, resolved = true): Promise { const MAX_PAGES = 25; let updated = 0; @@ -300,8 +297,6 @@ export function createTelemetryService(pb: PocketBase) { return updated; }, - // Paged scan over a base collection (never SQL views). Stable created sort; - // tight `fields` keeps payloads small for rollup passes. async pageCollection( collection: string, opts: { filter?: string; fields?: string }, diff --git a/src/lib/telemetry/api-logging.server.ts b/src/lib/telemetry/api-logging.server.ts index f3fd774..e9b23cc 100644 --- a/src/lib/telemetry/api-logging.server.ts +++ b/src/lib/telemetry/api-logging.server.ts @@ -2,9 +2,6 @@ import { createMiddleware } from "@tanstack/react-start"; import { recordActivity } from "@/utils/activities"; import { getRequestActor } from "./request-context.server"; -// Request-type counterpart of serverFnLoggingMiddleware for createFileRoute -// API handlers, which the global function middleware never sees. Not for the -// SSE route (a long-lived stream is not a request-shaped unit) or /api/health. export const apiLoggingMiddleware = createMiddleware({ type: "request", }).server(async ({ next, request }) => { diff --git a/src/lib/telemetry/auth-events.server.ts b/src/lib/telemetry/auth-events.server.ts index 53f51de..9623b72 100644 --- a/src/lib/telemetry/auth-events.server.ts +++ b/src/lib/telemetry/auth-events.server.ts @@ -9,8 +9,6 @@ interface AuthEventInput { error?: string; } -// Fire-and-forget: auth flows must never fail or slow down because telemetry -// did. The phone number is used only to resolve the player, never stored. export const recordAuthEvent = (name: string, input: AuthEventInput) => { void (async () => { try { @@ -52,8 +50,6 @@ export const recordAuthEvent = (name: string, input: AuthEventInput) => { type PasswordlessOverride = (originalImplementation: any) => any; -// Composes with a recipe's existing override.functions so telemetry wraps the -// final implementation (Twilio verify included). export const withPasswordlessTelemetry = (existing?: PasswordlessOverride): PasswordlessOverride => (originalImplementation: any) => { diff --git a/src/lib/telemetry/client/index.ts b/src/lib/telemetry/client/index.ts index eebcfc8..21c05b9 100644 --- a/src/lib/telemetry/client/index.ts +++ b/src/lib/telemetry/client/index.ts @@ -1,10 +1,5 @@ import type { AnyRouter } from "@tanstack/react-router"; -// Provider-free by design: renders nothing and mounts no React context, so it -// cannot disturb the LinguiProvider -> AuthProvider order. Browser-only; every -// entry point is a no-op during SSR. Runs in dev too - events go to the same -// PB the app is pointed at, which in dev is the local instance. - type QueuedEvent = Record & { kind: "event" | "error" | "vital" }; const FLUSH_INTERVAL_MS = 10_000; @@ -148,8 +143,7 @@ export const initTelemetry = (router: AnyRouter) => { captureError("unhandledrejection", event.reason); }); - // Vitals mostly report at hidden-visibility; their callbacks are registered - // before the flush listeners so those reports make the final beacon. + // Vitals report at hidden-visibility; register before the flush listeners below. void import("web-vitals") .then(({ onLCP, onCLS, onINP, onFCP, onTTFB }) => { const report = (metric: { name: string; value: number; rating: string }) => { diff --git a/src/lib/telemetry/error-hash.ts b/src/lib/telemetry/error-hash.ts index 7497eec..c6d01da 100644 --- a/src/lib/telemetry/error-hash.ts +++ b/src/lib/telemetry/error-hash.ts @@ -1,6 +1,3 @@ -// Deterministic client-error grouping across deploys: volatile fragments -// (ids, URLs, asset content hashes) are normalized away before hashing. - const normalizeMessage = (message: string): string => message .toLowerCase() @@ -13,8 +10,6 @@ const normalizeMessage = (message: string): string => const FRAME_NOISE = /^(at\s+)?(async\s+)?(module code||eval)/i; -// Vite emits content-hashed asset names (index-Bq2z1a3f.js); strip the hash so -// the same frame groups across deploys. const normalizeFrame = (frame: string): string => frame .trim() diff --git a/src/lib/telemetry/health.server.ts b/src/lib/telemetry/health.server.ts index 79fad37..5fe2816 100644 --- a/src/lib/telemetry/health.server.ts +++ b/src/lib/telemetry/health.server.ts @@ -41,8 +41,6 @@ const runChecks = async (): Promise => { return cached; }; -// Stale-while-revalidate: k8s probes get the cached snapshot instantly while a -// refresh runs in the background; only the very first request awaits a check. export const getHealthSnapshot = async (): Promise => { if (cached && Date.now() - Date.parse(cached.checkedAt) < CHECK_TTL_MS) { return cached; diff --git a/src/lib/telemetry/request-context.server.ts b/src/lib/telemetry/request-context.server.ts index 0c1caeb..85e78c5 100644 --- a/src/lib/telemetry/request-context.server.ts +++ b/src/lib/telemetry/request-context.server.ts @@ -1,6 +1,3 @@ -// Per-request side-channel between the session layer and the global logging -// middleware, which runs outside session middleware and cannot see its context. - const actorByRequest = new WeakMap(); const deniedRecorded = new WeakSet(); diff --git a/src/lib/telemetry/rollup-core.ts b/src/lib/telemetry/rollup-core.ts index af1d5b8..22a91aa 100644 --- a/src/lib/telemetry/rollup-core.ts +++ b/src/lib/telemetry/rollup-core.ts @@ -20,7 +20,6 @@ export interface ClientErrorRow { group_hash: string; } -// Nearest-rank percentile; values need not be pre-sorted. export const percentile = (values: number[], p: number): number => { if (values.length === 0) return 0; const sorted = [...values].sort((a, b) => a - b); diff --git a/src/lib/telemetry/rollup.server.ts b/src/lib/telemetry/rollup.server.ts index f5de7b6..579e1f1 100644 --- a/src/lib/telemetry/rollup.server.ts +++ b/src/lib/telemetry/rollup.server.ts @@ -23,8 +23,6 @@ const addDays = (dateStr: string, days: number): string => { return utcDay(d); }; -// PB stores autodates as "YYYY-MM-DD HH:MM:SS.sssZ" UTC; boundaries in the -// same shape compare correctly in filters. const bound = (dateStr: string): string => `${dateStr} 00:00:00.000Z`; const dayFilter = (dateStr: string): string => @@ -91,8 +89,6 @@ export const runRollups = async (dateStr: string): Promise => { return rollups.length; }; -// The first run after UTC midnight recomputes yesterday once more so its -// partial rows become final. Idempotent via the (date, metric, dim) key. export const runScheduledRollups = async (lastRunDay?: string): Promise => { const today = utcDay(new Date()); if (lastRunDay && lastRunDay !== today) { diff --git a/src/lib/telemetry/scheduler.server.ts b/src/lib/telemetry/scheduler.server.ts index 57649ba..b19cd57 100644 --- a/src/lib/telemetry/scheduler.server.ts +++ b/src/lib/telemetry/scheduler.server.ts @@ -52,9 +52,7 @@ const tickAlerts = async () => { } }; -// Single-replica deployment makes in-process intervals a valid scheduler -// (same precedent as the badge migration job). Started lazily from request -// handlers (health probes guarantee a call within seconds of boot). +// In-process intervals are valid only while the deployment runs a single replica. export const ensureTelemetryScheduler = () => { if (initialized || typeof window !== "undefined") return; initialized = true; diff --git a/src/utils/activities.ts b/src/utils/activities.ts index 5e17fe5..5bc92c5 100644 --- a/src/utils/activities.ts +++ b/src/utils/activities.ts @@ -41,8 +41,6 @@ const serverFnName = (request: Request, meta?: ServerFnMeta): string => { return pathParts[pathParts.length - 1] || "unknown"; }; -// Successful reads are skipped (client page views cover usage); read failures -// are always recorded. Naming is uniform across every feature server.ts. export const READ_FN_NAME = /^(get|list|search|fetch|preview|is[A-Z])/; const readSampleRate = (): number => { @@ -56,8 +54,6 @@ const shouldRecordSuccess = (name: string): boolean => { return sample > 0 && Math.random() < sample; }; -// Thrown Responses/redirects are control flow (session refresh 401s, route -// redirects), not failures. const isControlFlowError = (error: unknown): boolean => error instanceof Response || isRedirect(error); @@ -86,8 +82,7 @@ export const serverFnLoggingMiddleware = createMiddleware({ const userAgent = request.headers.get("user-agent") || undefined; const safeArgs = redactArguments(data); - // Registered globally, so session middleware runs inside this one; the actor - // is stashed on the Request by getSessionContextCore and read after next(). + // Runs outside session middleware; the actor is stashed on the Request and read after next(). const resolveActor = (): string | undefined => getRequestActor(request)?.playerId ?? ((context as any)?.metadata?.player_id as string | undefined); diff --git a/src/utils/supertokens.ts b/src/utils/supertokens.ts index 7ebe89a..d3b0ece 100644 --- a/src/utils/supertokens.ts +++ b/src/utils/supertokens.ts @@ -26,9 +26,6 @@ export const superTokensRequestMiddleware = createMiddleware({ return next({ context }); }); -// Session context when available, anonymous otherwise — never throws or -// redirects. For endpoints that must accept logged-out traffic (telemetry -// beacon: login-page errors are exactly the signal we want). export const superTokensOptionalRequestMiddleware = createMiddleware({ type: "request", }).server(async ({ next, request }) => { -- 2.54.0 From 1cc75591fa43055d0ebc1cad5d74f5c2b468438d Mon Sep 17 00:00:00 2001 From: yohlo Date: Tue, 25 Aug 2026 22:24:36 -0700 Subject: [PATCH 11/33] refactor(admin): align admin ui with app design language - dashboard: card grid replaced by status strip + nav-tile section + list - nav-tile promoted to shared component - stat tiles and usage screen de-carded, sections divided like settings - hub run tournament back to a list row - telemetry link restored in tools list --- src/app/routes/_authed/admin/index.tsx | 7 +- src/components/nav-tile/index.tsx | 28 ++++ src/components/nav-tile/nav-tile.module.css | 43 ++++++ .../components/dashboard/activity-feed.tsx | 87 ------------ .../components/dashboard/errors-card.tsx | 62 -------- .../admin/components/dashboard/index.tsx | 88 ++++++------ .../admin/components/dashboard/now-strip.tsx | 61 -------- .../components/dashboard/quiet-boundary.tsx | 10 +- .../components/dashboard/section-box.tsx | 14 ++ .../components/dashboard/status-strip.tsx | 74 ++++++++++ .../admin/components/dashboard/tools-list.tsx | 6 + .../components/dashboard/tournament-card.tsx | 76 ---------- .../dashboard/tournament-section.tsx | 48 +++++++ .../admin/components/dashboard/usage-card.tsx | 55 -------- .../telemetry/components/charts/stat-tile.tsx | 24 ++-- .../telemetry/components/usage-screen.tsx | 49 +++---- .../components/manage-tournament.tsx | 23 +-- .../started-tournament/sections/nav-grid.tsx | 23 +-- src/locales/de/messages.po | 133 +++++++----------- src/locales/en/messages.po | 133 +++++++----------- src/locales/es/messages.po | 133 +++++++----------- src/locales/ja/messages.po | 133 +++++++----------- 22 files changed, 511 insertions(+), 799 deletions(-) create mode 100644 src/components/nav-tile/index.tsx create mode 100644 src/components/nav-tile/nav-tile.module.css delete mode 100644 src/features/admin/components/dashboard/activity-feed.tsx delete mode 100644 src/features/admin/components/dashboard/errors-card.tsx delete mode 100644 src/features/admin/components/dashboard/now-strip.tsx create mode 100644 src/features/admin/components/dashboard/section-box.tsx create mode 100644 src/features/admin/components/dashboard/status-strip.tsx delete mode 100644 src/features/admin/components/dashboard/tournament-card.tsx create mode 100644 src/features/admin/components/dashboard/tournament-section.tsx delete mode 100644 src/features/admin/components/dashboard/usage-card.tsx diff --git a/src/app/routes/_authed/admin/index.tsx b/src/app/routes/_authed/admin/index.tsx index a36a1db..ec660c1 100644 --- a/src/app/routes/_authed/admin/index.tsx +++ b/src/app/routes/_authed/admin/index.tsx @@ -3,7 +3,7 @@ import { msg } from "@lingui/core/macro"; import { AdminDashboard } from "@/features/admin"; import { tournamentQueries } from "@/features/tournaments/queries"; import { telemetryQueries } from "@/features/telemetry/queries"; -import { lastNDays } from "@/features/telemetry/range"; +import { isoDay, lastNDays } from "@/features/telemetry/range"; import { prefetchServerQuery } from "@/lib/tanstack-query/utils/prefetch"; export const Route = createFileRoute("/_authed/admin/")({ @@ -15,6 +15,11 @@ export const Route = createFileRoute("/_authed/admin/")({ context.queryClient, telemetryQueries.errorGroups({ from: month.from, to: month.to }) ); + const today = isoDay(new Date()); + prefetchServerQuery( + context.queryClient, + telemetryQueries.rollups({ metrics: ["server_fn.count"], from: today, to: today }) + ); }, loader: () => ({ header: { diff --git a/src/components/nav-tile/index.tsx b/src/components/nav-tile/index.tsx new file mode 100644 index 0000000..221bf24 --- /dev/null +++ b/src/components/nav-tile/index.tsx @@ -0,0 +1,28 @@ +import { Text, UnstyledButton } from "@mantine/core"; +import { Icon } from "@phosphor-icons/react"; +import classes from "./nav-tile.module.css"; + +interface NavTileProps { + label: string; + Icon: Icon; + onClick?: () => void; + disabled?: boolean; +} + +/** A single square-ish nav tile with icon-over-label and press feedback. */ +export const NavTile = ({ label, Icon, onClick, disabled }: NavTileProps) => ( + + + + + + {label} + + +); + +export default NavTile; diff --git a/src/components/nav-tile/nav-tile.module.css b/src/components/nav-tile/nav-tile.module.css new file mode 100644 index 0000000..028dfed --- /dev/null +++ b/src/components/nav-tile/nav-tile.module.css @@ -0,0 +1,43 @@ +.tile { + -webkit-tap-highlight-color: transparent; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 8px; + padding: 14px 8px; + border-radius: var(--mantine-radius-md); + border: 1px solid var(--mantine-color-default-border); + background-color: var(--mantine-color-body); + text-align: center; +} + +.icon { + color: var(--mantine-primary-color-filled); + display: flex; +} + +@media (hover: hover) { + .tile:hover:not(:disabled) { + background-color: var(--mantine-color-default-hover); + } +} + +.tile:active:not(:disabled) { + background-color: var(--mantine-color-default-hover); +} + +.tile:disabled { + opacity: 0.5; + cursor: default; +} + +@media (prefers-reduced-motion: no-preference) { + .tile { + transition: background-color 120ms ease-out, transform 120ms cubic-bezier(0.32, 0.72, 0, 1); + } + + .tile:active:not(:disabled) { + transform: scale(0.97); + } +} diff --git a/src/features/admin/components/dashboard/activity-feed.tsx b/src/features/admin/components/dashboard/activity-feed.tsx deleted file mode 100644 index 340e9c7..0000000 --- a/src/features/admin/components/dashboard/activity-feed.tsx +++ /dev/null @@ -1,87 +0,0 @@ -import { Card, Group, Stack, Text } from "@mantine/core"; -import { Trans } from "@lingui/react/macro"; -import { useNavigate } from "@tanstack/react-router"; -import { CheckCircleIcon, XCircleIcon } from "@phosphor-icons/react"; -import { useServerQuery } from "@/lib/tanstack-query/hooks"; -import { searchActivities } from "@/features/activities/server"; -import { activityKeys } from "@/features/activities/queries"; -import { RelativeTime } from "@/features/telemetry/components/shared"; -import type { PlayerInfo } from "@/features/players/types"; - -const FEED_PARAMS = { perPage: 10 } as const; - -const playerName = (player?: string | PlayerInfo): string | null => { - if (!player || typeof player === "string") return null; - return [player.first_name, player.last_name].filter(Boolean).join(" ") || null; -}; - -export const ActivityFeed = () => { - const navigate = useNavigate(); - - const { data } = useServerQuery({ - queryKey: activityKeys.search(FEED_PARAMS), - queryFn: () => searchActivities({ data: FEED_PARAMS }), - options: { - refetchInterval: 30_000, - }, - }); - - return ( - - - - - Recent activity - - - - {data?.items.map((activity) => { - const name = playerName(activity.player); - return ( - navigate({ to: "/admin/telemetry/functions" })} - style={{ cursor: "pointer" }} - > - {activity.success ? ( - - ) : ( - - )} - - {activity.name} - {name && ( - - {" "} - · {name} - - )} - - - - - - ); - })} - {data && data.items.length === 0 && ( - - No activity yet - - )} - - - - ); -}; - -export default ActivityFeed; diff --git a/src/features/admin/components/dashboard/errors-card.tsx b/src/features/admin/components/dashboard/errors-card.tsx deleted file mode 100644 index 64417f9..0000000 --- a/src/features/admin/components/dashboard/errors-card.tsx +++ /dev/null @@ -1,62 +0,0 @@ -import { Badge, Card, Group, Stack, Text, UnstyledButton } from "@mantine/core"; -import { Trans } from "@lingui/react/macro"; -import { useNavigate } from "@tanstack/react-router"; -import { CheckCircleIcon, WarningCircleIcon } from "@phosphor-icons/react"; -import { useClientErrorGroups } from "@/features/telemetry/queries"; -import { RelativeTime } from "@/features/telemetry/components/shared"; -import { lastNDays } from "@/features/telemetry/range"; - -export const ErrorsCard = () => { - const navigate = useNavigate(); - const range = lastNDays(30); - - const { data: groups } = useClientErrorGroups({ from: range.from, to: range.to }); - - return ( - navigate({ to: "/admin/telemetry/errors" })}> - - - - - Client errors - - {groups && groups.length > 0 ? ( - - {groups.length} - - ) : ( - - )} - - {groups && groups.length > 0 ? ( - - {groups.slice(0, 3).map((group) => ( - - - - - {group.sample.message} - - - - - - - ))} - - ) : ( - - No unresolved errors - - )} - - - - ); -}; - -export default ErrorsCard; diff --git a/src/features/admin/components/dashboard/index.tsx b/src/features/admin/components/dashboard/index.tsx index 080896b..4c3f3e0 100644 --- a/src/features/admin/components/dashboard/index.tsx +++ b/src/features/admin/components/dashboard/index.tsx @@ -1,57 +1,51 @@ import { Suspense } from "react"; -import { Box, SimpleGrid, Stack } from "@mantine/core"; -import { CardSkeleton, StatTileSkeleton } from "@/components/skeletons"; +import { Group, SimpleGrid, Skeleton, Stack } from "@mantine/core"; import QuietBoundary from "./quiet-boundary"; -import NowStrip from "./now-strip"; -import TournamentCard from "./tournament-card"; -import UsageCard from "./usage-card"; -import ErrorsCard from "./errors-card"; -import ActivityFeed from "./activity-feed"; +import SectionBox from "./section-box"; +import StatusStrip from "./status-strip"; +import TournamentSection from "./tournament-section"; import ToolsList from "./tools-list"; -const NowStripSkeleton = () => ( - - - - - +const StripSkeleton = () => ( + + + {[0, 1, 2].map((i) => ( + + + + + ))} + + +); + +const TournamentSkeleton = () => ( + + + + + + + + ); export const AdminDashboard = () => ( - - - - }> - - - - - - }> - - - - - }> - - - - - - - }> - - - - - - - - - - - - + <> + + }> + + + + + + + }> + + + + + ); export default AdminDashboard; diff --git a/src/features/admin/components/dashboard/now-strip.tsx b/src/features/admin/components/dashboard/now-strip.tsx deleted file mode 100644 index b2b5a93..0000000 --- a/src/features/admin/components/dashboard/now-strip.tsx +++ /dev/null @@ -1,61 +0,0 @@ -import { useMemo } from "react"; -import { SimpleGrid } from "@mantine/core"; -import { useLingui } from "@lingui/react/macro"; -import { useNavigate } from "@tanstack/react-router"; -import { - useClientErrorGroups, - useRollupRange, - useTelemetryRuntimeStatus, -} from "@/features/telemetry/queries"; -import { StatTile } from "@/features/telemetry/components/charts"; -import { isoDay, lastNDays, listDays } from "@/features/telemetry/range"; - -export const NowStrip = () => { - const { t } = useLingui(); - const navigate = useNavigate(); - const range = useMemo(() => lastNDays(7), []); - - const { data: status } = useTelemetryRuntimeStatus(); - const { data: errorGroups } = useClientErrorGroups({ - from: lastNDays(30).from, - to: range.to, - }); - const { data: rollups } = useRollupRange({ - metrics: ["server_fn.count"], - from: range.from, - to: range.to, - }); - - const { callsToday, callsSeries } = useMemo(() => { - const byDate = new Map(); - for (const row of rollups ?? []) { - byDate.set(row.date, (byDate.get(row.date) ?? 0) + row.value); - } - return { - callsToday: byDate.get(isoDay(new Date())) ?? 0, - callsSeries: listDays(range).map((day) => byDate.get(day) ?? 0), - }; - }, [rollups, range]); - - const unresolved = errorGroups?.length ?? 0; - - return ( - - - navigate({ to: "/admin/telemetry/errors" })} - /> - navigate({ to: "/admin/telemetry/usage" })} - /> - - ); -}; - -export default NowStrip; diff --git a/src/features/admin/components/dashboard/quiet-boundary.tsx b/src/features/admin/components/dashboard/quiet-boundary.tsx index 2ab2d33..11f7813 100644 --- a/src/features/admin/components/dashboard/quiet-boundary.tsx +++ b/src/features/admin/components/dashboard/quiet-boundary.tsx @@ -1,5 +1,5 @@ import { Component, ReactNode } from "react"; -import { Card, Text } from "@mantine/core"; +import { Text } from "@mantine/core"; import { Trans } from "@lingui/react/macro"; interface QuietBoundaryProps { @@ -20,11 +20,9 @@ export class QuietBoundary extends Component - - Unavailable right now - - + + Unavailable right now + ); } return this.props.children; diff --git a/src/features/admin/components/dashboard/section-box.tsx b/src/features/admin/components/dashboard/section-box.tsx new file mode 100644 index 0000000..6ac0059 --- /dev/null +++ b/src/features/admin/components/dashboard/section-box.tsx @@ -0,0 +1,14 @@ +import { ReactNode } from "react"; +import { Box } from "@mantine/core"; + +export const SectionBox = ({ children }: { children: ReactNode }) => ( + + {children} + +); + +export default SectionBox; diff --git a/src/features/admin/components/dashboard/status-strip.tsx b/src/features/admin/components/dashboard/status-strip.tsx new file mode 100644 index 0000000..1c7e13c --- /dev/null +++ b/src/features/admin/components/dashboard/status-strip.tsx @@ -0,0 +1,74 @@ +import { useMemo } from "react"; +import { Group, Stack, Text, UnstyledButton } from "@mantine/core"; +import { useLingui } from "@lingui/react/macro"; +import { useNavigate } from "@tanstack/react-router"; +import { + useClientErrorGroups, + useRollupRange, + useTelemetryRuntimeStatus, +} from "@/features/telemetry/queries"; +import { isoDay, lastNDays } from "@/features/telemetry/range"; + +interface StatProps { + value: string | number; + label: string; + alert?: boolean; + onClick: () => void; +} + +const Stat = ({ value, label, alert, onClick }: StatProps) => ( + + + + {value} + + + {label} + + + +); + +export const StatusStrip = () => { + const { t } = useLingui(); + const navigate = useNavigate(); + const today = useMemo(() => isoDay(new Date()), []); + const month = useMemo(() => lastNDays(30), []); + + const { data: status } = useTelemetryRuntimeStatus(); + const { data: errorGroups } = useClientErrorGroups({ from: month.from, to: month.to }); + const { data: rollups } = useRollupRange({ + metrics: ["server_fn.count"], + from: today, + to: today, + }); + + const callsToday = useMemo( + () => (rollups ?? []).reduce((sum, row) => sum + row.value, 0), + [rollups] + ); + const openErrors = errorGroups?.length ?? 0; + + return ( + + navigate({ to: "/admin/telemetry/players" })} + /> + 0} + onClick={() => navigate({ to: "/admin/telemetry/errors" })} + /> + navigate({ to: "/admin/telemetry/usage" })} + /> + + ); +}; + +export default StatusStrip; diff --git a/src/features/admin/components/dashboard/tools-list.tsx b/src/features/admin/components/dashboard/tools-list.tsx index 318d407..c87b32b 100644 --- a/src/features/admin/components/dashboard/tools-list.tsx +++ b/src/features/admin/components/dashboard/tools-list.tsx @@ -6,6 +6,7 @@ import { TrophyIcon, MedalIcon, CrownIcon, + PulseIcon, } from "@phosphor-icons/react"; import ListLink from "@/components/list-link"; import ListButton from "@/components/list-button"; @@ -50,6 +51,11 @@ export const ToolsList = () => { Icon={TrophyIcon} to="/admin/tournaments" /> + { - const { t } = useLingui(); - const navigate = useNavigate(); - const { data: tournament } = useCurrentTournament(); - - if (!tournament) { - return ( - - - - No tournaments yet - - - - - ); - } - - const upcoming = !!tournament.start_time && Date.parse(tournament.start_time) > Date.now(); - - return ( - - - - - - {tournament.name} - - {upcoming && ( - - )} - - - - - - - - - - - ); -}; - -export default TournamentCard; diff --git a/src/features/admin/components/dashboard/tournament-section.tsx b/src/features/admin/components/dashboard/tournament-section.tsx new file mode 100644 index 0000000..a63d7d6 --- /dev/null +++ b/src/features/admin/components/dashboard/tournament-section.tsx @@ -0,0 +1,48 @@ +import { SimpleGrid, Text, Title } from "@mantine/core"; +import { useLingui } from "@lingui/react/macro"; +import { useNavigate } from "@tanstack/react-router"; +import { GearIcon, PlayIcon, UsersThreeIcon } from "@phosphor-icons/react"; +import { useCurrentTournament } from "@/features/tournaments/queries"; +import { NavTile } from "@/components/nav-tile"; +import { Countdown } from "@/components/countdown"; +import SectionBox from "./section-box"; + +export const TournamentSection = () => { + const { t } = useLingui(); + const navigate = useNavigate(); + const { data: tournament } = useCurrentTournament(); + + if (!tournament) return null; + + const upcoming = !!tournament.start_time && Date.parse(tournament.start_time) > Date.now(); + + return ( + + {tournament.name} + {upcoming && ( + + + + )} + + navigate({ to: `/admin/tournaments/run/${tournament.id}` })} + /> + navigate({ to: `/admin/tournaments/${tournament.id}/enrollment` })} + /> + navigate({ to: `/admin/tournaments/${tournament.id}` })} + /> + + + ); +}; + +export default TournamentSection; diff --git a/src/features/admin/components/dashboard/usage-card.tsx b/src/features/admin/components/dashboard/usage-card.tsx deleted file mode 100644 index 56a3dd3..0000000 --- a/src/features/admin/components/dashboard/usage-card.tsx +++ /dev/null @@ -1,55 +0,0 @@ -import { useMemo } from "react"; -import { Card, Group, Stack, Text, UnstyledButton } from "@mantine/core"; -import { Trans, useLingui } from "@lingui/react/macro"; -import { useNavigate } from "@tanstack/react-router"; -import { useRollupRange } from "@/features/telemetry/queries"; -import { Sparkline } from "@/features/telemetry/components/charts"; -import { fillDailySeries, lastNDays, listDays } from "@/features/telemetry/range"; - -export const UsageCard = () => { - const { t } = useLingui(); - const navigate = useNavigate(); - const range = useMemo(() => lastNDays(7), []); - - const { data: rollups } = useRollupRange({ - metrics: ["dau", "page_view.count"], - from: range.from, - to: range.to, - }); - - const { dauSeries, pageViewSeries } = useMemo(() => { - const days = listDays(range); - return { - dauSeries: fillDailySeries(rollups ?? [], "dau", "", days), - pageViewSeries: fillDailySeries(rollups ?? [], "page_view.count", "", days), - }; - }, [rollups, range]); - - return ( - navigate({ to: "/admin/telemetry/usage" })}> - - - - - Usage, 7 days - - - - - Active players - - - - - - Page views - - - - - - - ); -}; - -export default UsageCard; diff --git a/src/features/telemetry/components/charts/stat-tile.tsx b/src/features/telemetry/components/charts/stat-tile.tsx index 541dde4..d0e22c2 100644 --- a/src/features/telemetry/components/charts/stat-tile.tsx +++ b/src/features/telemetry/components/charts/stat-tile.tsx @@ -1,5 +1,5 @@ import { ReactNode } from "react"; -import { Box, Card, Group, Stack, Text, UnstyledButton } from "@mantine/core"; +import { Box, Group, Stack, Text, UnstyledButton } from "@mantine/core"; import Sparkline from "./sparkline"; interface StatTileProps { @@ -13,12 +13,9 @@ interface StatTileProps { export const StatTile = ({ label, value, delta, spark, caption, onClick }: StatTileProps) => { const content = ( - - - {label} - + - + {value} {delta !== undefined && delta !== 0 && ( @@ -28,6 +25,9 @@ export const StatTile = ({ label, value, delta, spark, caption, onClick }: StatT )} + + {label} + {caption && {caption}} {spark && spark.length > 0 && } @@ -35,19 +35,13 @@ export const StatTile = ({ label, value, delta, spark, caption, onClick }: StatT if (onClick) { return ( - - - {content} - + + {content} ); } - return ( - - {content} - - ); + return content; }; export default StatTile; diff --git a/src/features/telemetry/components/usage-screen.tsx b/src/features/telemetry/components/usage-screen.tsx index 3448453..cd9bd42 100644 --- a/src/features/telemetry/components/usage-screen.tsx +++ b/src/features/telemetry/components/usage-screen.tsx @@ -2,7 +2,7 @@ import { Suspense, useMemo, useState } from "react"; import { Trans, useLingui } from "@lingui/react/macro"; import { Badge, - Card, + Divider, Group, SegmentedControl, SimpleGrid, @@ -10,7 +10,7 @@ import { Text, Title, } from "@mantine/core"; -import { CardSkeleton, StatTileSkeleton } from "@/components/skeletons"; +import { ListSkeleton, StatTileSkeleton } from "@/components/skeletons"; import { useRollupRange } from "../queries"; import { fillDailySeries, lastNDays, latestValue, listDays, sumByDim } from "../range"; import { BarList, Sparkline, StatTile } from "./charts"; @@ -171,30 +171,30 @@ const UsageSections = ({ from, to }: { from: string; to: string }) => { /> Sessions} value={sessions.toLocaleString()} /> - - - - Page views by route - - - - + + + Page views by route + + + + + <Trans>Server functions</Trans> - - - - Top functions by call count - - - - + + + Top functions by call count + + + + + <Trans>Errors</Trans> @@ -206,6 +206,8 @@ const UsageSections = ({ from, to }: { from: string; to: string }) => { /> </Stack> + <Divider /> + <Stack gap="sm"> <Title order={4}> <Trans>Vitals</Trans> @@ -261,14 +263,13 @@ export const UsageScreen = () => { <Suspense fallback={ <Stack gap="lg"> - <StatTileSkeleton withCard align="row" /> + <StatTileSkeleton align="row" /> <SimpleGrid cols={3} spacing="sm"> - <StatTileSkeleton withCard /> - <StatTileSkeleton withCard /> - <StatTileSkeleton withCard /> + <StatTileSkeleton /> + <StatTileSkeleton /> + <StatTileSkeleton /> </SimpleGrid> - <CardSkeleton withAvatar={false} withBadge={false} /> - <CardSkeleton withAvatar={false} withBadge={false} /> + <ListSkeleton rows={6} withTrailing="text" /> </Stack> } > diff --git a/src/features/tournaments/components/manage-tournament.tsx b/src/features/tournaments/components/manage-tournament.tsx index a0ce96e..9e9a8c9 100644 --- a/src/features/tournaments/components/manage-tournament.tsx +++ b/src/features/tournaments/components/manage-tournament.tsx @@ -1,5 +1,5 @@ import { useTournament } from "../queries"; -import { Box, List } from "@mantine/core"; +import { List } from "@mantine/core"; import ListButton from "@/components/list-button"; import Sheet from "@/components/sheet/sheet"; import TournamentForm from "./tournament-form"; @@ -12,9 +12,7 @@ import { import { useSheet } from "@/hooks/use-sheet"; import ListLink from "@/components/list-link"; import EditRules from "./edit-rules"; -import Button from "@/components/button"; -import { Trans, useLingui } from "@lingui/react/macro"; -import { useNavigate } from "@tanstack/react-router"; +import { useLingui } from "@lingui/react/macro"; interface ManageTournamentProps { tournamentId: string; @@ -22,7 +20,6 @@ interface ManageTournamentProps { const ManageTournament = ({ tournamentId }: ManageTournamentProps) => { const { t } = useLingui(); - const navigate = useNavigate(); const { data: tournament } = useTournament(tournamentId); if (!tournament) throw new Error("Tournament not found."); @@ -39,18 +36,12 @@ const ManageTournament = ({ tournamentId }: ManageTournamentProps) => { return ( <> - <Box px="md" py="md"> - <Button - leftSection={<TreeStructureIcon weight="bold" size={20} />} - onClick={() => - navigate({ to: `/admin/tournaments/run/${tournamentId}` }) - } - > - <Trans>Run Tournament</Trans> - </Button> - </Box> - <List p="0"> + <ListLink + label={t`Run Tournament`} + Icon={TreeStructureIcon} + to={`/admin/tournaments/run/${tournamentId}`} + /> <ListLink label={t`Enrollment & Teams`} Icon={UsersThreeIcon} diff --git a/src/features/tournaments/components/started-tournament/sections/nav-grid.tsx b/src/features/tournaments/components/started-tournament/sections/nav-grid.tsx index 432400f..a98ece4 100644 --- a/src/features/tournaments/components/started-tournament/sections/nav-grid.tsx +++ b/src/features/tournaments/components/started-tournament/sections/nav-grid.tsx @@ -30,28 +30,9 @@ const RulesContent = lazy( () => import("../../upcoming-tournament/rules-content") ); -interface NavTileProps { - label: string; - Icon: Icon; - onClick?: () => void; - disabled?: boolean; -} +import { NavTile } from "@/components/nav-tile"; -/** A single square-ish nav tile with icon-over-label and press feedback. */ -export const NavTile = ({ label, Icon, onClick, disabled }: NavTileProps) => ( - <UnstyledButton - className={`${classes.tile} flxn-tile`} - onClick={onClick} - disabled={disabled} - > - <span className={`${classes.icon} flxn-tile-icon`}> - <Icon size={22} weight="bold" /> - </span> - <Text size="xs" fw={600} lineClamp={2} lh={1.2}> - {label} - </Text> - </UnstyledButton> -); +export { NavTile }; interface NavGridProps { tournament: Tournament; diff --git a/src/locales/de/messages.po b/src/locales/de/messages.po index 8e97ad2..398e94d 100644 --- a/src/locales/de/messages.po +++ b/src/locales/de/messages.po @@ -153,7 +153,7 @@ msgstr "{0}ms" msgid "{count, plural, one {Players (#)} other {Players (#)}}" msgstr "{count, plural, one {Spieler (#)} other {Spieler (#)}}" -#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:134 +#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:115 #: src/features/tournaments/components/upcoming-tournament/team-list-button.tsx:24 msgid "{count, plural, one {Teams (#)} other {Teams (#)}}" msgstr "{count, plural, one {Teams (#)} other {Teams (#)}}" @@ -355,7 +355,7 @@ msgstr "2. Frühere Abgabe" msgid "2nd place" msgstr "2. Platz" -#: src/features/telemetry/components/usage-screen.tsx:256 +#: src/features/telemetry/components/usage-screen.tsx:258 msgid "30d" msgstr "30 T" @@ -371,7 +371,7 @@ msgstr "3. Platz" msgid "5s" msgstr "5 s" -#: src/features/telemetry/components/usage-screen.tsx:255 +#: src/features/telemetry/components/usage-screen.tsx:257 msgid "7d" msgstr "7 T" @@ -420,10 +420,6 @@ msgstr "Konto erfolgreich erstellt!" msgid "Active device: {0}" msgstr "Aktives Gerät: {0}" -#: src/features/admin/components/dashboard/usage-card.tsx:39 -msgid "Active players" -msgstr "Aktive Spieler" - #: src/features/activities/components/activities-table.tsx:159 msgid "Activity Details" msgstr "Aktivitätsdetails" @@ -441,7 +437,7 @@ msgid "Add Team" msgstr "Team hinzufügen" #: src/app/routes/_authed/admin.tsx:15 -#: src/app/routes/_authed/admin/index.tsx:22 +#: src/app/routes/_authed/admin/index.tsx:27 #: src/features/core/hooks/use-links.ts:37 msgid "Admin" msgstr "Admin" @@ -564,7 +560,7 @@ msgstr "Ø Sieg-Marge" msgid "Award Badge" msgstr "Abzeichen verleihen" -#: src/features/admin/components/dashboard/tools-list.tsx:54 +#: src/features/admin/components/dashboard/tools-list.tsx:60 msgid "Award Badges" msgstr "Abzeichen verleihen" @@ -598,15 +594,15 @@ msgstr "Zurück" msgid "Badge awarded to {playerName}" msgstr "Abzeichen an {playerName} verliehen" -#: src/features/admin/components/dashboard/tools-list.tsx:85 +#: src/features/admin/components/dashboard/tools-list.tsx:91 msgid "Badge recalculation failed. Check the server logs." msgstr "Abzeichen-Neuberechnung fehlgeschlagen. Prüfe die Server-Logs." -#: src/features/admin/components/dashboard/tools-list.tsx:38 +#: src/features/admin/components/dashboard/tools-list.tsx:39 msgid "Badge recalculation is already running" msgstr "Abzeichen-Neuberechnung läuft bereits" -#: src/features/admin/components/dashboard/tools-list.tsx:37 +#: src/features/admin/components/dashboard/tools-list.tsx:38 msgid "Badge recalculation started in the background" msgstr "Abzeichen-Neuberechnung im Hintergrund gestartet" @@ -631,13 +627,13 @@ msgstr "Zirkus" msgid "Both teams cannot have 10 cups" msgstr "Beide Teams können nicht 10 Becher haben" -#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:106 +#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:87 #: src/features/tournaments/components/upcoming-tournament/index.tsx:151 msgid "Bracket" msgstr "Turnierbaum" #: src/app/routes/_authed/admin/preview.tsx:13 -#: src/features/admin/components/dashboard/tools-list.tsx:96 +#: src/features/admin/components/dashboard/tools-list.tsx:102 msgid "Bracket Preview" msgstr "Turnierbaum-Vorschau" @@ -654,7 +650,7 @@ msgstr "Brutal" msgid "CA" msgstr "CA" -#: src/features/admin/components/dashboard/now-strip.tsx:52 +#: src/features/admin/components/dashboard/status-strip.tsx:67 msgid "Calls today" msgstr "Aufrufe heute" @@ -720,16 +716,12 @@ msgstr "Ausgewählten Spieler entfernen" msgid "Client error spike" msgstr "Anstieg von Client-Fehlern" -#: src/features/admin/components/dashboard/errors-card.tsx:21 -msgid "Client errors" -msgstr "Client-Fehler" - #: src/features/telemetry/components/usage-screen.tsx:203 msgid "Client errors per day" msgstr "Client-Fehler pro Tag" #: src/components/sheet/modal.tsx:35 -#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:169 +#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:150 #: src/features/tournaments/components/upcoming-tournament/rules-list-button.tsx:36 msgid "Close" msgstr "Schließen" @@ -883,15 +875,10 @@ msgstr "Becher erzielt" msgid "Custom Hex Code" msgstr "Benutzerdefinierter Hex-Code" -#: src/features/admin/components/dashboard/usage-card.tsx:41 #: src/features/telemetry/components/usage-screen.tsx:159 msgid "Daily active players" msgstr "Täglich aktive Spieler" -#: src/features/admin/components/dashboard/usage-card.tsx:47 -msgid "Daily page views" -msgstr "Tägliche Seitenaufrufe" - #: src/features/settings/components/color-scheme-picker.tsx:39 msgid "Dark" msgstr "Dunkel" @@ -947,7 +934,7 @@ msgstr "Nachfolgende Matches wurden zurückgesetzt, da sich durch die Korrektur msgid "Duration" msgstr "Dauer" -#: src/features/telemetry/components/usage-screen.tsx:233 +#: src/features/telemetry/components/usage-screen.tsx:235 msgid "Duration p50 / p95" msgstr "Dauer p50 / p95" @@ -970,12 +957,12 @@ msgstr "Match bearbeiten" msgid "Edit match score" msgstr "Spielstand bearbeiten" -#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:114 +#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:95 msgid "Edit Prediction" msgstr "Tipp bearbeiten" -#: src/features/tournaments/components/manage-tournament.tsx:65 -#: src/features/tournaments/components/manage-tournament.tsx:92 +#: src/features/tournaments/components/manage-tournament.tsx:56 +#: src/features/tournaments/components/manage-tournament.tsx:83 msgid "Edit Rules" msgstr "Regeln bearbeiten" @@ -984,8 +971,8 @@ msgstr "Regeln bearbeiten" msgid "Edit Team" msgstr "Team bearbeiten" -#: src/features/tournaments/components/manage-tournament.tsx:60 -#: src/features/tournaments/components/manage-tournament.tsx:72 +#: src/features/tournaments/components/manage-tournament.tsx:51 +#: src/features/tournaments/components/manage-tournament.tsx:63 msgid "Edit Tournament" msgstr "Turnier bearbeiten" @@ -1048,7 +1035,7 @@ msgstr "Angemeldete Spieler" #: src/features/tournaments/components/enrollment/enrollment-stats.tsx:28 #: src/features/tournaments/components/enrollment/team-section.tsx:240 -#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:145 +#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:126 #: src/features/tournaments/components/upcoming-tournament/team-list-button.tsx:31 msgid "Enrolled Teams" msgstr "Angemeldete Teams" @@ -1058,13 +1045,13 @@ msgid "Enrolling as a free agent adds you to a pool of players looking for teamm msgstr "Als Free Agent kommst du in einen Pool von Spielern, die noch einen Teampartner suchen." #: src/app/routes/_authed/admin/tournaments/$id/enrollment.tsx:33 -#: src/features/admin/components/dashboard/tournament-card.tsx:61 +#: src/features/admin/components/dashboard/tournament-section.tsx:34 #: src/features/tournaments/components/upcoming-tournament/enroll-free-agent.tsx:29 #: src/features/tournaments/components/upcoming-tournament/index.tsx:69 msgid "Enrollment" msgstr "Anmeldung" -#: src/features/tournaments/components/manage-tournament.tsx:55 +#: src/features/tournaments/components/manage-tournament.tsx:46 msgid "Enrollment & Teams" msgstr "Anmeldungen & Teams" @@ -1181,7 +1168,7 @@ msgstr "Aktives Gerät konnte nicht festgelegt werden" msgid "Failed to set volume" msgstr "Lautstärke konnte nicht festgelegt werden" -#: src/features/admin/components/dashboard/tools-list.tsx:41 +#: src/features/admin/components/dashboard/tools-list.tsx:42 msgid "Failed to start badge recalculation" msgstr "Abzeichen-Neuberechnung konnte nicht gestartet werden" @@ -1251,7 +1238,7 @@ msgstr "Free-Agent-Anmeldung" msgid "Free Agents" msgstr "Free Agents" -#: src/features/telemetry/components/usage-screen.tsx:226 +#: src/features/telemetry/components/usage-screen.tsx:228 msgid "Function" msgstr "Funktion" @@ -1319,7 +1306,7 @@ msgstr "Gruppenphase erfolgreich erstellt!" msgid "Group Stage Setup" msgstr "Gruppenphase einrichten" -#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:98 +#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:79 msgid "Groups" msgstr "Gruppen" @@ -1465,7 +1452,7 @@ msgid "Last name must be 3-20 characters long and contain only letters" msgstr "Der Nachname muss 3-20 Zeichen lang sein und darf nur Buchstaben enthalten" #. placeholder {0}: migrationStatus.result.totalBadgesEarned -#: src/features/admin/components/dashboard/tools-list.tsx:73 +#: src/features/admin/components/dashboard/tools-list.tsx:79 msgid "Last recalculation: {0, plural, one {# badge earned} other {# badges earned}}" msgstr "Letzte Neuberechnung: {0, plural, one {# Abzeichen verdient} other {# Abzeichen verdient}}" @@ -1530,7 +1517,7 @@ msgstr "S" msgid "Mainline" msgstr "Mainline" -#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:114 +#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:95 msgid "Make Prediction" msgstr "Tipp abgeben" @@ -1538,8 +1525,8 @@ msgstr "Tipp abgeben" msgid "Make Your Prediction" msgstr "Gib deinen Tipp ab" -#: src/features/admin/components/dashboard/tournament-card.tsx:68 -#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:89 +#: src/features/admin/components/dashboard/tournament-section.tsx:39 +#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:70 #: src/features/tournaments/components/upcoming-tournament/index.tsx:144 msgid "Manage" msgstr "Verwalten" @@ -1554,8 +1541,7 @@ msgid "Manage Team Songs/Logos" msgstr "Team-Songs/Logos verwalten" #: src/app/routes/_authed/admin/tournaments/index.tsx:17 -#: src/features/admin/components/dashboard/tools-list.tsx:49 -#: src/features/admin/components/dashboard/tournament-card.tsx:25 +#: src/features/admin/components/dashboard/tools-list.tsx:50 msgid "Manage Tournaments" msgstr "Turniere verwalten" @@ -1642,7 +1628,7 @@ msgstr "MAU" msgid "Me" msgstr "Ich" -#: src/features/admin/components/dashboard/tools-list.tsx:59 +#: src/features/admin/components/dashboard/tools-list.tsx:65 msgid "Migrate Badge Progress" msgstr "Abzeichen-Fortschritt migrieren" @@ -1748,10 +1734,6 @@ msgstr "Keine aktive Wiedergabe zum Erfassen" msgid "No Activities Found" msgstr "Keine Aktivitäten gefunden" -#: src/features/admin/components/dashboard/activity-feed.tsx:78 -msgid "No activity yet" -msgstr "Noch keine Aktivität" - #: src/features/badges/components/badge-stats-table.tsx:45 msgid "No Badges Available" msgstr "Keine Abzeichen verfügbar" @@ -1844,15 +1826,10 @@ msgstr "Keine Teams gefunden, die zu \"{search}\" passen" msgid "No tournaments found" msgstr "Keine Turniere gefunden" -#: src/features/admin/components/dashboard/tournament-card.tsx:18 -msgid "No tournaments yet" -msgstr "Noch keine Turniere" - #: src/features/tournaments/components/tournament-card-list.tsx:40 msgid "No Tournaments Yet" msgstr "Noch keine Turniere" -#: src/features/admin/components/dashboard/errors-card.tsx:53 #: src/features/telemetry/components/error-browser/error-browser.tsx:27 msgid "No unresolved errors" msgstr "Keine offenen Fehler" @@ -1914,15 +1891,15 @@ msgstr "Sobald du angemeldet bist, kannst du andere Free Agents und ihre Telefon msgid "One time code" msgstr "Einmalcode" -#: src/features/admin/components/dashboard/now-strip.tsx:44 -msgid "Online now" -msgstr "Gerade online" +#: src/features/admin/components/dashboard/status-strip.tsx:56 +msgid "Online" +msgstr "Online" -#: src/features/admin/components/dashboard/now-strip.tsx:46 +#: src/features/admin/components/dashboard/status-strip.tsx:61 msgid "Open errors" msgstr "Offene Fehler" -#: src/features/admin/components/dashboard/tools-list.tsx:89 +#: src/features/admin/components/dashboard/tools-list.tsx:95 msgid "Open Pocketbase" msgstr "Pocketbase öffnen" @@ -1972,11 +1949,7 @@ msgstr "p50" msgid "p95" msgstr "p95" -#: src/features/admin/components/dashboard/usage-card.tsx:45 -msgid "Page views" -msgstr "Seitenaufrufe" - -#: src/features/telemetry/components/usage-screen.tsx:177 +#: src/features/telemetry/components/usage-screen.tsx:176 msgid "Page views by route" msgstr "Seitenaufrufe nach Route" @@ -2095,7 +2068,7 @@ msgstr "Punktevergabe für Tipps:" #: src/app/routes/_authed/tournaments/$id.predictions.tsx:23 #: src/features/predictions/components/prediction-leaderboard.tsx:147 -#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:123 +#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:104 msgid "Predictions" msgstr "Tipps" @@ -2146,14 +2119,10 @@ msgstr "Reaktionen" #. placeholder {0}: migrationStatus.playersProcessed #. placeholder {1}: migrationStatus.totalPlayers -#: src/features/admin/components/dashboard/tools-list.tsx:66 +#: src/features/admin/components/dashboard/tools-list.tsx:72 msgid "Recalculating badges in the background ({0}/{1})" msgstr "Abzeichen werden im Hintergrund neu berechnet ({0}/{1})" -#: src/features/admin/components/dashboard/activity-feed.tsx:34 -msgid "Recent activity" -msgstr "Letzte Aktivität" - #: src/features/tournaments/components/started-tournament/sections/recent-results.tsx:34 msgid "Recently finished" msgstr "Kürzlich beendet" @@ -2218,17 +2187,20 @@ msgstr "Runde {0}" msgid "Route" msgstr "Route" -#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:142 +#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:123 #: src/features/tournaments/components/upcoming-tournament/rules-list-button.tsx:23 msgid "Rules" msgstr "Regeln" +#: src/features/admin/components/dashboard/tournament-section.tsx:29 +msgid "Run" +msgstr "Starten" + #: src/app/routes/_authed/admin/tournaments/run.$id.tsx:36 msgid "Run {name}" msgstr "{name} ausführen" -#: src/features/admin/components/dashboard/tournament-card.tsx:53 -#: src/features/tournaments/components/manage-tournament.tsx:49 +#: src/features/tournaments/components/manage-tournament.tsx:41 msgid "Run Tournament" msgstr "Turnier starten" @@ -2517,7 +2489,7 @@ msgstr "Startzeit ist erforderlich" msgid "Start typing to search..." msgstr "Tippen, um zu suchen …" -#: src/features/admin/components/dashboard/tournament-card.tsx:43 +#: src/features/admin/components/dashboard/tournament-section.tsx:24 msgid "Starts in" msgstr "Beginnt in" @@ -2639,7 +2611,7 @@ msgstr "Team erfolgreich aktualisiert!" #: src/features/players/components/profile/skeleton.tsx:75 #: src/features/tournaments/components/profile/index.tsx:32 #: src/features/tournaments/components/profile/skeleton.tsx:89 -#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:137 +#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:118 msgid "Teams" msgstr "Teams" @@ -2648,6 +2620,7 @@ msgid "Teams created successfully!" msgstr "Teams erfolgreich erstellt!" #: src/app/routes/_authed/admin/telemetry.tsx:20 +#: src/features/admin/components/dashboard/tools-list.tsx:55 msgid "Telemetry" msgstr "Telemetrie" @@ -2730,7 +2703,7 @@ msgstr "Die besten {0, plural, one {# Team} other {# Teams}} aus jeder Gruppe ko msgid "Top {n} advance" msgstr "Top {n} kommen weiter" -#: src/features/telemetry/components/usage-screen.tsx:191 +#: src/features/telemetry/components/usage-screen.tsx:190 msgid "Top functions by call count" msgstr "Top-Funktionen nach Aufrufanzahl" @@ -2757,7 +2730,7 @@ msgstr "Turnier erfolgreich erstellt!" msgid "Tournament not found" msgstr "Turnier nicht gefunden" -#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:152 +#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:133 #: src/features/tournaments/components/upcoming-tournament/rules-list-button.tsx:25 msgid "Tournament Rules" msgstr "Turnierregeln" @@ -2786,7 +2759,7 @@ msgstr "trackId ist für die playTrack-Aktion erforderlich" msgid "Try the new themes" msgstr "Probier die neuen Designs aus" -#: src/features/admin/components/dashboard/quiet-boundary.tsx:26 +#: src/features/admin/components/dashboard/quiet-boundary.tsx:24 msgid "Unavailable right now" msgstr "Gerade nicht verfügbar" @@ -2851,10 +2824,6 @@ msgstr "Turnier aktualisieren" msgid "Usage" msgstr "Nutzung" -#: src/features/admin/components/dashboard/usage-card.tsx:34 -msgid "Usage, 7 days" -msgstr "Nutzung, 7 Tage" - #: src/features/activities/components/activities-table.tsx:202 #: src/features/telemetry/components/client-events-table.tsx:146 msgid "User Agent" @@ -2898,7 +2867,7 @@ msgstr "Tipps ansehen" msgid "Viewport" msgstr "Viewport" -#: src/features/telemetry/components/usage-screen.tsx:211 +#: src/features/telemetry/components/usage-screen.tsx:213 msgid "Vitals" msgstr "Vitals" diff --git a/src/locales/en/messages.po b/src/locales/en/messages.po index 59df585..ecd6291 100644 --- a/src/locales/en/messages.po +++ b/src/locales/en/messages.po @@ -153,7 +153,7 @@ msgstr "{0}ms" msgid "{count, plural, one {Players (#)} other {Players (#)}}" msgstr "{count, plural, one {Players (#)} other {Players (#)}}" -#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:134 +#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:115 #: src/features/tournaments/components/upcoming-tournament/team-list-button.tsx:24 msgid "{count, plural, one {Teams (#)} other {Teams (#)}}" msgstr "{count, plural, one {Teams (#)} other {Teams (#)}}" @@ -355,7 +355,7 @@ msgstr "2. Earlier submission" msgid "2nd place" msgstr "2nd place" -#: src/features/telemetry/components/usage-screen.tsx:256 +#: src/features/telemetry/components/usage-screen.tsx:258 msgid "30d" msgstr "30d" @@ -371,7 +371,7 @@ msgstr "3rd place" msgid "5s" msgstr "5s" -#: src/features/telemetry/components/usage-screen.tsx:255 +#: src/features/telemetry/components/usage-screen.tsx:257 msgid "7d" msgstr "7d" @@ -420,10 +420,6 @@ msgstr "Account created successfully!" msgid "Active device: {0}" msgstr "Active device: {0}" -#: src/features/admin/components/dashboard/usage-card.tsx:39 -msgid "Active players" -msgstr "Active players" - #: src/features/activities/components/activities-table.tsx:159 msgid "Activity Details" msgstr "Activity Details" @@ -441,7 +437,7 @@ msgid "Add Team" msgstr "Add Team" #: src/app/routes/_authed/admin.tsx:15 -#: src/app/routes/_authed/admin/index.tsx:22 +#: src/app/routes/_authed/admin/index.tsx:27 #: src/features/core/hooks/use-links.ts:37 msgid "Admin" msgstr "Admin" @@ -564,7 +560,7 @@ msgstr "Avg Win Margin" msgid "Award Badge" msgstr "Award Badge" -#: src/features/admin/components/dashboard/tools-list.tsx:54 +#: src/features/admin/components/dashboard/tools-list.tsx:60 msgid "Award Badges" msgstr "Award Badges" @@ -598,15 +594,15 @@ msgstr "Back" msgid "Badge awarded to {playerName}" msgstr "Badge awarded to {playerName}" -#: src/features/admin/components/dashboard/tools-list.tsx:85 +#: src/features/admin/components/dashboard/tools-list.tsx:91 msgid "Badge recalculation failed. Check the server logs." msgstr "Badge recalculation failed. Check the server logs." -#: src/features/admin/components/dashboard/tools-list.tsx:38 +#: src/features/admin/components/dashboard/tools-list.tsx:39 msgid "Badge recalculation is already running" msgstr "Badge recalculation is already running" -#: src/features/admin/components/dashboard/tools-list.tsx:37 +#: src/features/admin/components/dashboard/tools-list.tsx:38 msgid "Badge recalculation started in the background" msgstr "Badge recalculation started in the background" @@ -631,13 +627,13 @@ msgstr "Big Top" msgid "Both teams cannot have 10 cups" msgstr "Both teams cannot have 10 cups" -#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:106 +#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:87 #: src/features/tournaments/components/upcoming-tournament/index.tsx:151 msgid "Bracket" msgstr "Bracket" #: src/app/routes/_authed/admin/preview.tsx:13 -#: src/features/admin/components/dashboard/tools-list.tsx:96 +#: src/features/admin/components/dashboard/tools-list.tsx:102 msgid "Bracket Preview" msgstr "Bracket Preview" @@ -654,7 +650,7 @@ msgstr "Brutal" msgid "CA" msgstr "CA" -#: src/features/admin/components/dashboard/now-strip.tsx:52 +#: src/features/admin/components/dashboard/status-strip.tsx:67 msgid "Calls today" msgstr "Calls today" @@ -720,16 +716,12 @@ msgstr "Clear selected player" msgid "Client error spike" msgstr "Client error spike" -#: src/features/admin/components/dashboard/errors-card.tsx:21 -msgid "Client errors" -msgstr "Client errors" - #: src/features/telemetry/components/usage-screen.tsx:203 msgid "Client errors per day" msgstr "Client errors per day" #: src/components/sheet/modal.tsx:35 -#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:169 +#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:150 #: src/features/tournaments/components/upcoming-tournament/rules-list-button.tsx:36 msgid "Close" msgstr "Close" @@ -883,15 +875,10 @@ msgstr "Cups Made" msgid "Custom Hex Code" msgstr "Custom Hex Code" -#: src/features/admin/components/dashboard/usage-card.tsx:41 #: src/features/telemetry/components/usage-screen.tsx:159 msgid "Daily active players" msgstr "Daily active players" -#: src/features/admin/components/dashboard/usage-card.tsx:47 -msgid "Daily page views" -msgstr "Daily page views" - #: src/features/settings/components/color-scheme-picker.tsx:39 msgid "Dark" msgstr "Dark" @@ -947,7 +934,7 @@ msgstr "Downstream matches were reset because the correction changed who advance msgid "Duration" msgstr "Duration" -#: src/features/telemetry/components/usage-screen.tsx:233 +#: src/features/telemetry/components/usage-screen.tsx:235 msgid "Duration p50 / p95" msgstr "Duration p50 / p95" @@ -970,12 +957,12 @@ msgstr "Edit Match" msgid "Edit match score" msgstr "Edit match score" -#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:114 +#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:95 msgid "Edit Prediction" msgstr "Edit Prediction" -#: src/features/tournaments/components/manage-tournament.tsx:65 -#: src/features/tournaments/components/manage-tournament.tsx:92 +#: src/features/tournaments/components/manage-tournament.tsx:56 +#: src/features/tournaments/components/manage-tournament.tsx:83 msgid "Edit Rules" msgstr "Edit Rules" @@ -984,8 +971,8 @@ msgstr "Edit Rules" msgid "Edit Team" msgstr "Edit Team" -#: src/features/tournaments/components/manage-tournament.tsx:60 -#: src/features/tournaments/components/manage-tournament.tsx:72 +#: src/features/tournaments/components/manage-tournament.tsx:51 +#: src/features/tournaments/components/manage-tournament.tsx:63 msgid "Edit Tournament" msgstr "Edit Tournament" @@ -1048,7 +1035,7 @@ msgstr "Enrolled Players" #: src/features/tournaments/components/enrollment/enrollment-stats.tsx:28 #: src/features/tournaments/components/enrollment/team-section.tsx:240 -#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:145 +#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:126 #: src/features/tournaments/components/upcoming-tournament/team-list-button.tsx:31 msgid "Enrolled Teams" msgstr "Enrolled Teams" @@ -1058,13 +1045,13 @@ msgid "Enrolling as a free agent adds you to a pool of players looking for teamm msgstr "Enrolling as a free agent adds you to a pool of players looking for teammates." #: src/app/routes/_authed/admin/tournaments/$id/enrollment.tsx:33 -#: src/features/admin/components/dashboard/tournament-card.tsx:61 +#: src/features/admin/components/dashboard/tournament-section.tsx:34 #: src/features/tournaments/components/upcoming-tournament/enroll-free-agent.tsx:29 #: src/features/tournaments/components/upcoming-tournament/index.tsx:69 msgid "Enrollment" msgstr "Enrollment" -#: src/features/tournaments/components/manage-tournament.tsx:55 +#: src/features/tournaments/components/manage-tournament.tsx:46 msgid "Enrollment & Teams" msgstr "Enrollment & Teams" @@ -1181,7 +1168,7 @@ msgstr "Failed to set active device" msgid "Failed to set volume" msgstr "Failed to set volume" -#: src/features/admin/components/dashboard/tools-list.tsx:41 +#: src/features/admin/components/dashboard/tools-list.tsx:42 msgid "Failed to start badge recalculation" msgstr "Failed to start badge recalculation" @@ -1251,7 +1238,7 @@ msgstr "Free Agent Enrollment" msgid "Free Agents" msgstr "Free Agents" -#: src/features/telemetry/components/usage-screen.tsx:226 +#: src/features/telemetry/components/usage-screen.tsx:228 msgid "Function" msgstr "Function" @@ -1319,7 +1306,7 @@ msgstr "Group stage generated successfully!" msgid "Group Stage Setup" msgstr "Group Stage Setup" -#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:98 +#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:79 msgid "Groups" msgstr "Groups" @@ -1465,7 +1452,7 @@ msgid "Last name must be 3-20 characters long and contain only letters" msgstr "Last name must be 3-20 characters long and contain only letters" #. placeholder {0}: migrationStatus.result.totalBadgesEarned -#: src/features/admin/components/dashboard/tools-list.tsx:73 +#: src/features/admin/components/dashboard/tools-list.tsx:79 msgid "Last recalculation: {0, plural, one {# badge earned} other {# badges earned}}" msgstr "Last recalculation: {0, plural, one {# badge earned} other {# badges earned}}" @@ -1530,7 +1517,7 @@ msgstr "M" msgid "Mainline" msgstr "Mainline" -#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:114 +#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:95 msgid "Make Prediction" msgstr "Make Prediction" @@ -1538,8 +1525,8 @@ msgstr "Make Prediction" msgid "Make Your Prediction" msgstr "Make Your Prediction" -#: src/features/admin/components/dashboard/tournament-card.tsx:68 -#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:89 +#: src/features/admin/components/dashboard/tournament-section.tsx:39 +#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:70 #: src/features/tournaments/components/upcoming-tournament/index.tsx:144 msgid "Manage" msgstr "Manage" @@ -1554,8 +1541,7 @@ msgid "Manage Team Songs/Logos" msgstr "Manage Team Songs/Logos" #: src/app/routes/_authed/admin/tournaments/index.tsx:17 -#: src/features/admin/components/dashboard/tools-list.tsx:49 -#: src/features/admin/components/dashboard/tournament-card.tsx:25 +#: src/features/admin/components/dashboard/tools-list.tsx:50 msgid "Manage Tournaments" msgstr "Manage Tournaments" @@ -1642,7 +1628,7 @@ msgstr "MAU" msgid "Me" msgstr "Me" -#: src/features/admin/components/dashboard/tools-list.tsx:59 +#: src/features/admin/components/dashboard/tools-list.tsx:65 msgid "Migrate Badge Progress" msgstr "Migrate Badge Progress" @@ -1748,10 +1734,6 @@ msgstr "No active playback to capture" msgid "No Activities Found" msgstr "No Activities Found" -#: src/features/admin/components/dashboard/activity-feed.tsx:78 -msgid "No activity yet" -msgstr "No activity yet" - #: src/features/badges/components/badge-stats-table.tsx:45 msgid "No Badges Available" msgstr "No Badges Available" @@ -1844,15 +1826,10 @@ msgstr "No teams found matching \"{search}\"" msgid "No tournaments found" msgstr "No tournaments found" -#: src/features/admin/components/dashboard/tournament-card.tsx:18 -msgid "No tournaments yet" -msgstr "No tournaments yet" - #: src/features/tournaments/components/tournament-card-list.tsx:40 msgid "No Tournaments Yet" msgstr "No Tournaments Yet" -#: src/features/admin/components/dashboard/errors-card.tsx:53 #: src/features/telemetry/components/error-browser/error-browser.tsx:27 msgid "No unresolved errors" msgstr "No unresolved errors" @@ -1914,15 +1891,15 @@ msgstr "Once enrolled, you can view other free agents and their phone number in msgid "One time code" msgstr "One time code" -#: src/features/admin/components/dashboard/now-strip.tsx:44 -msgid "Online now" -msgstr "Online now" +#: src/features/admin/components/dashboard/status-strip.tsx:56 +msgid "Online" +msgstr "Online" -#: src/features/admin/components/dashboard/now-strip.tsx:46 +#: src/features/admin/components/dashboard/status-strip.tsx:61 msgid "Open errors" msgstr "Open errors" -#: src/features/admin/components/dashboard/tools-list.tsx:89 +#: src/features/admin/components/dashboard/tools-list.tsx:95 msgid "Open Pocketbase" msgstr "Open Pocketbase" @@ -1972,11 +1949,7 @@ msgstr "p50" msgid "p95" msgstr "p95" -#: src/features/admin/components/dashboard/usage-card.tsx:45 -msgid "Page views" -msgstr "Page views" - -#: src/features/telemetry/components/usage-screen.tsx:177 +#: src/features/telemetry/components/usage-screen.tsx:176 msgid "Page views by route" msgstr "Page views by route" @@ -2095,7 +2068,7 @@ msgstr "Prediction Scoring:" #: src/app/routes/_authed/tournaments/$id.predictions.tsx:23 #: src/features/predictions/components/prediction-leaderboard.tsx:147 -#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:123 +#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:104 msgid "Predictions" msgstr "Predictions" @@ -2146,14 +2119,10 @@ msgstr "Reactions" #. placeholder {0}: migrationStatus.playersProcessed #. placeholder {1}: migrationStatus.totalPlayers -#: src/features/admin/components/dashboard/tools-list.tsx:66 +#: src/features/admin/components/dashboard/tools-list.tsx:72 msgid "Recalculating badges in the background ({0}/{1})" msgstr "Recalculating badges in the background ({0}/{1})" -#: src/features/admin/components/dashboard/activity-feed.tsx:34 -msgid "Recent activity" -msgstr "Recent activity" - #: src/features/tournaments/components/started-tournament/sections/recent-results.tsx:34 msgid "Recently finished" msgstr "Recently finished" @@ -2218,17 +2187,20 @@ msgstr "Round {0}" msgid "Route" msgstr "Route" -#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:142 +#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:123 #: src/features/tournaments/components/upcoming-tournament/rules-list-button.tsx:23 msgid "Rules" msgstr "Rules" +#: src/features/admin/components/dashboard/tournament-section.tsx:29 +msgid "Run" +msgstr "Run" + #: src/app/routes/_authed/admin/tournaments/run.$id.tsx:36 msgid "Run {name}" msgstr "Run {name}" -#: src/features/admin/components/dashboard/tournament-card.tsx:53 -#: src/features/tournaments/components/manage-tournament.tsx:49 +#: src/features/tournaments/components/manage-tournament.tsx:41 msgid "Run Tournament" msgstr "Run Tournament" @@ -2517,7 +2489,7 @@ msgstr "Start time is required" msgid "Start typing to search..." msgstr "Start typing to search..." -#: src/features/admin/components/dashboard/tournament-card.tsx:43 +#: src/features/admin/components/dashboard/tournament-section.tsx:24 msgid "Starts in" msgstr "Starts in" @@ -2639,7 +2611,7 @@ msgstr "Team updated successfully!" #: src/features/players/components/profile/skeleton.tsx:75 #: src/features/tournaments/components/profile/index.tsx:32 #: src/features/tournaments/components/profile/skeleton.tsx:89 -#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:137 +#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:118 msgid "Teams" msgstr "Teams" @@ -2648,6 +2620,7 @@ msgid "Teams created successfully!" msgstr "Teams created successfully!" #: src/app/routes/_authed/admin/telemetry.tsx:20 +#: src/features/admin/components/dashboard/tools-list.tsx:55 msgid "Telemetry" msgstr "Telemetry" @@ -2730,7 +2703,7 @@ msgstr "Top {0, plural, one {# team} other {# teams}} from each group will advan msgid "Top {n} advance" msgstr "Top {n} advance" -#: src/features/telemetry/components/usage-screen.tsx:191 +#: src/features/telemetry/components/usage-screen.tsx:190 msgid "Top functions by call count" msgstr "Top functions by call count" @@ -2757,7 +2730,7 @@ msgstr "Tournament created successfully!" msgid "Tournament not found" msgstr "Tournament not found" -#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:152 +#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:133 #: src/features/tournaments/components/upcoming-tournament/rules-list-button.tsx:25 msgid "Tournament Rules" msgstr "Tournament Rules" @@ -2786,7 +2759,7 @@ msgstr "trackId is required for playTrack action" msgid "Try the new themes" msgstr "Try the new themes" -#: src/features/admin/components/dashboard/quiet-boundary.tsx:26 +#: src/features/admin/components/dashboard/quiet-boundary.tsx:24 msgid "Unavailable right now" msgstr "Unavailable right now" @@ -2851,10 +2824,6 @@ msgstr "Update Tournament" msgid "Usage" msgstr "Usage" -#: src/features/admin/components/dashboard/usage-card.tsx:34 -msgid "Usage, 7 days" -msgstr "Usage, 7 days" - #: src/features/activities/components/activities-table.tsx:202 #: src/features/telemetry/components/client-events-table.tsx:146 msgid "User Agent" @@ -2898,7 +2867,7 @@ msgstr "View Predictions" msgid "Viewport" msgstr "Viewport" -#: src/features/telemetry/components/usage-screen.tsx:211 +#: src/features/telemetry/components/usage-screen.tsx:213 msgid "Vitals" msgstr "Vitals" diff --git a/src/locales/es/messages.po b/src/locales/es/messages.po index 29f2cea..78c5655 100644 --- a/src/locales/es/messages.po +++ b/src/locales/es/messages.po @@ -153,7 +153,7 @@ msgstr "{0}ms" msgid "{count, plural, one {Players (#)} other {Players (#)}}" msgstr "{count, plural, one {Jugadores (#)} other {Jugadores (#)}}" -#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:134 +#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:115 #: src/features/tournaments/components/upcoming-tournament/team-list-button.tsx:24 msgid "{count, plural, one {Teams (#)} other {Teams (#)}}" msgstr "{count, plural, one {Equipos (#)} other {Equipos (#)}}" @@ -355,7 +355,7 @@ msgstr "2. Envío más temprano" msgid "2nd place" msgstr "2do lugar" -#: src/features/telemetry/components/usage-screen.tsx:256 +#: src/features/telemetry/components/usage-screen.tsx:258 msgid "30d" msgstr "30d" @@ -371,7 +371,7 @@ msgstr "3er lugar" msgid "5s" msgstr "5s" -#: src/features/telemetry/components/usage-screen.tsx:255 +#: src/features/telemetry/components/usage-screen.tsx:257 msgid "7d" msgstr "7d" @@ -420,10 +420,6 @@ msgstr "¡Cuenta creada exitosamente!" msgid "Active device: {0}" msgstr "Dispositivo activo: {0}" -#: src/features/admin/components/dashboard/usage-card.tsx:39 -msgid "Active players" -msgstr "Jugadores activos" - #: src/features/activities/components/activities-table.tsx:159 msgid "Activity Details" msgstr "Detalles de la actividad" @@ -441,7 +437,7 @@ msgid "Add Team" msgstr "Agregar Equipo" #: src/app/routes/_authed/admin.tsx:15 -#: src/app/routes/_authed/admin/index.tsx:22 +#: src/app/routes/_authed/admin/index.tsx:27 #: src/features/core/hooks/use-links.ts:37 msgid "Admin" msgstr "Admin" @@ -564,7 +560,7 @@ msgstr "Prom. Margen de Victoria" msgid "Award Badge" msgstr "Otorgar insignia" -#: src/features/admin/components/dashboard/tools-list.tsx:54 +#: src/features/admin/components/dashboard/tools-list.tsx:60 msgid "Award Badges" msgstr "Otorgar insignias" @@ -598,15 +594,15 @@ msgstr "Atrás" msgid "Badge awarded to {playerName}" msgstr "Insignia otorgada a {playerName}" -#: src/features/admin/components/dashboard/tools-list.tsx:85 +#: src/features/admin/components/dashboard/tools-list.tsx:91 msgid "Badge recalculation failed. Check the server logs." msgstr "Falló el recálculo de insignias. Revisa los registros del servidor." -#: src/features/admin/components/dashboard/tools-list.tsx:38 +#: src/features/admin/components/dashboard/tools-list.tsx:39 msgid "Badge recalculation is already running" msgstr "El recálculo de insignias ya está en curso" -#: src/features/admin/components/dashboard/tools-list.tsx:37 +#: src/features/admin/components/dashboard/tools-list.tsx:38 msgid "Badge recalculation started in the background" msgstr "Recálculo de insignias iniciado en segundo plano" @@ -631,13 +627,13 @@ msgstr "Circo" msgid "Both teams cannot have 10 cups" msgstr "Los dos equipos no pueden tener 10 vasos" -#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:106 +#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:87 #: src/features/tournaments/components/upcoming-tournament/index.tsx:151 msgid "Bracket" msgstr "Bracket" #: src/app/routes/_authed/admin/preview.tsx:13 -#: src/features/admin/components/dashboard/tools-list.tsx:96 +#: src/features/admin/components/dashboard/tools-list.tsx:102 msgid "Bracket Preview" msgstr "Vista previa del bracket" @@ -654,7 +650,7 @@ msgstr "Brutal" msgid "CA" msgstr "VC" -#: src/features/admin/components/dashboard/now-strip.tsx:52 +#: src/features/admin/components/dashboard/status-strip.tsx:67 msgid "Calls today" msgstr "Llamadas hoy" @@ -720,16 +716,12 @@ msgstr "Quitar jugador seleccionado" msgid "Client error spike" msgstr "Aumento de errores de cliente" -#: src/features/admin/components/dashboard/errors-card.tsx:21 -msgid "Client errors" -msgstr "Errores de cliente" - #: src/features/telemetry/components/usage-screen.tsx:203 msgid "Client errors per day" msgstr "Errores de cliente por día" #: src/components/sheet/modal.tsx:35 -#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:169 +#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:150 #: src/features/tournaments/components/upcoming-tournament/rules-list-button.tsx:36 msgid "Close" msgstr "Cerrar" @@ -883,15 +875,10 @@ msgstr "Vasos anotados" msgid "Custom Hex Code" msgstr "Código hex personalizado" -#: src/features/admin/components/dashboard/usage-card.tsx:41 #: src/features/telemetry/components/usage-screen.tsx:159 msgid "Daily active players" msgstr "Jugadores activos diarios" -#: src/features/admin/components/dashboard/usage-card.tsx:47 -msgid "Daily page views" -msgstr "Vistas de página diarias" - #: src/features/settings/components/color-scheme-picker.tsx:39 msgid "Dark" msgstr "Oscuro" @@ -947,7 +934,7 @@ msgstr "Los partidos posteriores se reiniciaron porque la corrección cambió qu msgid "Duration" msgstr "Duración" -#: src/features/telemetry/components/usage-screen.tsx:233 +#: src/features/telemetry/components/usage-screen.tsx:235 msgid "Duration p50 / p95" msgstr "Duración p50 / p95" @@ -970,12 +957,12 @@ msgstr "Editar partido" msgid "Edit match score" msgstr "Editar marcador del partido" -#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:114 +#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:95 msgid "Edit Prediction" msgstr "Editar predicción" -#: src/features/tournaments/components/manage-tournament.tsx:65 -#: src/features/tournaments/components/manage-tournament.tsx:92 +#: src/features/tournaments/components/manage-tournament.tsx:56 +#: src/features/tournaments/components/manage-tournament.tsx:83 msgid "Edit Rules" msgstr "Editar reglas" @@ -984,8 +971,8 @@ msgstr "Editar reglas" msgid "Edit Team" msgstr "Editar equipo" -#: src/features/tournaments/components/manage-tournament.tsx:60 -#: src/features/tournaments/components/manage-tournament.tsx:72 +#: src/features/tournaments/components/manage-tournament.tsx:51 +#: src/features/tournaments/components/manage-tournament.tsx:63 msgid "Edit Tournament" msgstr "Editar torneo" @@ -1048,7 +1035,7 @@ msgstr "Jugadores inscritos" #: src/features/tournaments/components/enrollment/enrollment-stats.tsx:28 #: src/features/tournaments/components/enrollment/team-section.tsx:240 -#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:145 +#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:126 #: src/features/tournaments/components/upcoming-tournament/team-list-button.tsx:31 msgid "Enrolled Teams" msgstr "Equipos inscritos" @@ -1058,13 +1045,13 @@ msgid "Enrolling as a free agent adds you to a pool of players looking for teamm msgstr "Inscribirte como agente libre te agrega a un grupo de jugadores buscando compañeros de equipo." #: src/app/routes/_authed/admin/tournaments/$id/enrollment.tsx:33 -#: src/features/admin/components/dashboard/tournament-card.tsx:61 +#: src/features/admin/components/dashboard/tournament-section.tsx:34 #: src/features/tournaments/components/upcoming-tournament/enroll-free-agent.tsx:29 #: src/features/tournaments/components/upcoming-tournament/index.tsx:69 msgid "Enrollment" msgstr "Inscripción" -#: src/features/tournaments/components/manage-tournament.tsx:55 +#: src/features/tournaments/components/manage-tournament.tsx:46 msgid "Enrollment & Teams" msgstr "Inscripción y equipos" @@ -1181,7 +1168,7 @@ msgstr "No se pudo establecer el dispositivo activo" msgid "Failed to set volume" msgstr "No se pudo ajustar el volumen" -#: src/features/admin/components/dashboard/tools-list.tsx:41 +#: src/features/admin/components/dashboard/tools-list.tsx:42 msgid "Failed to start badge recalculation" msgstr "No se pudo iniciar el recálculo de insignias" @@ -1251,7 +1238,7 @@ msgstr "Inscripción de agente libre" msgid "Free Agents" msgstr "Agentes libres" -#: src/features/telemetry/components/usage-screen.tsx:226 +#: src/features/telemetry/components/usage-screen.tsx:228 msgid "Function" msgstr "Función" @@ -1319,7 +1306,7 @@ msgstr "¡Fase de grupos generada con éxito!" msgid "Group Stage Setup" msgstr "Configuración de la fase de grupos" -#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:98 +#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:79 msgid "Groups" msgstr "Grupos" @@ -1465,7 +1452,7 @@ msgid "Last name must be 3-20 characters long and contain only letters" msgstr "El apellido debe tener entre 3 y 20 caracteres y contener solo letras" #. placeholder {0}: migrationStatus.result.totalBadgesEarned -#: src/features/admin/components/dashboard/tools-list.tsx:73 +#: src/features/admin/components/dashboard/tools-list.tsx:79 msgid "Last recalculation: {0, plural, one {# badge earned} other {# badges earned}}" msgstr "Último recálculo: {0, plural, one {# insignia obtenida} other {# insignias obtenidas}}" @@ -1530,7 +1517,7 @@ msgstr "J" msgid "Mainline" msgstr "Mainline" -#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:114 +#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:95 msgid "Make Prediction" msgstr "Hacer predicción" @@ -1538,8 +1525,8 @@ msgstr "Hacer predicción" msgid "Make Your Prediction" msgstr "Haz tu predicción" -#: src/features/admin/components/dashboard/tournament-card.tsx:68 -#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:89 +#: src/features/admin/components/dashboard/tournament-section.tsx:39 +#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:70 #: src/features/tournaments/components/upcoming-tournament/index.tsx:144 msgid "Manage" msgstr "Administrar" @@ -1554,8 +1541,7 @@ msgid "Manage Team Songs/Logos" msgstr "Administrar canciones/logos del equipo" #: src/app/routes/_authed/admin/tournaments/index.tsx:17 -#: src/features/admin/components/dashboard/tools-list.tsx:49 -#: src/features/admin/components/dashboard/tournament-card.tsx:25 +#: src/features/admin/components/dashboard/tools-list.tsx:50 msgid "Manage Tournaments" msgstr "Administrar torneos" @@ -1642,7 +1628,7 @@ msgstr "MAU" msgid "Me" msgstr "Yo" -#: src/features/admin/components/dashboard/tools-list.tsx:59 +#: src/features/admin/components/dashboard/tools-list.tsx:65 msgid "Migrate Badge Progress" msgstr "Migrar progreso de insignias" @@ -1748,10 +1734,6 @@ msgstr "No hay reproducción activa para capturar" msgid "No Activities Found" msgstr "No se encontraron actividades" -#: src/features/admin/components/dashboard/activity-feed.tsx:78 -msgid "No activity yet" -msgstr "Aún no hay actividad" - #: src/features/badges/components/badge-stats-table.tsx:45 msgid "No Badges Available" msgstr "No hay insignias disponibles" @@ -1844,15 +1826,10 @@ msgstr "No se encontraron equipos que coincidan con \"{search}\"" msgid "No tournaments found" msgstr "No se encontraron torneos" -#: src/features/admin/components/dashboard/tournament-card.tsx:18 -msgid "No tournaments yet" -msgstr "Aún no hay torneos" - #: src/features/tournaments/components/tournament-card-list.tsx:40 msgid "No Tournaments Yet" msgstr "Aún no hay torneos" -#: src/features/admin/components/dashboard/errors-card.tsx:53 #: src/features/telemetry/components/error-browser/error-browser.tsx:27 msgid "No unresolved errors" msgstr "No hay errores sin resolver" @@ -1914,15 +1891,15 @@ msgstr "Una vez inscrito, puedes ver a otros agentes libres y su número de tel msgid "One time code" msgstr "Código de un solo uso" -#: src/features/admin/components/dashboard/now-strip.tsx:44 -msgid "Online now" -msgstr "En línea ahora" +#: src/features/admin/components/dashboard/status-strip.tsx:56 +msgid "Online" +msgstr "En línea" -#: src/features/admin/components/dashboard/now-strip.tsx:46 +#: src/features/admin/components/dashboard/status-strip.tsx:61 msgid "Open errors" msgstr "Errores abiertos" -#: src/features/admin/components/dashboard/tools-list.tsx:89 +#: src/features/admin/components/dashboard/tools-list.tsx:95 msgid "Open Pocketbase" msgstr "Abrir Pocketbase" @@ -1972,11 +1949,7 @@ msgstr "p50" msgid "p95" msgstr "p95" -#: src/features/admin/components/dashboard/usage-card.tsx:45 -msgid "Page views" -msgstr "Vistas de página" - -#: src/features/telemetry/components/usage-screen.tsx:177 +#: src/features/telemetry/components/usage-screen.tsx:176 msgid "Page views by route" msgstr "Vistas de página por ruta" @@ -2095,7 +2068,7 @@ msgstr "Puntuación de predicciones:" #: src/app/routes/_authed/tournaments/$id.predictions.tsx:23 #: src/features/predictions/components/prediction-leaderboard.tsx:147 -#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:123 +#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:104 msgid "Predictions" msgstr "Predicciones" @@ -2146,14 +2119,10 @@ msgstr "Reacciones" #. placeholder {0}: migrationStatus.playersProcessed #. placeholder {1}: migrationStatus.totalPlayers -#: src/features/admin/components/dashboard/tools-list.tsx:66 +#: src/features/admin/components/dashboard/tools-list.tsx:72 msgid "Recalculating badges in the background ({0}/{1})" msgstr "Recalculando insignias en segundo plano ({0}/{1})" -#: src/features/admin/components/dashboard/activity-feed.tsx:34 -msgid "Recent activity" -msgstr "Actividad reciente" - #: src/features/tournaments/components/started-tournament/sections/recent-results.tsx:34 msgid "Recently finished" msgstr "Recién terminados" @@ -2218,17 +2187,20 @@ msgstr "Ronda {0}" msgid "Route" msgstr "Ruta" -#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:142 +#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:123 #: src/features/tournaments/components/upcoming-tournament/rules-list-button.tsx:23 msgid "Rules" msgstr "Reglas" +#: src/features/admin/components/dashboard/tournament-section.tsx:29 +msgid "Run" +msgstr "Iniciar" + #: src/app/routes/_authed/admin/tournaments/run.$id.tsx:36 msgid "Run {name}" msgstr "Ejecutar {name}" -#: src/features/admin/components/dashboard/tournament-card.tsx:53 -#: src/features/tournaments/components/manage-tournament.tsx:49 +#: src/features/tournaments/components/manage-tournament.tsx:41 msgid "Run Tournament" msgstr "Iniciar torneo" @@ -2517,7 +2489,7 @@ msgstr "Se requiere la hora de inicio" msgid "Start typing to search..." msgstr "Empieza a escribir para buscar..." -#: src/features/admin/components/dashboard/tournament-card.tsx:43 +#: src/features/admin/components/dashboard/tournament-section.tsx:24 msgid "Starts in" msgstr "Comienza en" @@ -2639,7 +2611,7 @@ msgstr "¡Equipo actualizado exitosamente!" #: src/features/players/components/profile/skeleton.tsx:75 #: src/features/tournaments/components/profile/index.tsx:32 #: src/features/tournaments/components/profile/skeleton.tsx:89 -#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:137 +#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:118 msgid "Teams" msgstr "Equipos" @@ -2648,6 +2620,7 @@ msgid "Teams created successfully!" msgstr "¡Equipos creados exitosamente!" #: src/app/routes/_authed/admin/telemetry.tsx:20 +#: src/features/admin/components/dashboard/tools-list.tsx:55 msgid "Telemetry" msgstr "Telemetría" @@ -2730,7 +2703,7 @@ msgstr "{0, plural, one {El mejor equipo de cada grupo avanzará} other {Los mej msgid "Top {n} advance" msgstr "Los mejores {n} avanzan" -#: src/features/telemetry/components/usage-screen.tsx:191 +#: src/features/telemetry/components/usage-screen.tsx:190 msgid "Top functions by call count" msgstr "Funciones principales por cantidad de llamadas" @@ -2757,7 +2730,7 @@ msgstr "¡Torneo creado exitosamente!" msgid "Tournament not found" msgstr "Torneo no encontrado" -#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:152 +#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:133 #: src/features/tournaments/components/upcoming-tournament/rules-list-button.tsx:25 msgid "Tournament Rules" msgstr "Reglas del torneo" @@ -2786,7 +2759,7 @@ msgstr "Se requiere trackId para la acción playTrack" msgid "Try the new themes" msgstr "Prueba los nuevos temas" -#: src/features/admin/components/dashboard/quiet-boundary.tsx:26 +#: src/features/admin/components/dashboard/quiet-boundary.tsx:24 msgid "Unavailable right now" msgstr "No disponible por ahora" @@ -2851,10 +2824,6 @@ msgstr "Actualizar torneo" msgid "Usage" msgstr "Uso" -#: src/features/admin/components/dashboard/usage-card.tsx:34 -msgid "Usage, 7 days" -msgstr "Uso, 7 días" - #: src/features/activities/components/activities-table.tsx:202 #: src/features/telemetry/components/client-events-table.tsx:146 msgid "User Agent" @@ -2898,7 +2867,7 @@ msgstr "Ver predicciones" msgid "Viewport" msgstr "Viewport" -#: src/features/telemetry/components/usage-screen.tsx:211 +#: src/features/telemetry/components/usage-screen.tsx:213 msgid "Vitals" msgstr "Vitales" diff --git a/src/locales/ja/messages.po b/src/locales/ja/messages.po index 329fa3a..b46b4ac 100644 --- a/src/locales/ja/messages.po +++ b/src/locales/ja/messages.po @@ -153,7 +153,7 @@ msgstr "{0}ms" msgid "{count, plural, one {Players (#)} other {Players (#)}}" msgstr "{count, plural, one {プレイヤー(#)} other {プレイヤー(#)}}" -#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:134 +#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:115 #: src/features/tournaments/components/upcoming-tournament/team-list-button.tsx:24 msgid "{count, plural, one {Teams (#)} other {Teams (#)}}" msgstr "{count, plural, one {チーム(#)} other {チーム(#)}}" @@ -355,7 +355,7 @@ msgstr "2. 早い提出" msgid "2nd place" msgstr "2位" -#: src/features/telemetry/components/usage-screen.tsx:256 +#: src/features/telemetry/components/usage-screen.tsx:258 msgid "30d" msgstr "30日" @@ -371,7 +371,7 @@ msgstr "3位" msgid "5s" msgstr "5秒" -#: src/features/telemetry/components/usage-screen.tsx:255 +#: src/features/telemetry/components/usage-screen.tsx:257 msgid "7d" msgstr "7日" @@ -420,10 +420,6 @@ msgstr "アカウントが作成されました!" msgid "Active device: {0}" msgstr "アクティブなデバイス: {0}" -#: src/features/admin/components/dashboard/usage-card.tsx:39 -msgid "Active players" -msgstr "アクティブなプレイヤー" - #: src/features/activities/components/activities-table.tsx:159 msgid "Activity Details" msgstr "アクティビティの詳細" @@ -441,7 +437,7 @@ msgid "Add Team" msgstr "チームを追加" #: src/app/routes/_authed/admin.tsx:15 -#: src/app/routes/_authed/admin/index.tsx:22 +#: src/app/routes/_authed/admin/index.tsx:27 #: src/features/core/hooks/use-links.ts:37 msgid "Admin" msgstr "管理者" @@ -564,7 +560,7 @@ msgstr "平均勝差" msgid "Award Badge" msgstr "バッジを付与" -#: src/features/admin/components/dashboard/tools-list.tsx:54 +#: src/features/admin/components/dashboard/tools-list.tsx:60 msgid "Award Badges" msgstr "バッジを付与" @@ -598,15 +594,15 @@ msgstr "戻る" msgid "Badge awarded to {playerName}" msgstr "{playerName}にバッジを付与しました" -#: src/features/admin/components/dashboard/tools-list.tsx:85 +#: src/features/admin/components/dashboard/tools-list.tsx:91 msgid "Badge recalculation failed. Check the server logs." msgstr "バッジの再計算に失敗しました。サーバーログを確認してください。" -#: src/features/admin/components/dashboard/tools-list.tsx:38 +#: src/features/admin/components/dashboard/tools-list.tsx:39 msgid "Badge recalculation is already running" msgstr "バッジの再計算はすでに実行中です" -#: src/features/admin/components/dashboard/tools-list.tsx:37 +#: src/features/admin/components/dashboard/tools-list.tsx:38 msgid "Badge recalculation started in the background" msgstr "バックグラウンドでバッジの再計算を開始しました" @@ -631,13 +627,13 @@ msgstr "サーカス" msgid "Both teams cannot have 10 cups" msgstr "両チーム同時に10カップにはなりません" -#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:106 +#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:87 #: src/features/tournaments/components/upcoming-tournament/index.tsx:151 msgid "Bracket" msgstr "ブラケット" #: src/app/routes/_authed/admin/preview.tsx:13 -#: src/features/admin/components/dashboard/tools-list.tsx:96 +#: src/features/admin/components/dashboard/tools-list.tsx:102 msgid "Bracket Preview" msgstr "ブラケットプレビュー" @@ -654,7 +650,7 @@ msgstr "ブルータル" msgid "CA" msgstr "CA" -#: src/features/admin/components/dashboard/now-strip.tsx:52 +#: src/features/admin/components/dashboard/status-strip.tsx:67 msgid "Calls today" msgstr "本日の呼び出し数" @@ -720,16 +716,12 @@ msgstr "選択したプレイヤーを解除" msgid "Client error spike" msgstr "クライアントエラー急増" -#: src/features/admin/components/dashboard/errors-card.tsx:21 -msgid "Client errors" -msgstr "クライアントエラー" - #: src/features/telemetry/components/usage-screen.tsx:203 msgid "Client errors per day" msgstr "1日あたりのクライアントエラー数" #: src/components/sheet/modal.tsx:35 -#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:169 +#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:150 #: src/features/tournaments/components/upcoming-tournament/rules-list-button.tsx:36 msgid "Close" msgstr "閉じる" @@ -883,15 +875,10 @@ msgstr "決めたカップ" msgid "Custom Hex Code" msgstr "カスタムHEXコード" -#: src/features/admin/components/dashboard/usage-card.tsx:41 #: src/features/telemetry/components/usage-screen.tsx:159 msgid "Daily active players" msgstr "デイリーアクティブプレイヤー数" -#: src/features/admin/components/dashboard/usage-card.tsx:47 -msgid "Daily page views" -msgstr "1日あたりのページビュー数" - #: src/features/settings/components/color-scheme-picker.tsx:39 msgid "Dark" msgstr "ダーク" @@ -947,7 +934,7 @@ msgstr "修正により勝ち上がりの相手が変わったため、以降の msgid "Duration" msgstr "所要時間" -#: src/features/telemetry/components/usage-screen.tsx:233 +#: src/features/telemetry/components/usage-screen.tsx:235 msgid "Duration p50 / p95" msgstr "所要時間 p50 / p95" @@ -970,12 +957,12 @@ msgstr "試合を編集" msgid "Edit match score" msgstr "試合スコアを編集" -#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:114 +#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:95 msgid "Edit Prediction" msgstr "予想を編集" -#: src/features/tournaments/components/manage-tournament.tsx:65 -#: src/features/tournaments/components/manage-tournament.tsx:92 +#: src/features/tournaments/components/manage-tournament.tsx:56 +#: src/features/tournaments/components/manage-tournament.tsx:83 msgid "Edit Rules" msgstr "ルールを編集" @@ -984,8 +971,8 @@ msgstr "ルールを編集" msgid "Edit Team" msgstr "チームを編集" -#: src/features/tournaments/components/manage-tournament.tsx:60 -#: src/features/tournaments/components/manage-tournament.tsx:72 +#: src/features/tournaments/components/manage-tournament.tsx:51 +#: src/features/tournaments/components/manage-tournament.tsx:63 msgid "Edit Tournament" msgstr "トーナメントを編集" @@ -1048,7 +1035,7 @@ msgstr "登録済みプレイヤー" #: src/features/tournaments/components/enrollment/enrollment-stats.tsx:28 #: src/features/tournaments/components/enrollment/team-section.tsx:240 -#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:145 +#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:126 #: src/features/tournaments/components/upcoming-tournament/team-list-button.tsx:31 msgid "Enrolled Teams" msgstr "登録済みチーム" @@ -1058,13 +1045,13 @@ msgid "Enrolling as a free agent adds you to a pool of players looking for teamm msgstr "フリーエージェントとして登録すると、チームメイトを探しているプレイヤーのプールに追加されます。" #: src/app/routes/_authed/admin/tournaments/$id/enrollment.tsx:33 -#: src/features/admin/components/dashboard/tournament-card.tsx:61 +#: src/features/admin/components/dashboard/tournament-section.tsx:34 #: src/features/tournaments/components/upcoming-tournament/enroll-free-agent.tsx:29 #: src/features/tournaments/components/upcoming-tournament/index.tsx:69 msgid "Enrollment" msgstr "参加登録" -#: src/features/tournaments/components/manage-tournament.tsx:55 +#: src/features/tournaments/components/manage-tournament.tsx:46 msgid "Enrollment & Teams" msgstr "参加登録とチーム" @@ -1181,7 +1168,7 @@ msgstr "アクティブなデバイスの設定に失敗しました" msgid "Failed to set volume" msgstr "音量の設定に失敗しました" -#: src/features/admin/components/dashboard/tools-list.tsx:41 +#: src/features/admin/components/dashboard/tools-list.tsx:42 msgid "Failed to start badge recalculation" msgstr "バッジの再計算を開始できませんでした" @@ -1251,7 +1238,7 @@ msgstr "フリーエージェント登録" msgid "Free Agents" msgstr "フリーエージェント" -#: src/features/telemetry/components/usage-screen.tsx:226 +#: src/features/telemetry/components/usage-screen.tsx:228 msgid "Function" msgstr "関数" @@ -1319,7 +1306,7 @@ msgstr "グループステージを生成しました!" msgid "Group Stage Setup" msgstr "グループステージ設定" -#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:98 +#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:79 msgid "Groups" msgstr "グループ" @@ -1465,7 +1452,7 @@ msgid "Last name must be 3-20 characters long and contain only letters" msgstr "姓は3〜20文字の英字のみで入力してください" #. placeholder {0}: migrationStatus.result.totalBadgesEarned -#: src/features/admin/components/dashboard/tools-list.tsx:73 +#: src/features/admin/components/dashboard/tools-list.tsx:79 msgid "Last recalculation: {0, plural, one {# badge earned} other {# badges earned}}" msgstr "前回の再計算:{0, plural, one {# 個のバッジを獲得} other {# 個のバッジを獲得}}" @@ -1530,7 +1517,7 @@ msgstr "M" msgid "Mainline" msgstr "メインライン" -#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:114 +#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:95 msgid "Make Prediction" msgstr "予想する" @@ -1538,8 +1525,8 @@ msgstr "予想する" msgid "Make Your Prediction" msgstr "予想を入力" -#: src/features/admin/components/dashboard/tournament-card.tsx:68 -#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:89 +#: src/features/admin/components/dashboard/tournament-section.tsx:39 +#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:70 #: src/features/tournaments/components/upcoming-tournament/index.tsx:144 msgid "Manage" msgstr "管理" @@ -1554,8 +1541,7 @@ msgid "Manage Team Songs/Logos" msgstr "チームの曲・ロゴを管理" #: src/app/routes/_authed/admin/tournaments/index.tsx:17 -#: src/features/admin/components/dashboard/tools-list.tsx:49 -#: src/features/admin/components/dashboard/tournament-card.tsx:25 +#: src/features/admin/components/dashboard/tools-list.tsx:50 msgid "Manage Tournaments" msgstr "トーナメント管理" @@ -1642,7 +1628,7 @@ msgstr "MAU" msgid "Me" msgstr "自分" -#: src/features/admin/components/dashboard/tools-list.tsx:59 +#: src/features/admin/components/dashboard/tools-list.tsx:65 msgid "Migrate Badge Progress" msgstr "バッジ進捗を移行" @@ -1748,10 +1734,6 @@ msgstr "キャプチャする再生中のプレイバックがありません" msgid "No Activities Found" msgstr "アクティビティが見つかりません" -#: src/features/admin/components/dashboard/activity-feed.tsx:78 -msgid "No activity yet" -msgstr "まだアクティビティがありません" - #: src/features/badges/components/badge-stats-table.tsx:45 msgid "No Badges Available" msgstr "利用可能なバッジがありません" @@ -1844,15 +1826,10 @@ msgstr "「{search}」に一致するチームが見つかりません" msgid "No tournaments found" msgstr "トーナメントが見つかりません" -#: src/features/admin/components/dashboard/tournament-card.tsx:18 -msgid "No tournaments yet" -msgstr "トーナメントはまだありません" - #: src/features/tournaments/components/tournament-card-list.tsx:40 msgid "No Tournaments Yet" msgstr "トーナメントはまだありません" -#: src/features/admin/components/dashboard/errors-card.tsx:53 #: src/features/telemetry/components/error-browser/error-browser.tsx:27 msgid "No unresolved errors" msgstr "未解決のエラーはありません" @@ -1914,15 +1891,15 @@ msgstr "登録が完了すると、他のフリーエージェントとその電 msgid "One time code" msgstr "ワンタイムコード" -#: src/features/admin/components/dashboard/now-strip.tsx:44 -msgid "Online now" -msgstr "現在オンライン" +#: src/features/admin/components/dashboard/status-strip.tsx:56 +msgid "Online" +msgstr "オンライン" -#: src/features/admin/components/dashboard/now-strip.tsx:46 +#: src/features/admin/components/dashboard/status-strip.tsx:61 msgid "Open errors" msgstr "未解決のエラー" -#: src/features/admin/components/dashboard/tools-list.tsx:89 +#: src/features/admin/components/dashboard/tools-list.tsx:95 msgid "Open Pocketbase" msgstr "Pocketbaseを開く" @@ -1972,11 +1949,7 @@ msgstr "p50" msgid "p95" msgstr "p95" -#: src/features/admin/components/dashboard/usage-card.tsx:45 -msgid "Page views" -msgstr "ページビュー数" - -#: src/features/telemetry/components/usage-screen.tsx:177 +#: src/features/telemetry/components/usage-screen.tsx:176 msgid "Page views by route" msgstr "ルート別ページビュー数" @@ -2095,7 +2068,7 @@ msgstr "予想の得点方式:" #: src/app/routes/_authed/tournaments/$id.predictions.tsx:23 #: src/features/predictions/components/prediction-leaderboard.tsx:147 -#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:123 +#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:104 msgid "Predictions" msgstr "予想" @@ -2146,14 +2119,10 @@ msgstr "リアクション" #. placeholder {0}: migrationStatus.playersProcessed #. placeholder {1}: migrationStatus.totalPlayers -#: src/features/admin/components/dashboard/tools-list.tsx:66 +#: src/features/admin/components/dashboard/tools-list.tsx:72 msgid "Recalculating badges in the background ({0}/{1})" msgstr "バックグラウンドでバッジを再計算中({0}/{1})" -#: src/features/admin/components/dashboard/activity-feed.tsx:34 -msgid "Recent activity" -msgstr "最近のアクティビティ" - #: src/features/tournaments/components/started-tournament/sections/recent-results.tsx:34 msgid "Recently finished" msgstr "最近終了した試合" @@ -2218,17 +2187,20 @@ msgstr "ラウンド{0}" msgid "Route" msgstr "ルート" -#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:142 +#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:123 #: src/features/tournaments/components/upcoming-tournament/rules-list-button.tsx:23 msgid "Rules" msgstr "ルール" +#: src/features/admin/components/dashboard/tournament-section.tsx:29 +msgid "Run" +msgstr "実行" + #: src/app/routes/_authed/admin/tournaments/run.$id.tsx:36 msgid "Run {name}" msgstr "{name}を実行" -#: src/features/admin/components/dashboard/tournament-card.tsx:53 -#: src/features/tournaments/components/manage-tournament.tsx:49 +#: src/features/tournaments/components/manage-tournament.tsx:41 msgid "Run Tournament" msgstr "トーナメントを実行" @@ -2517,7 +2489,7 @@ msgstr "開始時間を入力してください" msgid "Start typing to search..." msgstr "入力して検索..." -#: src/features/admin/components/dashboard/tournament-card.tsx:43 +#: src/features/admin/components/dashboard/tournament-section.tsx:24 msgid "Starts in" msgstr "開始まで" @@ -2639,7 +2611,7 @@ msgstr "チームを更新しました!" #: src/features/players/components/profile/skeleton.tsx:75 #: src/features/tournaments/components/profile/index.tsx:32 #: src/features/tournaments/components/profile/skeleton.tsx:89 -#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:137 +#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:118 msgid "Teams" msgstr "チーム" @@ -2648,6 +2620,7 @@ msgid "Teams created successfully!" msgstr "チームを作成しました!" #: src/app/routes/_authed/admin/telemetry.tsx:20 +#: src/features/admin/components/dashboard/tools-list.tsx:55 msgid "Telemetry" msgstr "テレメトリー" @@ -2730,7 +2703,7 @@ msgstr "各グループの上位{0, plural, one {#チーム} other {#チーム}} msgid "Top {n} advance" msgstr "上位{n}チームが勝ち上がり" -#: src/features/telemetry/components/usage-screen.tsx:191 +#: src/features/telemetry/components/usage-screen.tsx:190 msgid "Top functions by call count" msgstr "呼び出し数の多い関数" @@ -2757,7 +2730,7 @@ msgstr "トーナメントを作成しました!" msgid "Tournament not found" msgstr "トーナメントが見つかりません" -#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:152 +#: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:133 #: src/features/tournaments/components/upcoming-tournament/rules-list-button.tsx:25 msgid "Tournament Rules" msgstr "トーナメントルール" @@ -2786,7 +2759,7 @@ msgstr "playTrackアクションにはtrackIdが必要です" msgid "Try the new themes" msgstr "新しいテーマを試してみましょう" -#: src/features/admin/components/dashboard/quiet-boundary.tsx:26 +#: src/features/admin/components/dashboard/quiet-boundary.tsx:24 msgid "Unavailable right now" msgstr "現在利用できません" @@ -2851,10 +2824,6 @@ msgstr "トーナメントを更新" msgid "Usage" msgstr "利用状況" -#: src/features/admin/components/dashboard/usage-card.tsx:34 -msgid "Usage, 7 days" -msgstr "利用状況(7日間)" - #: src/features/activities/components/activities-table.tsx:202 #: src/features/telemetry/components/client-events-table.tsx:146 msgid "User Agent" @@ -2898,7 +2867,7 @@ msgstr "予想を見る" msgid "Viewport" msgstr "ビューポート" -#: src/features/telemetry/components/usage-screen.tsx:211 +#: src/features/telemetry/components/usage-screen.tsx:213 msgid "Vitals" msgstr "バイタル" -- 2.54.0 From 7ea189aa61121d32c88da74b87a1f543d948cb7c Mon Sep 17 00:00:00 2001 From: yohlo <yohlerkyle@gmail.com> Date: Tue, 25 Aug 2026 22:26:54 -0700 Subject: [PATCH 12/33] fix(telemetry): stop logging beacon requests as activities --- src/app/routes/api/telemetry/beacon.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/app/routes/api/telemetry/beacon.ts b/src/app/routes/api/telemetry/beacon.ts index 9161560..8ca7ec4 100644 --- a/src/app/routes/api/telemetry/beacon.ts +++ b/src/app/routes/api/telemetry/beacon.ts @@ -1,6 +1,5 @@ import { createFileRoute } from "@tanstack/react-router"; import { superTokensOptionalRequestMiddleware } from "@/utils/supertokens"; -import { apiLoggingMiddleware } from "@/lib/telemetry/api-logging.server"; import { beaconPayloadSchema, MAX_BEACON_BYTES, type BeaconEvent } from "@/lib/telemetry/beacon-schema"; import { errorGroupHash } from "@/lib/telemetry/error-hash"; import { redactArguments } from "@/lib/telemetry/redact-args"; @@ -86,7 +85,7 @@ const writeEvents = async ( export const Route = createFileRoute("/api/telemetry/beacon")({ server: { - middleware: [apiLoggingMiddleware, superTokensOptionalRequestMiddleware], + middleware: [superTokensOptionalRequestMiddleware], handlers: { POST: async ({ request, context }) => { const player = (context as unknown as { player?: Player | null }).player ?? undefined; -- 2.54.0 From fd0489a22f97ac00423c89ac2e8a6bb72326cd59 Mon Sep 17 00:00:00 2001 From: yohlo <yohlerkyle@gmail.com> Date: Tue, 25 Aug 2026 22:35:16 -0700 Subject: [PATCH 13/33] refactor(telemetry): chart scale labels, calmer vitals and error styling, usage-first tabs - sparklines get start/end date and max-value footers - vitals rating shown by value color instead of badges - error rows use plain counts, subtle resolve button, terse empty state - telemetry tab order: usage, players, functions, events, errors; usage is default --- src/app/routes/_authed/admin/telemetry.tsx | 18 ++--- .../routes/_authed/admin/telemetry/index.tsx | 2 +- .../admin/components/dashboard/tools-list.tsx | 2 +- .../telemetry/components/charts/sparkline.tsx | 35 ++++++++- .../telemetry/components/charts/stat-tile.tsx | 21 ++++- .../error-browser/error-browser.tsx | 15 ++-- .../error-browser/error-group-row.tsx | 14 ++-- .../telemetry/components/usage-screen.tsx | 41 +++++----- src/locales/de/messages.po | 78 ++++++++----------- src/locales/en/messages.po | 78 ++++++++----------- src/locales/es/messages.po | 78 ++++++++----------- src/locales/ja/messages.po | 78 ++++++++----------- 12 files changed, 222 insertions(+), 238 deletions(-) diff --git a/src/app/routes/_authed/admin/telemetry.tsx b/src/app/routes/_authed/admin/telemetry.tsx index 6ba2abe..c4e6735 100644 --- a/src/app/routes/_authed/admin/telemetry.tsx +++ b/src/app/routes/_authed/admin/telemetry.tsx @@ -4,11 +4,11 @@ import { Trans } from "@lingui/react/macro"; import { msg } from "@lingui/core/macro"; const TAB_ROUTES = { + usage: "/admin/telemetry/usage", + players: "/admin/telemetry/players", functions: "/admin/telemetry/functions", events: "/admin/telemetry/events", errors: "/admin/telemetry/errors", - players: "/admin/telemetry/players", - usage: "/admin/telemetry/usage", } as const; type TabValue = keyof typeof TAB_ROUTES; @@ -29,7 +29,7 @@ function TelemetryLayout() { const activeTab = (Object.keys(TAB_ROUTES) as TabValue[]).find((tab) => pathname.endsWith(`/${tab}`) - ) ?? "functions"; + ) ?? "usage"; return ( <Tabs @@ -41,6 +41,12 @@ function TelemetryLayout() { }} > <Tabs.List mb="md" style={{ flexWrap: "nowrap", overflowX: "auto", overflowY: "hidden" }}> + <Tabs.Tab value="usage"> + <Trans>Usage</Trans> + </Tabs.Tab> + <Tabs.Tab value="players"> + <Trans>Players</Trans> + </Tabs.Tab> <Tabs.Tab value="functions"> <Trans>Functions</Trans> </Tabs.Tab> @@ -50,12 +56,6 @@ function TelemetryLayout() { <Tabs.Tab value="errors"> <Trans>Errors</Trans> </Tabs.Tab> - <Tabs.Tab value="players"> - <Trans>Players</Trans> - </Tabs.Tab> - <Tabs.Tab value="usage"> - <Trans>Usage</Trans> - </Tabs.Tab> </Tabs.List> <Outlet /> diff --git a/src/app/routes/_authed/admin/telemetry/index.tsx b/src/app/routes/_authed/admin/telemetry/index.tsx index e9553e9..67a876c 100644 --- a/src/app/routes/_authed/admin/telemetry/index.tsx +++ b/src/app/routes/_authed/admin/telemetry/index.tsx @@ -2,6 +2,6 @@ import { createFileRoute, redirect } from "@tanstack/react-router"; export const Route = createFileRoute("/_authed/admin/telemetry/")({ beforeLoad: () => { - throw redirect({ to: "/admin/telemetry/functions" }); + throw redirect({ to: "/admin/telemetry/usage" }); }, }); diff --git a/src/features/admin/components/dashboard/tools-list.tsx b/src/features/admin/components/dashboard/tools-list.tsx index c87b32b..0126df1 100644 --- a/src/features/admin/components/dashboard/tools-list.tsx +++ b/src/features/admin/components/dashboard/tools-list.tsx @@ -54,7 +54,7 @@ export const ToolsList = () => { <ListLink label={t`Telemetry`} Icon={PulseIcon} - to="/admin/telemetry/functions" + to="/admin/telemetry/usage" /> <ListLink label={t`Award Badges`} diff --git a/src/features/telemetry/components/charts/sparkline.tsx b/src/features/telemetry/components/charts/sparkline.tsx index 8014a15..987c06f 100644 --- a/src/features/telemetry/components/charts/sparkline.tsx +++ b/src/features/telemetry/components/charts/sparkline.tsx @@ -1,9 +1,15 @@ -import { Text } from "@mantine/core"; +import { Group, Text } from "@mantine/core"; import { niceExtent, pointsToPath } from "./chart-path"; const VIEW_WIDTH = 240; const PAD = 3; +export interface SparklineScale { + start?: string; + end?: string; + formatValue?: (value: number) => string; +} + interface SparklineProps { data: number[]; series2?: number[]; @@ -11,6 +17,7 @@ interface SparklineProps { showArea?: boolean; label?: string; color?: string; + scale?: SparklineScale; } export const Sparkline = ({ @@ -20,6 +27,7 @@ export const Sparkline = ({ showArea = false, label, color = "var(--mantine-color-anchor)", + scale, }: SparklineProps) => { if (data.length === 0) { return ( @@ -38,7 +46,11 @@ export const Sparkline = ({ ? `${path} L${VIEW_WIDTH - PAD},${height - PAD} L${PAD},${height - PAD} Z` : null; - return ( + const allValues = series2?.length ? [...data, ...series2] : data; + const max = Math.max(...allValues); + const formatValue = scale?.formatValue ?? ((value: number) => value.toLocaleString()); + + const svg = ( <svg viewBox={`0 0 ${VIEW_WIDTH} ${height}`} preserveAspectRatio="none" @@ -72,6 +84,25 @@ export const Sparkline = ({ )} </svg> ); + + if (!scale) return svg; + + return ( + <div> + {svg} + <Group justify="space-between" gap="xs" mt={2}> + <Text size="xs" c="dimmed"> + {scale.start} + </Text> + <Text size="xs" c="dimmed" style={{ fontVariantNumeric: "tabular-nums" }}> + ↑{formatValue(max)} + </Text> + <Text size="xs" c="dimmed"> + {scale.end} + </Text> + </Group> + </div> + ); }; export default Sparkline; diff --git a/src/features/telemetry/components/charts/stat-tile.tsx b/src/features/telemetry/components/charts/stat-tile.tsx index d0e22c2..4009dd5 100644 --- a/src/features/telemetry/components/charts/stat-tile.tsx +++ b/src/features/telemetry/components/charts/stat-tile.tsx @@ -1,21 +1,32 @@ import { ReactNode } from "react"; import { Box, Group, Stack, Text, UnstyledButton } from "@mantine/core"; -import Sparkline from "./sparkline"; +import Sparkline, { SparklineScale } from "./sparkline"; interface StatTileProps { label: ReactNode; value: ReactNode; + valueColor?: string; delta?: number; spark?: number[]; + sparkScale?: SparklineScale; caption?: ReactNode; onClick?: () => void; } -export const StatTile = ({ label, value, delta, spark, caption, onClick }: StatTileProps) => { +export const StatTile = ({ + label, + value, + valueColor, + delta, + spark, + sparkScale, + caption, + onClick, +}: StatTileProps) => { const content = ( <Stack gap={2}> <Group gap="xs" align="baseline" wrap="nowrap"> - <Text fz="xl" fw={700} lh={1.2}> + <Text fz="xl" fw={700} lh={1.2} c={valueColor}> {value} </Text> {delta !== undefined && delta !== 0 && ( @@ -29,7 +40,9 @@ export const StatTile = ({ label, value, delta, spark, caption, onClick }: StatT {label} </Text> {caption && <Box>{caption}</Box>} - {spark && spark.length > 0 && <Sparkline data={spark} height={28} />} + {spark && spark.length > 0 && ( + <Sparkline data={spark} height={28} scale={sparkScale} /> + )} </Stack> ); diff --git a/src/features/telemetry/components/error-browser/error-browser.tsx b/src/features/telemetry/components/error-browser/error-browser.tsx index ded83ce..c5cd1bd 100644 --- a/src/features/telemetry/components/error-browser/error-browser.tsx +++ b/src/features/telemetry/components/error-browser/error-browser.tsx @@ -1,9 +1,7 @@ import { Suspense, useMemo, useState } from "react"; -import { useLingui } from "@lingui/react/macro"; -import { Container, Divider, Group, Stack, Switch } from "@mantine/core"; -import { ChecksIcon } from "@phosphor-icons/react"; +import { Trans, useLingui } from "@lingui/react/macro"; +import { Container, Divider, Group, Stack, Switch, Text } from "@mantine/core"; import { ListSkeleton } from "@/components/skeletons"; -import EmptyState from "@/components/empty-state"; import { useClientErrorGroups } from "../../queries"; import { lastNDays } from "../../range"; import { ErrorGroupRow } from "./error-group-row"; @@ -17,16 +15,13 @@ const ErrorGroupList = ({ to: string; includeResolved: boolean; }) => { - const { t } = useLingui(); const { data: groups } = useClientErrorGroups({ from, to, includeResolved }); if (groups.length === 0) { return ( - <EmptyState - icon={<ChecksIcon size={32} />} - title={t`No unresolved errors`} - description={t`Nothing has broken in the last 30 days. Keep it up.`} - /> + <Text size="sm" c="dimmed" px="md" py="xl" ta="center"> + <Trans>No unresolved errors</Trans> + </Text> ); } diff --git a/src/features/telemetry/components/error-browser/error-group-row.tsx b/src/features/telemetry/components/error-browser/error-group-row.tsx index 9c260fa..1452aa5 100644 --- a/src/features/telemetry/components/error-browser/error-group-row.tsx +++ b/src/features/telemetry/components/error-browser/error-group-row.tsx @@ -1,7 +1,6 @@ import { useState } from "react"; import { Trans, useLingui } from "@lingui/react/macro"; import { - Badge, Box, Button, Code, @@ -42,9 +41,14 @@ export const ErrorGroupRow = ({ group }: { group: ClientErrorGroup }) => { > <Stack gap={4}> <Group gap="xs" wrap="nowrap"> - <Badge color={group.resolved ? "gray" : "red"} variant="light" size="sm"> - {group.count.toLocaleString()} - </Badge> + <Text + size="sm" + fw={700} + c={group.resolved ? "dimmed" : "red"} + style={{ flexShrink: 0, fontVariantNumeric: "tabular-nums" }} + > + {group.count.toLocaleString()}× + </Text> <Text size="sm" fw={600} lineClamp={2} style={{ minWidth: 0 }}> {group.sample.message} </Text> @@ -85,7 +89,7 @@ export const ErrorGroupRow = ({ group }: { group: ClientErrorGroup }) => { <Group justify="flex-end"> <Button size="xs" - variant="light" + variant="subtle" color={group.resolved ? "yellow" : "green"} loading={resolveMutation.isPending} onClick={() => diff --git a/src/features/telemetry/components/usage-screen.tsx b/src/features/telemetry/components/usage-screen.tsx index cd9bd42..ab20da7 100644 --- a/src/features/telemetry/components/usage-screen.tsx +++ b/src/features/telemetry/components/usage-screen.tsx @@ -1,7 +1,6 @@ import { Suspense, useMemo, useState } from "react"; import { Trans, useLingui } from "@lingui/react/macro"; import { - Badge, Divider, Group, SegmentedControl, @@ -45,25 +44,18 @@ const vitalRating = (name: VitalName, value: number): "good" | "ok" | "poor" => return "ok"; }; -const VitalBadge = ({ name, value }: { name: VitalName; value: number }) => { +const vitalColor = (name: VitalName, value: number): string | undefined => { const rating = vitalRating(name, value); - const color = rating === "good" ? "green" : rating === "ok" ? "yellow" : "red"; - return ( - <Badge color={color} variant="light" size="sm"> - {rating === "good" ? ( - <Trans>Good</Trans> - ) : rating === "ok" ? ( - <Trans>Needs improvement</Trans> - ) : ( - <Trans>Poor</Trans> - )} - </Badge> - ); + if (rating === "good") return undefined; + return rating === "ok" ? "yellow.8" : "red"; }; const formatVital = (name: VitalName, value: number) => name === "CLS" ? value.toFixed(2) : `${Math.round(value)}ms`; +const dayLabel = (i18n: { date: (d: Date, opts?: Intl.DateTimeFormatOptions) => string }, day: string) => + i18n.date(new Date(`${day}T12:00:00Z`), { month: "short", day: "numeric" }); + const topItems = (totals: Map<string, number>, onClick?: (dim: string) => void) => [...totals.entries()] .sort((a, b) => b[1] - a[1]) @@ -75,6 +67,7 @@ const topItems = (totals: Map<string, number>, onClick?: (dim: string) => void) })); const FnDurationDetail = ({ fn, from, to }: { fn: string; from: string; to: string }) => { + const { i18n } = useLingui(); const { data: rows } = useRollupRange({ metrics: ["server_fn.duration.p50", "server_fn.duration.p95"], from, @@ -87,7 +80,17 @@ const FnDurationDetail = ({ fn, from, to }: { fn: string; from: string; to: stri return ( <Stack gap="xs"> - <Sparkline data={p50} series2={p95} height={64} showArea /> + <Sparkline + data={p50} + series2={p95} + height={64} + showArea + scale={{ + start: dayLabel(i18n, from), + end: dayLabel(i18n, to), + formatValue: (value) => `${Math.round(value)}ms`, + }} + /> <Group gap="md"> <Group gap={6}> <svg width={18} height={8} aria-hidden> @@ -119,7 +122,7 @@ const FnDurationDetail = ({ fn, from, to }: { fn: string; from: string; to: stri }; const UsageSections = ({ from, to }: { from: string; to: string }) => { - const { t } = useLingui(); + const { t, i18n } = useLingui(); const [selectedFn, setSelectedFn] = useState<string | null>(null); const { isOpen: fnDetailOpened, open: openFnDetail, close: closeFnDetail } = useSheet(); @@ -159,6 +162,7 @@ const UsageSections = ({ from, to }: { from: string; to: string }) => { label={<Trans>Daily active players</Trans>} value={(dauSeries[dauSeries.length - 1] ?? 0).toLocaleString()} spark={dauSeries} + sparkScale={{ start: dayLabel(i18n, from), end: dayLabel(i18n, to) }} /> <SimpleGrid cols={3} spacing="sm"> <StatTile @@ -200,9 +204,10 @@ const UsageSections = ({ from, to }: { from: string; to: string }) => { <Trans>Errors</Trans> Client errors per day} + label={Client errors} value={errorSeries.reduce((sum, value) => sum + value, 0).toLocaleString()} spark={errorSeries} + sparkScale={{ start: dayLabel(i18n, from), end: dayLabel(i18n, to) }} /> @@ -218,7 +223,7 @@ const UsageSections = ({ from, to }: { from: string; to: string }) => { key={name} label={name} value={value !== undefined ? formatVital(name, value) : "—"} - caption={value !== undefined ? : undefined} + valueColor={value !== undefined ? vitalColor(name, value) : undefined} /> ))} diff --git a/src/locales/de/messages.po b/src/locales/de/messages.po index 398e94d..ce3d182 100644 --- a/src/locales/de/messages.po +++ b/src/locales/de/messages.po @@ -355,7 +355,7 @@ msgstr "2. Frühere Abgabe" msgid "2nd place" msgstr "2. Platz" -#: src/features/telemetry/components/usage-screen.tsx:258 +#: src/features/telemetry/components/usage-screen.tsx:263 msgid "30d" msgstr "30 T" @@ -371,7 +371,7 @@ msgstr "3. Platz" msgid "5s" msgstr "5 s" -#: src/features/telemetry/components/usage-screen.tsx:257 +#: src/features/telemetry/components/usage-screen.tsx:262 msgid "7d" msgstr "7 T" @@ -716,9 +716,9 @@ msgstr "Ausgewählten Spieler entfernen" msgid "Client error spike" msgstr "Anstieg von Client-Fehlern" -#: src/features/telemetry/components/usage-screen.tsx:203 -msgid "Client errors per day" -msgstr "Client-Fehler pro Tag" +#: src/features/telemetry/components/usage-screen.tsx:207 +msgid "Client errors" +msgstr "Client-Fehler" #: src/components/sheet/modal.tsx:35 #: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:150 @@ -875,7 +875,7 @@ msgstr "Becher erzielt" msgid "Custom Hex Code" msgstr "Benutzerdefinierter Hex-Code" -#: src/features/telemetry/components/usage-screen.tsx:159 +#: src/features/telemetry/components/usage-screen.tsx:162 msgid "Daily active players" msgstr "Täglich aktive Spieler" @@ -934,7 +934,7 @@ msgstr "Nachfolgende Matches wurden zurückgesetzt, da sich durch die Korrektur msgid "Duration" msgstr "Dauer" -#: src/features/telemetry/components/usage-screen.tsx:235 +#: src/features/telemetry/components/usage-screen.tsx:240 msgid "Duration p50 / p95" msgstr "Dauer p50 / p95" @@ -996,7 +996,7 @@ msgstr "Enddatum (optional)" msgid "End Match" msgstr "Match beenden" -#: src/features/telemetry/components/usage-screen.tsx:156 +#: src/features/telemetry/components/usage-screen.tsx:159 msgid "Engagement" msgstr "Engagement" @@ -1080,11 +1080,11 @@ msgstr "Gib deinen Namen ein" msgid "Enter your phone number" msgstr "Gib deine Telefonnummer ein" -#: src/features/telemetry/components/error-browser/error-group-row.tsx:29 +#: src/features/telemetry/components/error-browser/error-group-row.tsx:28 msgid "Error group reopened" msgstr "Fehlergruppe wieder geöffnet" -#: src/features/telemetry/components/error-browser/error-group-row.tsx:29 +#: src/features/telemetry/components/error-browser/error-group-row.tsx:28 msgid "Error group resolved" msgstr "Fehlergruppe behoben" @@ -1096,8 +1096,8 @@ msgstr "Fehlermeldung" msgid "Error: {errorMessage}" msgstr "Fehler: {errorMessage}" -#: src/app/routes/_authed/admin/telemetry.tsx:51 -#: src/features/telemetry/components/usage-screen.tsx:200 +#: src/app/routes/_authed/admin/telemetry.tsx:57 +#: src/features/telemetry/components/usage-screen.tsx:204 msgid "Errors" msgstr "Fehler" @@ -1109,7 +1109,7 @@ msgstr "Ereignisdetails" msgid "Event name" msgstr "Ereignisname" -#: src/app/routes/_authed/admin/telemetry.tsx:48 +#: src/app/routes/_authed/admin/telemetry.tsx:54 msgid "Events" msgstr "Ereignisse" @@ -1238,7 +1238,7 @@ msgstr "Free-Agent-Anmeldung" msgid "Free Agents" msgstr "Free Agents" -#: src/features/telemetry/components/usage-screen.tsx:228 +#: src/features/telemetry/components/usage-screen.tsx:233 msgid "Function" msgstr "Funktion" @@ -1246,7 +1246,7 @@ msgstr "Funktion" msgid "Function Name" msgstr "Funktionsname" -#: src/app/routes/_authed/admin/telemetry.tsx:45 +#: src/app/routes/_authed/admin/telemetry.tsx:51 msgid "Functions" msgstr "Funktionen" @@ -1264,10 +1264,6 @@ msgstr "Zurück" msgid "Go Back" msgstr "Zurück" -#: src/features/telemetry/components/usage-screen.tsx:54 -msgid "Good" -msgstr "Gut" - #: src/themes/seventies/manifest.ts:6 msgid "Groovy" msgstr "Groovy" @@ -1375,7 +1371,7 @@ msgstr "Wenn du zum ersten Mal dabei bist, wähle <0>Neuer Spieler, andernfa msgid "Important: Enrolling as a free agent does not guarantee a tournament spot. To secure a spot, one team member must register through the app and select a walkout song." msgstr "Wichtig: Die Anmeldung als Free Agent garantiert keinen Turnierplatz. Um dir einen Platz zu sichern, muss sich ein Teammitglied über die App registrieren und einen Walkout-Song auswählen." -#: src/features/telemetry/components/error-browser/error-browser.tsx:56 +#: src/features/telemetry/components/error-browser/error-browser.tsx:51 msgid "Include resolved" msgstr "Behobene einschließen" @@ -1552,7 +1548,7 @@ msgstr "Turniere verwalten" msgid "margin" msgstr "Differenz" -#: src/features/telemetry/components/error-browser/error-group-row.tsx:98 +#: src/features/telemetry/components/error-browser/error-group-row.tsx:102 msgid "Mark resolved" msgstr "Als behoben markieren" @@ -1620,7 +1616,7 @@ msgstr "Matchup" msgid "Matchup details" msgstr "Matchup-Details" -#: src/features/telemetry/components/usage-screen.tsx:169 +#: src/features/telemetry/components/usage-screen.tsx:173 msgid "MAU" msgstr "MAU" @@ -1666,10 +1662,6 @@ msgstr "Mindestens 2 Spieler nötig, um Teams zu erstellen" msgid "Need at least 4 teams to create a group stage format. Current team count: {0}" msgstr "Mindestens 4 Teams nötig, um eine Gruppenphase zu erstellen. Aktuelle Teamanzahl: {0}" -#: src/features/telemetry/components/usage-screen.tsx:56 -msgid "Needs improvement" -msgstr "Verbesserungswürdig" - #: src/features/reactions/components/emoji-picker.tsx:23 msgid "nerd" msgstr "Nerd" @@ -1830,7 +1822,7 @@ msgstr "Keine Turniere gefunden" msgid "No Tournaments Yet" msgstr "Noch keine Turniere" -#: src/features/telemetry/components/error-browser/error-browser.tsx:27 +#: src/features/telemetry/components/error-browser/error-browser.tsx:23 msgid "No unresolved errors" msgstr "Keine offenen Fehler" @@ -1846,10 +1838,6 @@ msgstr "Stimmt nicht" msgid "Not right — enter our score" msgstr "Stimmt nicht — unser Ergebnis eingeben" -#: src/features/telemetry/components/error-browser/error-browser.tsx:28 -msgid "Nothing has broken in the last 30 days. Keep it up." -msgstr "In den letzten 30 Tagen ist nichts kaputtgegangen. Weiter so!" - #: src/features/settings/components/notifications-section.tsx:18 #: src/features/settings/components/notifications-section.tsx:59 msgid "Notifications" @@ -1941,15 +1929,15 @@ msgstr "Übersicht" msgid "owo" msgstr "owo" -#: src/features/telemetry/components/usage-screen.tsx:97 +#: src/features/telemetry/components/usage-screen.tsx:100 msgid "p50" msgstr "p50" -#: src/features/telemetry/components/usage-screen.tsx:113 +#: src/features/telemetry/components/usage-screen.tsx:116 msgid "p95" msgstr "p95" -#: src/features/telemetry/components/usage-screen.tsx:176 +#: src/features/telemetry/components/usage-screen.tsx:180 msgid "Page views by route" msgstr "Seitenaufrufe nach Route" @@ -2036,7 +2024,7 @@ msgstr "Spieler erfolgreich entfernt" msgid "Player Stats" msgstr "Spielerstatistiken" -#: src/app/routes/_authed/admin/telemetry.tsx:54 +#: src/app/routes/_authed/admin/telemetry.tsx:48 #: src/features/teams/components/team-profile/index.tsx:29 msgid "Players" msgstr "Spieler" @@ -2050,10 +2038,6 @@ msgstr "Spieler sind erforderlich" msgid "Playing on {0}" msgstr "Wird auf {0} abgespielt" -#: src/features/telemetry/components/usage-screen.tsx:58 -msgid "Poor" -msgstr "Schlecht" - #: src/features/tournaments/components/group-stage-view.tsx:314 msgid "Populate Knockout Bracket" msgstr "K.-o.-Runde befüllen" @@ -2137,7 +2121,7 @@ msgstr "Regional" msgid "Regional Tournament" msgstr "Regionalturnier" -#: src/features/telemetry/components/error-browser/error-group-row.tsx:98 +#: src/features/telemetry/components/error-browser/error-group-row.tsx:102 msgid "Reopen" msgstr "Wieder öffnen" @@ -2154,7 +2138,7 @@ msgstr "Ergebnis melden" msgid "Request failed" msgstr "Anfrage fehlgeschlagen" -#: src/features/telemetry/components/error-browser/error-group-row.tsx:63 +#: src/features/telemetry/components/error-browser/error-group-row.tsx:67 msgid "Resolved" msgstr "Behoben" @@ -2359,7 +2343,7 @@ msgstr "Serie steht unentschieden" msgid "Server function failing" msgstr "Serverfunktion schlägt fehl" -#: src/features/telemetry/components/usage-screen.tsx:186 +#: src/features/telemetry/components/usage-screen.tsx:190 msgid "Server functions" msgstr "Serverfunktionen" @@ -2371,7 +2355,7 @@ msgstr "serverFn-Name" msgid "Session" msgstr "Sitzung" -#: src/features/telemetry/components/usage-screen.tsx:172 +#: src/features/telemetry/components/usage-screen.tsx:176 msgid "Sessions" msgstr "Sitzungen" @@ -2703,7 +2687,7 @@ msgstr "Die besten {0, plural, one {# Team} other {# Teams}} aus jeder Gruppe ko msgid "Top {n} advance" msgstr "Top {n} kommen weiter" -#: src/features/telemetry/components/usage-screen.tsx:190 +#: src/features/telemetry/components/usage-screen.tsx:194 msgid "Top functions by call count" msgstr "Top-Funktionen nach Aufrufanzahl" @@ -2820,7 +2804,7 @@ msgstr "Team aktualisieren" msgid "Update Tournament" msgstr "Turnier aktualisieren" -#: src/app/routes/_authed/admin/telemetry.tsx:57 +#: src/app/routes/_authed/admin/telemetry.tsx:45 msgid "Usage" msgstr "Nutzung" @@ -2867,7 +2851,7 @@ msgstr "Tipps ansehen" msgid "Viewport" msgstr "Viewport" -#: src/features/telemetry/components/usage-screen.tsx:213 +#: src/features/telemetry/components/usage-screen.tsx:218 msgid "Vitals" msgstr "Vitals" @@ -2906,7 +2890,7 @@ msgstr "Walkout-Segment ({segmentDuration}s)" msgid "Walkout Song" msgstr "Walkout-Song" -#: src/features/telemetry/components/usage-screen.tsx:165 +#: src/features/telemetry/components/usage-screen.tsx:169 msgid "WAU" msgstr "WAU" diff --git a/src/locales/en/messages.po b/src/locales/en/messages.po index ecd6291..bce234c 100644 --- a/src/locales/en/messages.po +++ b/src/locales/en/messages.po @@ -355,7 +355,7 @@ msgstr "2. Earlier submission" msgid "2nd place" msgstr "2nd place" -#: src/features/telemetry/components/usage-screen.tsx:258 +#: src/features/telemetry/components/usage-screen.tsx:263 msgid "30d" msgstr "30d" @@ -371,7 +371,7 @@ msgstr "3rd place" msgid "5s" msgstr "5s" -#: src/features/telemetry/components/usage-screen.tsx:257 +#: src/features/telemetry/components/usage-screen.tsx:262 msgid "7d" msgstr "7d" @@ -716,9 +716,9 @@ msgstr "Clear selected player" msgid "Client error spike" msgstr "Client error spike" -#: src/features/telemetry/components/usage-screen.tsx:203 -msgid "Client errors per day" -msgstr "Client errors per day" +#: src/features/telemetry/components/usage-screen.tsx:207 +msgid "Client errors" +msgstr "Client errors" #: src/components/sheet/modal.tsx:35 #: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:150 @@ -875,7 +875,7 @@ msgstr "Cups Made" msgid "Custom Hex Code" msgstr "Custom Hex Code" -#: src/features/telemetry/components/usage-screen.tsx:159 +#: src/features/telemetry/components/usage-screen.tsx:162 msgid "Daily active players" msgstr "Daily active players" @@ -934,7 +934,7 @@ msgstr "Downstream matches were reset because the correction changed who advance msgid "Duration" msgstr "Duration" -#: src/features/telemetry/components/usage-screen.tsx:235 +#: src/features/telemetry/components/usage-screen.tsx:240 msgid "Duration p50 / p95" msgstr "Duration p50 / p95" @@ -996,7 +996,7 @@ msgstr "End Date (Optional)" msgid "End Match" msgstr "End Match" -#: src/features/telemetry/components/usage-screen.tsx:156 +#: src/features/telemetry/components/usage-screen.tsx:159 msgid "Engagement" msgstr "Engagement" @@ -1080,11 +1080,11 @@ msgstr "Enter your name" msgid "Enter your phone number" msgstr "Enter your phone number" -#: src/features/telemetry/components/error-browser/error-group-row.tsx:29 +#: src/features/telemetry/components/error-browser/error-group-row.tsx:28 msgid "Error group reopened" msgstr "Error group reopened" -#: src/features/telemetry/components/error-browser/error-group-row.tsx:29 +#: src/features/telemetry/components/error-browser/error-group-row.tsx:28 msgid "Error group resolved" msgstr "Error group resolved" @@ -1096,8 +1096,8 @@ msgstr "Error Message" msgid "Error: {errorMessage}" msgstr "Error: {errorMessage}" -#: src/app/routes/_authed/admin/telemetry.tsx:51 -#: src/features/telemetry/components/usage-screen.tsx:200 +#: src/app/routes/_authed/admin/telemetry.tsx:57 +#: src/features/telemetry/components/usage-screen.tsx:204 msgid "Errors" msgstr "Errors" @@ -1109,7 +1109,7 @@ msgstr "Event Details" msgid "Event name" msgstr "Event name" -#: src/app/routes/_authed/admin/telemetry.tsx:48 +#: src/app/routes/_authed/admin/telemetry.tsx:54 msgid "Events" msgstr "Events" @@ -1238,7 +1238,7 @@ msgstr "Free Agent Enrollment" msgid "Free Agents" msgstr "Free Agents" -#: src/features/telemetry/components/usage-screen.tsx:228 +#: src/features/telemetry/components/usage-screen.tsx:233 msgid "Function" msgstr "Function" @@ -1246,7 +1246,7 @@ msgstr "Function" msgid "Function Name" msgstr "Function Name" -#: src/app/routes/_authed/admin/telemetry.tsx:45 +#: src/app/routes/_authed/admin/telemetry.tsx:51 msgid "Functions" msgstr "Functions" @@ -1264,10 +1264,6 @@ msgstr "Go back" msgid "Go Back" msgstr "Go Back" -#: src/features/telemetry/components/usage-screen.tsx:54 -msgid "Good" -msgstr "Good" - #: src/themes/seventies/manifest.ts:6 msgid "Groovy" msgstr "Groovy" @@ -1375,7 +1371,7 @@ msgstr "If this is your first time participating, please select <0>New PlayerNuevo jugador; si n msgid "Important: Enrolling as a free agent does not guarantee a tournament spot. To secure a spot, one team member must register through the app and select a walkout song." msgstr "Importante: inscribirte como agente libre no garantiza un lugar en el torneo. Para asegurar un lugar, un integrante del equipo debe registrarse en la app y elegir una canción de entrada." -#: src/features/telemetry/components/error-browser/error-browser.tsx:56 +#: src/features/telemetry/components/error-browser/error-browser.tsx:51 msgid "Include resolved" msgstr "Incluir resueltos" @@ -1552,7 +1548,7 @@ msgstr "Administrar torneos" msgid "margin" msgstr "margen" -#: src/features/telemetry/components/error-browser/error-group-row.tsx:98 +#: src/features/telemetry/components/error-browser/error-group-row.tsx:102 msgid "Mark resolved" msgstr "Marcar como resuelto" @@ -1620,7 +1616,7 @@ msgstr "Enfrentamiento" msgid "Matchup details" msgstr "Detalles del enfrentamiento" -#: src/features/telemetry/components/usage-screen.tsx:169 +#: src/features/telemetry/components/usage-screen.tsx:173 msgid "MAU" msgstr "MAU" @@ -1666,10 +1662,6 @@ msgstr "Se necesitan al menos 2 jugadores para crear equipos" msgid "Need at least 4 teams to create a group stage format. Current team count: {0}" msgstr "Se necesitan al menos 4 equipos para crear un formato de fase de grupos. Equipos actuales: {0}" -#: src/features/telemetry/components/usage-screen.tsx:56 -msgid "Needs improvement" -msgstr "Necesita mejorar" - #: src/features/reactions/components/emoji-picker.tsx:23 msgid "nerd" msgstr "nerd" @@ -1830,7 +1822,7 @@ msgstr "No se encontraron torneos" msgid "No Tournaments Yet" msgstr "Aún no hay torneos" -#: src/features/telemetry/components/error-browser/error-browser.tsx:27 +#: src/features/telemetry/components/error-browser/error-browser.tsx:23 msgid "No unresolved errors" msgstr "No hay errores sin resolver" @@ -1846,10 +1838,6 @@ msgstr "Incorrecto" msgid "Not right — enter our score" msgstr "Incorrecto — ingresa nuestro marcador" -#: src/features/telemetry/components/error-browser/error-browser.tsx:28 -msgid "Nothing has broken in the last 30 days. Keep it up." -msgstr "Nada se ha roto en los últimos 30 días. Sigue así." - #: src/features/settings/components/notifications-section.tsx:18 #: src/features/settings/components/notifications-section.tsx:59 msgid "Notifications" @@ -1941,15 +1929,15 @@ msgstr "Resumen" msgid "owo" msgstr "owo" -#: src/features/telemetry/components/usage-screen.tsx:97 +#: src/features/telemetry/components/usage-screen.tsx:100 msgid "p50" msgstr "p50" -#: src/features/telemetry/components/usage-screen.tsx:113 +#: src/features/telemetry/components/usage-screen.tsx:116 msgid "p95" msgstr "p95" -#: src/features/telemetry/components/usage-screen.tsx:176 +#: src/features/telemetry/components/usage-screen.tsx:180 msgid "Page views by route" msgstr "Vistas de página por ruta" @@ -2036,7 +2024,7 @@ msgstr "Jugador eliminado con éxito" msgid "Player Stats" msgstr "Estadísticas de jugador" -#: src/app/routes/_authed/admin/telemetry.tsx:54 +#: src/app/routes/_authed/admin/telemetry.tsx:48 #: src/features/teams/components/team-profile/index.tsx:29 msgid "Players" msgstr "Jugadores" @@ -2050,10 +2038,6 @@ msgstr "Los jugadores son obligatorios" msgid "Playing on {0}" msgstr "Reproduciendo en {0}" -#: src/features/telemetry/components/usage-screen.tsx:58 -msgid "Poor" -msgstr "Deficiente" - #: src/features/tournaments/components/group-stage-view.tsx:314 msgid "Populate Knockout Bracket" msgstr "Llenar el bracket eliminatorio" @@ -2137,7 +2121,7 @@ msgstr "Regional" msgid "Regional Tournament" msgstr "Torneo regional" -#: src/features/telemetry/components/error-browser/error-group-row.tsx:98 +#: src/features/telemetry/components/error-browser/error-group-row.tsx:102 msgid "Reopen" msgstr "Reabrir" @@ -2154,7 +2138,7 @@ msgstr "Reportar marcador" msgid "Request failed" msgstr "Falló la solicitud" -#: src/features/telemetry/components/error-browser/error-group-row.tsx:63 +#: src/features/telemetry/components/error-browser/error-group-row.tsx:67 msgid "Resolved" msgstr "Resuelto" @@ -2359,7 +2343,7 @@ msgstr "La serie está empatada" msgid "Server function failing" msgstr "Función del servidor fallando" -#: src/features/telemetry/components/usage-screen.tsx:186 +#: src/features/telemetry/components/usage-screen.tsx:190 msgid "Server functions" msgstr "Funciones del servidor" @@ -2371,7 +2355,7 @@ msgstr "nombre de serverFn" msgid "Session" msgstr "Sesión" -#: src/features/telemetry/components/usage-screen.tsx:172 +#: src/features/telemetry/components/usage-screen.tsx:176 msgid "Sessions" msgstr "Sesiones" @@ -2703,7 +2687,7 @@ msgstr "{0, plural, one {El mejor equipo de cada grupo avanzará} other {Los mej msgid "Top {n} advance" msgstr "Los mejores {n} avanzan" -#: src/features/telemetry/components/usage-screen.tsx:190 +#: src/features/telemetry/components/usage-screen.tsx:194 msgid "Top functions by call count" msgstr "Funciones principales por cantidad de llamadas" @@ -2820,7 +2804,7 @@ msgstr "Actualizar equipo" msgid "Update Tournament" msgstr "Actualizar torneo" -#: src/app/routes/_authed/admin/telemetry.tsx:57 +#: src/app/routes/_authed/admin/telemetry.tsx:45 msgid "Usage" msgstr "Uso" @@ -2867,7 +2851,7 @@ msgstr "Ver predicciones" msgid "Viewport" msgstr "Viewport" -#: src/features/telemetry/components/usage-screen.tsx:213 +#: src/features/telemetry/components/usage-screen.tsx:218 msgid "Vitals" msgstr "Vitales" @@ -2906,7 +2890,7 @@ msgstr "Segmento de entrada ({segmentDuration}s)" msgid "Walkout Song" msgstr "Canción de entrada" -#: src/features/telemetry/components/usage-screen.tsx:165 +#: src/features/telemetry/components/usage-screen.tsx:169 msgid "WAU" msgstr "WAU" diff --git a/src/locales/ja/messages.po b/src/locales/ja/messages.po index b46b4ac..289f538 100644 --- a/src/locales/ja/messages.po +++ b/src/locales/ja/messages.po @@ -355,7 +355,7 @@ msgstr "2. 早い提出" msgid "2nd place" msgstr "2位" -#: src/features/telemetry/components/usage-screen.tsx:258 +#: src/features/telemetry/components/usage-screen.tsx:263 msgid "30d" msgstr "30日" @@ -371,7 +371,7 @@ msgstr "3位" msgid "5s" msgstr "5秒" -#: src/features/telemetry/components/usage-screen.tsx:257 +#: src/features/telemetry/components/usage-screen.tsx:262 msgid "7d" msgstr "7日" @@ -716,9 +716,9 @@ msgstr "選択したプレイヤーを解除" msgid "Client error spike" msgstr "クライアントエラー急増" -#: src/features/telemetry/components/usage-screen.tsx:203 -msgid "Client errors per day" -msgstr "1日あたりのクライアントエラー数" +#: src/features/telemetry/components/usage-screen.tsx:207 +msgid "Client errors" +msgstr "クライアントエラー" #: src/components/sheet/modal.tsx:35 #: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:150 @@ -875,7 +875,7 @@ msgstr "決めたカップ" msgid "Custom Hex Code" msgstr "カスタムHEXコード" -#: src/features/telemetry/components/usage-screen.tsx:159 +#: src/features/telemetry/components/usage-screen.tsx:162 msgid "Daily active players" msgstr "デイリーアクティブプレイヤー数" @@ -934,7 +934,7 @@ msgstr "修正により勝ち上がりの相手が変わったため、以降の msgid "Duration" msgstr "所要時間" -#: src/features/telemetry/components/usage-screen.tsx:235 +#: src/features/telemetry/components/usage-screen.tsx:240 msgid "Duration p50 / p95" msgstr "所要時間 p50 / p95" @@ -996,7 +996,7 @@ msgstr "終了日(任意)" msgid "End Match" msgstr "試合を終了" -#: src/features/telemetry/components/usage-screen.tsx:156 +#: src/features/telemetry/components/usage-screen.tsx:159 msgid "Engagement" msgstr "エンゲージメント" @@ -1080,11 +1080,11 @@ msgstr "名前を入力" msgid "Enter your phone number" msgstr "電話番号を入力" -#: src/features/telemetry/components/error-browser/error-group-row.tsx:29 +#: src/features/telemetry/components/error-browser/error-group-row.tsx:28 msgid "Error group reopened" msgstr "エラーグループを再オープンしました" -#: src/features/telemetry/components/error-browser/error-group-row.tsx:29 +#: src/features/telemetry/components/error-browser/error-group-row.tsx:28 msgid "Error group resolved" msgstr "エラーグループを解決済みにしました" @@ -1096,8 +1096,8 @@ msgstr "エラーメッセージ" msgid "Error: {errorMessage}" msgstr "エラー: {errorMessage}" -#: src/app/routes/_authed/admin/telemetry.tsx:51 -#: src/features/telemetry/components/usage-screen.tsx:200 +#: src/app/routes/_authed/admin/telemetry.tsx:57 +#: src/features/telemetry/components/usage-screen.tsx:204 msgid "Errors" msgstr "エラー" @@ -1109,7 +1109,7 @@ msgstr "イベント詳細" msgid "Event name" msgstr "イベント名" -#: src/app/routes/_authed/admin/telemetry.tsx:48 +#: src/app/routes/_authed/admin/telemetry.tsx:54 msgid "Events" msgstr "イベント" @@ -1238,7 +1238,7 @@ msgstr "フリーエージェント登録" msgid "Free Agents" msgstr "フリーエージェント" -#: src/features/telemetry/components/usage-screen.tsx:228 +#: src/features/telemetry/components/usage-screen.tsx:233 msgid "Function" msgstr "関数" @@ -1246,7 +1246,7 @@ msgstr "関数" msgid "Function Name" msgstr "関数名" -#: src/app/routes/_authed/admin/telemetry.tsx:45 +#: src/app/routes/_authed/admin/telemetry.tsx:51 msgid "Functions" msgstr "関数" @@ -1264,10 +1264,6 @@ msgstr "戻る" msgid "Go Back" msgstr "戻る" -#: src/features/telemetry/components/usage-screen.tsx:54 -msgid "Good" -msgstr "良好" - #: src/themes/seventies/manifest.ts:6 msgid "Groovy" msgstr "グルーヴィー" @@ -1375,7 +1371,7 @@ msgstr "初めて参加する場合は<0>新しいプレイヤーを、そ msgid "Important: Enrolling as a free agent does not guarantee a tournament spot. To secure a spot, one team member must register through the app and select a walkout song." msgstr "重要: フリーエージェントとして登録しても、トーナメントへの参加枠は保証されません。参加枠を確保するには、チームメンバーの1人がアプリから登録し、ウォークアウトソングを選択する必要があります。" -#: src/features/telemetry/components/error-browser/error-browser.tsx:56 +#: src/features/telemetry/components/error-browser/error-browser.tsx:51 msgid "Include resolved" msgstr "解決済みを含める" @@ -1552,7 +1548,7 @@ msgstr "トーナメント管理" msgid "margin" msgstr "得点差" -#: src/features/telemetry/components/error-browser/error-group-row.tsx:98 +#: src/features/telemetry/components/error-browser/error-group-row.tsx:102 msgid "Mark resolved" msgstr "解決済みにする" @@ -1620,7 +1616,7 @@ msgstr "対戦カード" msgid "Matchup details" msgstr "対戦カードの詳細" -#: src/features/telemetry/components/usage-screen.tsx:169 +#: src/features/telemetry/components/usage-screen.tsx:173 msgid "MAU" msgstr "MAU" @@ -1666,10 +1662,6 @@ msgstr "チームを作成するには2人以上のプレイヤーが必要で msgid "Need at least 4 teams to create a group stage format. Current team count: {0}" msgstr "グループステージ形式を作成するには4チーム以上が必要です。現在のチーム数: {0}" -#: src/features/telemetry/components/usage-screen.tsx:56 -msgid "Needs improvement" -msgstr "要改善" - #: src/features/reactions/components/emoji-picker.tsx:23 msgid "nerd" msgstr "オタク" @@ -1830,7 +1822,7 @@ msgstr "トーナメントが見つかりません" msgid "No Tournaments Yet" msgstr "トーナメントはまだありません" -#: src/features/telemetry/components/error-browser/error-browser.tsx:27 +#: src/features/telemetry/components/error-browser/error-browser.tsx:23 msgid "No unresolved errors" msgstr "未解決のエラーはありません" @@ -1846,10 +1838,6 @@ msgstr "違う" msgid "Not right — enter our score" msgstr "違う — スコアを入力" -#: src/features/telemetry/components/error-browser/error-browser.tsx:28 -msgid "Nothing has broken in the last 30 days. Keep it up." -msgstr "過去30日間、何も壊れていません。この調子です。" - #: src/features/settings/components/notifications-section.tsx:18 #: src/features/settings/components/notifications-section.tsx:59 msgid "Notifications" @@ -1941,15 +1929,15 @@ msgstr "概要" msgid "owo" msgstr "owo" -#: src/features/telemetry/components/usage-screen.tsx:97 +#: src/features/telemetry/components/usage-screen.tsx:100 msgid "p50" msgstr "p50" -#: src/features/telemetry/components/usage-screen.tsx:113 +#: src/features/telemetry/components/usage-screen.tsx:116 msgid "p95" msgstr "p95" -#: src/features/telemetry/components/usage-screen.tsx:176 +#: src/features/telemetry/components/usage-screen.tsx:180 msgid "Page views by route" msgstr "ルート別ページビュー数" @@ -2036,7 +2024,7 @@ msgstr "プレイヤーを削除しました" msgid "Player Stats" msgstr "プレイヤースタッツ" -#: src/app/routes/_authed/admin/telemetry.tsx:54 +#: src/app/routes/_authed/admin/telemetry.tsx:48 #: src/features/teams/components/team-profile/index.tsx:29 msgid "Players" msgstr "プレイヤー" @@ -2050,10 +2038,6 @@ msgstr "プレイヤーの選択が必要です" msgid "Playing on {0}" msgstr "{0}で再生中" -#: src/features/telemetry/components/usage-screen.tsx:58 -msgid "Poor" -msgstr "不良" - #: src/features/tournaments/components/group-stage-view.tsx:314 msgid "Populate Knockout Bracket" msgstr "ノックアウトブラケットを作成" @@ -2137,7 +2121,7 @@ msgstr "地域" msgid "Regional Tournament" msgstr "地域トーナメント" -#: src/features/telemetry/components/error-browser/error-group-row.tsx:98 +#: src/features/telemetry/components/error-browser/error-group-row.tsx:102 msgid "Reopen" msgstr "再オープン" @@ -2154,7 +2138,7 @@ msgstr "スコアを報告" msgid "Request failed" msgstr "リクエストに失敗しました" -#: src/features/telemetry/components/error-browser/error-group-row.tsx:63 +#: src/features/telemetry/components/error-browser/error-group-row.tsx:67 msgid "Resolved" msgstr "解決済み" @@ -2359,7 +2343,7 @@ msgstr "シリーズはタイです" msgid "Server function failing" msgstr "サーバー関数が失敗しています" -#: src/features/telemetry/components/usage-screen.tsx:186 +#: src/features/telemetry/components/usage-screen.tsx:190 msgid "Server functions" msgstr "サーバー関数" @@ -2371,7 +2355,7 @@ msgstr "serverFn名" msgid "Session" msgstr "セッション" -#: src/features/telemetry/components/usage-screen.tsx:172 +#: src/features/telemetry/components/usage-screen.tsx:176 msgid "Sessions" msgstr "セッション数" @@ -2703,7 +2687,7 @@ msgstr "各グループの上位{0, plural, one {#チーム} other {#チーム}} msgid "Top {n} advance" msgstr "上位{n}チームが勝ち上がり" -#: src/features/telemetry/components/usage-screen.tsx:190 +#: src/features/telemetry/components/usage-screen.tsx:194 msgid "Top functions by call count" msgstr "呼び出し数の多い関数" @@ -2820,7 +2804,7 @@ msgstr "チームを更新" msgid "Update Tournament" msgstr "トーナメントを更新" -#: src/app/routes/_authed/admin/telemetry.tsx:57 +#: src/app/routes/_authed/admin/telemetry.tsx:45 msgid "Usage" msgstr "利用状況" @@ -2867,7 +2851,7 @@ msgstr "予想を見る" msgid "Viewport" msgstr "ビューポート" -#: src/features/telemetry/components/usage-screen.tsx:213 +#: src/features/telemetry/components/usage-screen.tsx:218 msgid "Vitals" msgstr "バイタル" @@ -2906,7 +2890,7 @@ msgstr "ウォークアウト区間({segmentDuration}秒)" msgid "Walkout Song" msgstr "ウォークアウトソング" -#: src/features/telemetry/components/usage-screen.tsx:165 +#: src/features/telemetry/components/usage-screen.tsx:169 msgid "WAU" msgstr "WAU" -- 2.54.0 From 5d7747839c7d6f999043df5ab25393fcb9a00314 Mon Sep 17 00:00:00 2001 From: yohlo Date: Tue, 25 Aug 2026 22:38:45 -0700 Subject: [PATCH 14/33] refactor(telemetry): text-only list empty states, distinct p50/p95 series - paginated-list empty prop is a plain string, no icon block - second sparkline series drawn in dimmed color; area disabled on dual charts - duration legend shows latest p50/p95 values --- .../components/activities-table.tsx | 4 +- .../telemetry/components/charts/sparkline.tsx | 6 +- .../components/client-events-table.tsx | 3 +- .../components/shared/paginated-list.tsx | 9 +-- .../telemetry/components/usage-screen.tsx | 9 +-- src/locales/de/messages.po | 64 +++++++++---------- src/locales/en/messages.po | 64 +++++++++---------- src/locales/es/messages.po | 64 +++++++++---------- src/locales/ja/messages.po | 64 +++++++++---------- 9 files changed, 145 insertions(+), 142 deletions(-) diff --git a/src/features/activities/components/activities-table.tsx b/src/features/activities/components/activities-table.tsx index 2a37fa7..45dd0ab 100644 --- a/src/features/activities/components/activities-table.tsx +++ b/src/features/activities/components/activities-table.tsx @@ -1,7 +1,7 @@ import { useState, useMemo } from "react"; import { Trans, Plural, useLingui } from "@lingui/react/macro"; import { Alert, Code, Container, Group, Stack, Text } from "@mantine/core"; -import { CheckIcon, XIcon, PulseIcon } from "@phosphor-icons/react"; +import { CheckIcon, XIcon } from "@phosphor-icons/react"; import { Activity, ActivitySearchParams } from "../types"; import { useActivities } from "../queries"; import { usePlayers } from "@/features/players/queries"; @@ -129,7 +129,7 @@ export const ActivitiesTable = () => { activity.id} - empty={{ icon: , title: t`No Activities Found` }} + empty={t`No Activities Found`} page={page} totalPages={result.totalPages} onPageChange={setPage} diff --git a/src/features/telemetry/components/charts/sparkline.tsx b/src/features/telemetry/components/charts/sparkline.tsx index 987c06f..3aba675 100644 --- a/src/features/telemetry/components/charts/sparkline.tsx +++ b/src/features/telemetry/components/charts/sparkline.tsx @@ -17,6 +17,7 @@ interface SparklineProps { showArea?: boolean; label?: string; color?: string; + series2Color?: string; scale?: SparklineScale; } @@ -27,6 +28,7 @@ export const Sparkline = ({ showArea = false, label, color = "var(--mantine-color-anchor)", + series2Color = "var(--mantine-color-dimmed)", scale, }: SparklineProps) => { if (data.length === 0) { @@ -42,7 +44,7 @@ export const Sparkline = ({ const secondPath = series2?.length ? pointsToPath(series2, VIEW_WIDTH, height, PAD, extent) : null; - const areaPath = showArea + const areaPath = showArea && !secondPath ? `${path} L${VIEW_WIDTH - PAD},${height - PAD} L${PAD},${height - PAD} Z` : null; @@ -74,7 +76,7 @@ export const Sparkline = ({ { event.id} - empty={{ icon: , title: t`No Events Found` }} + empty={t`No Events Found`} page={page} totalPages={result.totalPages} onPageChange={setPage} diff --git a/src/features/telemetry/components/shared/paginated-list.tsx b/src/features/telemetry/components/shared/paginated-list.tsx index 63f6d20..c44d097 100644 --- a/src/features/telemetry/components/shared/paginated-list.tsx +++ b/src/features/telemetry/components/shared/paginated-list.tsx @@ -1,12 +1,11 @@ import { ReactNode } from "react"; -import { Box, Divider, Group, Pagination, Stack } from "@mantine/core"; -import EmptyState from "@/components/empty-state"; +import { Box, Divider, Group, Pagination, Stack, Text } from "@mantine/core"; interface PaginatedListProps { items: T[]; renderItem: (item: T) => ReactNode; keyOf: (item: T) => string; - empty: { icon: ReactNode; title: string; description?: string }; + empty: string; page?: number; totalPages?: number; onPageChange?: (page: number) => void; @@ -32,7 +31,9 @@ export const PaginatedList = ({
{items.length === 0 && ( - + + {empty} + )} {totalPages > 1 && onPageChange && ( diff --git a/src/features/telemetry/components/usage-screen.tsx b/src/features/telemetry/components/usage-screen.tsx index ab20da7..825e54b 100644 --- a/src/features/telemetry/components/usage-screen.tsx +++ b/src/features/telemetry/components/usage-screen.tsx @@ -77,6 +77,8 @@ const FnDurationDetail = ({ fn, from, to }: { fn: string; from: string; to: stri const days = listDays({ from, to }); const p50 = fillDailySeries(rows, "server_fn.duration.p50", fn, days); const p95 = fillDailySeries(rows, "server_fn.duration.p95", fn, days); + const lastNonZero = (series: number[]) => + [...series].reverse().find((value) => value > 0) ?? 0; return ( @@ -84,7 +86,6 @@ const FnDurationDetail = ({ fn, from, to }: { fn: string; from: string; to: stri data={p50} series2={p95} height={64} - showArea scale={{ start: dayLabel(i18n, from), end: dayLabel(i18n, to), @@ -97,7 +98,7 @@ const FnDurationDetail = ({ fn, from, to }: { fn: string; from: string; to: stri - p50 + p50 · {Math.round(lastNonZero(p50))}ms @@ -107,13 +108,13 @@ const FnDurationDetail = ({ fn, from, to }: { fn: string; from: string; to: stri y1={4} x2={18} y2={4} - stroke="var(--mantine-color-anchor)" + stroke="var(--mantine-color-dimmed)" strokeWidth={2} strokeDasharray="4 3" /> - p95 + p95 · {Math.round(lastNonZero(p95))}ms diff --git a/src/locales/de/messages.po b/src/locales/de/messages.po index ce3d182..a6e83e1 100644 --- a/src/locales/de/messages.po +++ b/src/locales/de/messages.po @@ -58,7 +58,7 @@ msgid "{0, plural, one {# total activity} other {# total activities}}" msgstr "{0, plural, one {# Aktivität insgesamt} other {# Aktivitäten insgesamt}}" #. placeholder {0}: result.totalItems -#: src/features/telemetry/components/client-events-table.tsx:87 +#: src/features/telemetry/components/client-events-table.tsx:86 msgid "{0, plural, one {# total event} other {# total events}}" msgstr "{0, plural, one {# Ereignis insgesamt} other {# Ereignisse insgesamt}}" @@ -355,7 +355,7 @@ msgstr "2. Frühere Abgabe" msgid "2nd place" msgstr "2. Platz" -#: src/features/telemetry/components/usage-screen.tsx:263 +#: src/features/telemetry/components/usage-screen.tsx:264 msgid "30d" msgstr "30 T" @@ -371,7 +371,7 @@ msgstr "3. Platz" msgid "5s" msgstr "5 s" -#: src/features/telemetry/components/usage-screen.tsx:262 +#: src/features/telemetry/components/usage-screen.tsx:263 msgid "7d" msgstr "7 T" @@ -480,7 +480,7 @@ msgstr "Ein unerwarteter Fehler ist aufgetreten. Bitte versuche es erneut." msgid "Announce matchup" msgstr "Matchup ansagen" -#: src/features/telemetry/components/client-events-table.tsx:49 +#: src/features/telemetry/components/client-events-table.tsx:48 msgid "Anonymous" msgstr "Anonym" @@ -716,7 +716,7 @@ msgstr "Ausgewählten Spieler entfernen" msgid "Client error spike" msgstr "Anstieg von Client-Fehlern" -#: src/features/telemetry/components/usage-screen.tsx:207 +#: src/features/telemetry/components/usage-screen.tsx:208 msgid "Client errors" msgstr "Client-Fehler" @@ -846,7 +846,7 @@ msgid "Create Tournament" msgstr "Turnier erstellen" #: src/features/activities/components/activities-table.tsx:198 -#: src/features/telemetry/components/client-events-table.tsx:149 +#: src/features/telemetry/components/client-events-table.tsx:148 msgid "Created" msgstr "Erstellt" @@ -875,7 +875,7 @@ msgstr "Becher erzielt" msgid "Custom Hex Code" msgstr "Benutzerdefinierter Hex-Code" -#: src/features/telemetry/components/usage-screen.tsx:162 +#: src/features/telemetry/components/usage-screen.tsx:163 msgid "Daily active players" msgstr "Täglich aktive Spieler" @@ -934,7 +934,7 @@ msgstr "Nachfolgende Matches wurden zurückgesetzt, da sich durch die Korrektur msgid "Duration" msgstr "Dauer" -#: src/features/telemetry/components/usage-screen.tsx:240 +#: src/features/telemetry/components/usage-screen.tsx:241 msgid "Duration p50 / p95" msgstr "Dauer p50 / p95" @@ -996,7 +996,7 @@ msgstr "Enddatum (optional)" msgid "End Match" msgstr "Match beenden" -#: src/features/telemetry/components/usage-screen.tsx:159 +#: src/features/telemetry/components/usage-screen.tsx:160 msgid "Engagement" msgstr "Engagement" @@ -1097,15 +1097,15 @@ msgid "Error: {errorMessage}" msgstr "Fehler: {errorMessage}" #: src/app/routes/_authed/admin/telemetry.tsx:57 -#: src/features/telemetry/components/usage-screen.tsx:204 +#: src/features/telemetry/components/usage-screen.tsx:205 msgid "Errors" msgstr "Fehler" -#: src/features/telemetry/components/client-events-table.tsx:113 +#: src/features/telemetry/components/client-events-table.tsx:112 msgid "Event Details" msgstr "Ereignisdetails" -#: src/features/telemetry/components/client-events-table.tsx:70 +#: src/features/telemetry/components/client-events-table.tsx:69 msgid "Event name" msgstr "Ereignisname" @@ -1238,7 +1238,7 @@ msgstr "Free-Agent-Anmeldung" msgid "Free Agents" msgstr "Free Agents" -#: src/features/telemetry/components/usage-screen.tsx:233 +#: src/features/telemetry/components/usage-screen.tsx:234 msgid "Function" msgstr "Funktion" @@ -1616,7 +1616,7 @@ msgstr "Matchup" msgid "Matchup details" msgstr "Matchup-Details" -#: src/features/telemetry/components/usage-screen.tsx:173 +#: src/features/telemetry/components/usage-screen.tsx:174 msgid "MAU" msgstr "MAU" @@ -1638,7 +1638,7 @@ msgid "MMR Calculation:" msgstr "MMR-Berechnung:" #: src/features/teams/components/team-form/index.tsx:200 -#: src/features/telemetry/components/client-events-table.tsx:119 +#: src/features/telemetry/components/client-events-table.tsx:118 #: src/features/tournaments/components/tournament-form.tsx:137 msgid "Name" msgstr "Name" @@ -1734,7 +1734,7 @@ msgstr "Keine Abzeichen verfügbar" msgid "No devices found." msgstr "Keine Geräte gefunden." -#: src/features/telemetry/components/client-events-table.tsx:94 +#: src/features/telemetry/components/client-events-table.tsx:93 msgid "No Events Found" msgstr "Keine Ereignisse gefunden" @@ -1929,15 +1929,15 @@ msgstr "Übersicht" msgid "owo" msgstr "owo" -#: src/features/telemetry/components/usage-screen.tsx:100 +#: src/features/telemetry/components/usage-screen.tsx:101 msgid "p50" msgstr "p50" -#: src/features/telemetry/components/usage-screen.tsx:116 +#: src/features/telemetry/components/usage-screen.tsx:117 msgid "p95" msgstr "p95" -#: src/features/telemetry/components/usage-screen.tsx:180 +#: src/features/telemetry/components/usage-screen.tsx:181 msgid "Page views by route" msgstr "Seitenaufrufe nach Route" @@ -1949,7 +1949,7 @@ msgstr "Partner-Zuweisungen" msgid "Partners will be randomly assigned when enrollment closes" msgstr "Partner werden nach Anmeldeschluss zufällig zugewiesen" -#: src/features/telemetry/components/client-events-table.tsx:122 +#: src/features/telemetry/components/client-events-table.tsx:121 msgid "Path" msgstr "Pfad" @@ -1995,8 +1995,8 @@ msgstr "Wiedergabesteuerung fehlgeschlagen" #: src/features/activities/components/activities-table.tsx:98 #: src/features/activities/components/activities-table.tsx:188 #: src/features/admin/components/award-badges.tsx:31 -#: src/features/telemetry/components/client-events-table.tsx:73 -#: src/features/telemetry/components/client-events-table.tsx:120 +#: src/features/telemetry/components/client-events-table.tsx:72 +#: src/features/telemetry/components/client-events-table.tsx:119 msgid "Player" msgstr "Spieler" @@ -2076,7 +2076,7 @@ msgstr "Profil" msgid "Progress " msgstr "Fortschritt " -#: src/features/telemetry/components/client-events-table.tsx:136 +#: src/features/telemetry/components/client-events-table.tsx:135 msgid "Props" msgstr "Props" @@ -2167,7 +2167,7 @@ msgstr "rofl" msgid "Round {0}" msgstr "Runde {0}" -#: src/features/telemetry/components/client-events-table.tsx:125 +#: src/features/telemetry/components/client-events-table.tsx:124 msgid "Route" msgstr "Route" @@ -2343,7 +2343,7 @@ msgstr "Serie steht unentschieden" msgid "Server function failing" msgstr "Serverfunktion schlägt fehl" -#: src/features/telemetry/components/usage-screen.tsx:190 +#: src/features/telemetry/components/usage-screen.tsx:191 msgid "Server functions" msgstr "Serverfunktionen" @@ -2351,11 +2351,11 @@ msgstr "Serverfunktionen" msgid "serverFn name" msgstr "serverFn-Name" -#: src/features/telemetry/components/client-events-table.tsx:128 +#: src/features/telemetry/components/client-events-table.tsx:127 msgid "Session" msgstr "Sitzung" -#: src/features/telemetry/components/usage-screen.tsx:176 +#: src/features/telemetry/components/usage-screen.tsx:177 msgid "Sessions" msgstr "Sitzungen" @@ -2687,7 +2687,7 @@ msgstr "Die besten {0, plural, one {# Team} other {# Teams}} aus jeder Gruppe ko msgid "Top {n} advance" msgstr "Top {n} kommen weiter" -#: src/features/telemetry/components/usage-screen.tsx:194 +#: src/features/telemetry/components/usage-screen.tsx:195 msgid "Top functions by call count" msgstr "Top-Funktionen nach Aufrufanzahl" @@ -2809,11 +2809,11 @@ msgid "Usage" msgstr "Nutzung" #: src/features/activities/components/activities-table.tsx:202 -#: src/features/telemetry/components/client-events-table.tsx:146 +#: src/features/telemetry/components/client-events-table.tsx:145 msgid "User Agent" msgstr "User-Agent" -#: src/features/telemetry/components/client-events-table.tsx:131 +#: src/features/telemetry/components/client-events-table.tsx:130 msgid "Value" msgstr "Wert" @@ -2851,7 +2851,7 @@ msgstr "Tipps ansehen" msgid "Viewport" msgstr "Viewport" -#: src/features/telemetry/components/usage-screen.tsx:218 +#: src/features/telemetry/components/usage-screen.tsx:219 msgid "Vitals" msgstr "Vitals" @@ -2890,7 +2890,7 @@ msgstr "Walkout-Segment ({segmentDuration}s)" msgid "Walkout Song" msgstr "Walkout-Song" -#: src/features/telemetry/components/usage-screen.tsx:169 +#: src/features/telemetry/components/usage-screen.tsx:170 msgid "WAU" msgstr "WAU" diff --git a/src/locales/en/messages.po b/src/locales/en/messages.po index bce234c..da0c7e5 100644 --- a/src/locales/en/messages.po +++ b/src/locales/en/messages.po @@ -58,7 +58,7 @@ msgid "{0, plural, one {# total activity} other {# total activities}}" msgstr "{0, plural, one {# total activity} other {# total activities}}" #. placeholder {0}: result.totalItems -#: src/features/telemetry/components/client-events-table.tsx:87 +#: src/features/telemetry/components/client-events-table.tsx:86 msgid "{0, plural, one {# total event} other {# total events}}" msgstr "{0, plural, one {# total event} other {# total events}}" @@ -355,7 +355,7 @@ msgstr "2. Earlier submission" msgid "2nd place" msgstr "2nd place" -#: src/features/telemetry/components/usage-screen.tsx:263 +#: src/features/telemetry/components/usage-screen.tsx:264 msgid "30d" msgstr "30d" @@ -371,7 +371,7 @@ msgstr "3rd place" msgid "5s" msgstr "5s" -#: src/features/telemetry/components/usage-screen.tsx:262 +#: src/features/telemetry/components/usage-screen.tsx:263 msgid "7d" msgstr "7d" @@ -480,7 +480,7 @@ msgstr "An unexpected error occurred. Please try again." msgid "Announce matchup" msgstr "Announce matchup" -#: src/features/telemetry/components/client-events-table.tsx:49 +#: src/features/telemetry/components/client-events-table.tsx:48 msgid "Anonymous" msgstr "Anonymous" @@ -716,7 +716,7 @@ msgstr "Clear selected player" msgid "Client error spike" msgstr "Client error spike" -#: src/features/telemetry/components/usage-screen.tsx:207 +#: src/features/telemetry/components/usage-screen.tsx:208 msgid "Client errors" msgstr "Client errors" @@ -846,7 +846,7 @@ msgid "Create Tournament" msgstr "Create Tournament" #: src/features/activities/components/activities-table.tsx:198 -#: src/features/telemetry/components/client-events-table.tsx:149 +#: src/features/telemetry/components/client-events-table.tsx:148 msgid "Created" msgstr "Created" @@ -875,7 +875,7 @@ msgstr "Cups Made" msgid "Custom Hex Code" msgstr "Custom Hex Code" -#: src/features/telemetry/components/usage-screen.tsx:162 +#: src/features/telemetry/components/usage-screen.tsx:163 msgid "Daily active players" msgstr "Daily active players" @@ -934,7 +934,7 @@ msgstr "Downstream matches were reset because the correction changed who advance msgid "Duration" msgstr "Duration" -#: src/features/telemetry/components/usage-screen.tsx:240 +#: src/features/telemetry/components/usage-screen.tsx:241 msgid "Duration p50 / p95" msgstr "Duration p50 / p95" @@ -996,7 +996,7 @@ msgstr "End Date (Optional)" msgid "End Match" msgstr "End Match" -#: src/features/telemetry/components/usage-screen.tsx:159 +#: src/features/telemetry/components/usage-screen.tsx:160 msgid "Engagement" msgstr "Engagement" @@ -1097,15 +1097,15 @@ msgid "Error: {errorMessage}" msgstr "Error: {errorMessage}" #: src/app/routes/_authed/admin/telemetry.tsx:57 -#: src/features/telemetry/components/usage-screen.tsx:204 +#: src/features/telemetry/components/usage-screen.tsx:205 msgid "Errors" msgstr "Errors" -#: src/features/telemetry/components/client-events-table.tsx:113 +#: src/features/telemetry/components/client-events-table.tsx:112 msgid "Event Details" msgstr "Event Details" -#: src/features/telemetry/components/client-events-table.tsx:70 +#: src/features/telemetry/components/client-events-table.tsx:69 msgid "Event name" msgstr "Event name" @@ -1238,7 +1238,7 @@ msgstr "Free Agent Enrollment" msgid "Free Agents" msgstr "Free Agents" -#: src/features/telemetry/components/usage-screen.tsx:233 +#: src/features/telemetry/components/usage-screen.tsx:234 msgid "Function" msgstr "Function" @@ -1616,7 +1616,7 @@ msgstr "Matchup" msgid "Matchup details" msgstr "Matchup details" -#: src/features/telemetry/components/usage-screen.tsx:173 +#: src/features/telemetry/components/usage-screen.tsx:174 msgid "MAU" msgstr "MAU" @@ -1638,7 +1638,7 @@ msgid "MMR Calculation:" msgstr "MMR Calculation:" #: src/features/teams/components/team-form/index.tsx:200 -#: src/features/telemetry/components/client-events-table.tsx:119 +#: src/features/telemetry/components/client-events-table.tsx:118 #: src/features/tournaments/components/tournament-form.tsx:137 msgid "Name" msgstr "Name" @@ -1734,7 +1734,7 @@ msgstr "No Badges Available" msgid "No devices found." msgstr "No devices found." -#: src/features/telemetry/components/client-events-table.tsx:94 +#: src/features/telemetry/components/client-events-table.tsx:93 msgid "No Events Found" msgstr "No Events Found" @@ -1929,15 +1929,15 @@ msgstr "Overview" msgid "owo" msgstr "owo" -#: src/features/telemetry/components/usage-screen.tsx:100 +#: src/features/telemetry/components/usage-screen.tsx:101 msgid "p50" msgstr "p50" -#: src/features/telemetry/components/usage-screen.tsx:116 +#: src/features/telemetry/components/usage-screen.tsx:117 msgid "p95" msgstr "p95" -#: src/features/telemetry/components/usage-screen.tsx:180 +#: src/features/telemetry/components/usage-screen.tsx:181 msgid "Page views by route" msgstr "Page views by route" @@ -1949,7 +1949,7 @@ msgstr "Partner Assignments" msgid "Partners will be randomly assigned when enrollment closes" msgstr "Partners will be randomly assigned when enrollment closes" -#: src/features/telemetry/components/client-events-table.tsx:122 +#: src/features/telemetry/components/client-events-table.tsx:121 msgid "Path" msgstr "Path" @@ -1995,8 +1995,8 @@ msgstr "Playback control failed" #: src/features/activities/components/activities-table.tsx:98 #: src/features/activities/components/activities-table.tsx:188 #: src/features/admin/components/award-badges.tsx:31 -#: src/features/telemetry/components/client-events-table.tsx:73 -#: src/features/telemetry/components/client-events-table.tsx:120 +#: src/features/telemetry/components/client-events-table.tsx:72 +#: src/features/telemetry/components/client-events-table.tsx:119 msgid "Player" msgstr "Player" @@ -2076,7 +2076,7 @@ msgstr "Profile" msgid "Progress " msgstr "Progress " -#: src/features/telemetry/components/client-events-table.tsx:136 +#: src/features/telemetry/components/client-events-table.tsx:135 msgid "Props" msgstr "Props" @@ -2167,7 +2167,7 @@ msgstr "rofl" msgid "Round {0}" msgstr "Round {0}" -#: src/features/telemetry/components/client-events-table.tsx:125 +#: src/features/telemetry/components/client-events-table.tsx:124 msgid "Route" msgstr "Route" @@ -2343,7 +2343,7 @@ msgstr "Series is tied" msgid "Server function failing" msgstr "Server function failing" -#: src/features/telemetry/components/usage-screen.tsx:190 +#: src/features/telemetry/components/usage-screen.tsx:191 msgid "Server functions" msgstr "Server functions" @@ -2351,11 +2351,11 @@ msgstr "Server functions" msgid "serverFn name" msgstr "serverFn name" -#: src/features/telemetry/components/client-events-table.tsx:128 +#: src/features/telemetry/components/client-events-table.tsx:127 msgid "Session" msgstr "Session" -#: src/features/telemetry/components/usage-screen.tsx:176 +#: src/features/telemetry/components/usage-screen.tsx:177 msgid "Sessions" msgstr "Sessions" @@ -2687,7 +2687,7 @@ msgstr "Top {0, plural, one {# team} other {# teams}} from each group will advan msgid "Top {n} advance" msgstr "Top {n} advance" -#: src/features/telemetry/components/usage-screen.tsx:194 +#: src/features/telemetry/components/usage-screen.tsx:195 msgid "Top functions by call count" msgstr "Top functions by call count" @@ -2809,11 +2809,11 @@ msgid "Usage" msgstr "Usage" #: src/features/activities/components/activities-table.tsx:202 -#: src/features/telemetry/components/client-events-table.tsx:146 +#: src/features/telemetry/components/client-events-table.tsx:145 msgid "User Agent" msgstr "User Agent" -#: src/features/telemetry/components/client-events-table.tsx:131 +#: src/features/telemetry/components/client-events-table.tsx:130 msgid "Value" msgstr "Value" @@ -2851,7 +2851,7 @@ msgstr "View Predictions" msgid "Viewport" msgstr "Viewport" -#: src/features/telemetry/components/usage-screen.tsx:218 +#: src/features/telemetry/components/usage-screen.tsx:219 msgid "Vitals" msgstr "Vitals" @@ -2890,7 +2890,7 @@ msgstr "Walkout Segment ({segmentDuration}s)" msgid "Walkout Song" msgstr "Walkout Song" -#: src/features/telemetry/components/usage-screen.tsx:169 +#: src/features/telemetry/components/usage-screen.tsx:170 msgid "WAU" msgstr "WAU" diff --git a/src/locales/es/messages.po b/src/locales/es/messages.po index 0b9b073..9a42bbf 100644 --- a/src/locales/es/messages.po +++ b/src/locales/es/messages.po @@ -58,7 +58,7 @@ msgid "{0, plural, one {# total activity} other {# total activities}}" msgstr "{0, plural, one {# actividad total} other {# actividades totales}}" #. placeholder {0}: result.totalItems -#: src/features/telemetry/components/client-events-table.tsx:87 +#: src/features/telemetry/components/client-events-table.tsx:86 msgid "{0, plural, one {# total event} other {# total events}}" msgstr "{0, plural, one {# evento total} other {# eventos totales}}" @@ -355,7 +355,7 @@ msgstr "2. Envío más temprano" msgid "2nd place" msgstr "2do lugar" -#: src/features/telemetry/components/usage-screen.tsx:263 +#: src/features/telemetry/components/usage-screen.tsx:264 msgid "30d" msgstr "30d" @@ -371,7 +371,7 @@ msgstr "3er lugar" msgid "5s" msgstr "5s" -#: src/features/telemetry/components/usage-screen.tsx:262 +#: src/features/telemetry/components/usage-screen.tsx:263 msgid "7d" msgstr "7d" @@ -480,7 +480,7 @@ msgstr "Ocurrió un error inesperado. Intenta de nuevo." msgid "Announce matchup" msgstr "Anunciar enfrentamiento" -#: src/features/telemetry/components/client-events-table.tsx:49 +#: src/features/telemetry/components/client-events-table.tsx:48 msgid "Anonymous" msgstr "Anónimo" @@ -716,7 +716,7 @@ msgstr "Quitar jugador seleccionado" msgid "Client error spike" msgstr "Aumento de errores de cliente" -#: src/features/telemetry/components/usage-screen.tsx:207 +#: src/features/telemetry/components/usage-screen.tsx:208 msgid "Client errors" msgstr "Errores de cliente" @@ -846,7 +846,7 @@ msgid "Create Tournament" msgstr "Crear torneo" #: src/features/activities/components/activities-table.tsx:198 -#: src/features/telemetry/components/client-events-table.tsx:149 +#: src/features/telemetry/components/client-events-table.tsx:148 msgid "Created" msgstr "Creado" @@ -875,7 +875,7 @@ msgstr "Vasos anotados" msgid "Custom Hex Code" msgstr "Código hex personalizado" -#: src/features/telemetry/components/usage-screen.tsx:162 +#: src/features/telemetry/components/usage-screen.tsx:163 msgid "Daily active players" msgstr "Jugadores activos diarios" @@ -934,7 +934,7 @@ msgstr "Los partidos posteriores se reiniciaron porque la corrección cambió qu msgid "Duration" msgstr "Duración" -#: src/features/telemetry/components/usage-screen.tsx:240 +#: src/features/telemetry/components/usage-screen.tsx:241 msgid "Duration p50 / p95" msgstr "Duración p50 / p95" @@ -996,7 +996,7 @@ msgstr "Fecha de fin (opcional)" msgid "End Match" msgstr "Terminar partido" -#: src/features/telemetry/components/usage-screen.tsx:159 +#: src/features/telemetry/components/usage-screen.tsx:160 msgid "Engagement" msgstr "Interacción" @@ -1097,15 +1097,15 @@ msgid "Error: {errorMessage}" msgstr "Error: {errorMessage}" #: src/app/routes/_authed/admin/telemetry.tsx:57 -#: src/features/telemetry/components/usage-screen.tsx:204 +#: src/features/telemetry/components/usage-screen.tsx:205 msgid "Errors" msgstr "Errores" -#: src/features/telemetry/components/client-events-table.tsx:113 +#: src/features/telemetry/components/client-events-table.tsx:112 msgid "Event Details" msgstr "Detalles del evento" -#: src/features/telemetry/components/client-events-table.tsx:70 +#: src/features/telemetry/components/client-events-table.tsx:69 msgid "Event name" msgstr "Nombre del evento" @@ -1238,7 +1238,7 @@ msgstr "Inscripción de agente libre" msgid "Free Agents" msgstr "Agentes libres" -#: src/features/telemetry/components/usage-screen.tsx:233 +#: src/features/telemetry/components/usage-screen.tsx:234 msgid "Function" msgstr "Función" @@ -1616,7 +1616,7 @@ msgstr "Enfrentamiento" msgid "Matchup details" msgstr "Detalles del enfrentamiento" -#: src/features/telemetry/components/usage-screen.tsx:173 +#: src/features/telemetry/components/usage-screen.tsx:174 msgid "MAU" msgstr "MAU" @@ -1638,7 +1638,7 @@ msgid "MMR Calculation:" msgstr "Cálculo de MMR:" #: src/features/teams/components/team-form/index.tsx:200 -#: src/features/telemetry/components/client-events-table.tsx:119 +#: src/features/telemetry/components/client-events-table.tsx:118 #: src/features/tournaments/components/tournament-form.tsx:137 msgid "Name" msgstr "Nombre" @@ -1734,7 +1734,7 @@ msgstr "No hay insignias disponibles" msgid "No devices found." msgstr "No se encontraron dispositivos." -#: src/features/telemetry/components/client-events-table.tsx:94 +#: src/features/telemetry/components/client-events-table.tsx:93 msgid "No Events Found" msgstr "No se encontraron eventos" @@ -1929,15 +1929,15 @@ msgstr "Resumen" msgid "owo" msgstr "owo" -#: src/features/telemetry/components/usage-screen.tsx:100 +#: src/features/telemetry/components/usage-screen.tsx:101 msgid "p50" msgstr "p50" -#: src/features/telemetry/components/usage-screen.tsx:116 +#: src/features/telemetry/components/usage-screen.tsx:117 msgid "p95" msgstr "p95" -#: src/features/telemetry/components/usage-screen.tsx:180 +#: src/features/telemetry/components/usage-screen.tsx:181 msgid "Page views by route" msgstr "Vistas de página por ruta" @@ -1949,7 +1949,7 @@ msgstr "Asignación de compañeros" msgid "Partners will be randomly assigned when enrollment closes" msgstr "Los compañeros se asignarán al azar cuando cierre la inscripción" -#: src/features/telemetry/components/client-events-table.tsx:122 +#: src/features/telemetry/components/client-events-table.tsx:121 msgid "Path" msgstr "Ruta" @@ -1995,8 +1995,8 @@ msgstr "Falló el control de reproducción" #: src/features/activities/components/activities-table.tsx:98 #: src/features/activities/components/activities-table.tsx:188 #: src/features/admin/components/award-badges.tsx:31 -#: src/features/telemetry/components/client-events-table.tsx:73 -#: src/features/telemetry/components/client-events-table.tsx:120 +#: src/features/telemetry/components/client-events-table.tsx:72 +#: src/features/telemetry/components/client-events-table.tsx:119 msgid "Player" msgstr "Jugador" @@ -2076,7 +2076,7 @@ msgstr "Perfil" msgid "Progress " msgstr "Progreso " -#: src/features/telemetry/components/client-events-table.tsx:136 +#: src/features/telemetry/components/client-events-table.tsx:135 msgid "Props" msgstr "Props" @@ -2167,7 +2167,7 @@ msgstr "carcajada" msgid "Round {0}" msgstr "Ronda {0}" -#: src/features/telemetry/components/client-events-table.tsx:125 +#: src/features/telemetry/components/client-events-table.tsx:124 msgid "Route" msgstr "Ruta" @@ -2343,7 +2343,7 @@ msgstr "La serie está empatada" msgid "Server function failing" msgstr "Función del servidor fallando" -#: src/features/telemetry/components/usage-screen.tsx:190 +#: src/features/telemetry/components/usage-screen.tsx:191 msgid "Server functions" msgstr "Funciones del servidor" @@ -2351,11 +2351,11 @@ msgstr "Funciones del servidor" msgid "serverFn name" msgstr "nombre de serverFn" -#: src/features/telemetry/components/client-events-table.tsx:128 +#: src/features/telemetry/components/client-events-table.tsx:127 msgid "Session" msgstr "Sesión" -#: src/features/telemetry/components/usage-screen.tsx:176 +#: src/features/telemetry/components/usage-screen.tsx:177 msgid "Sessions" msgstr "Sesiones" @@ -2687,7 +2687,7 @@ msgstr "{0, plural, one {El mejor equipo de cada grupo avanzará} other {Los mej msgid "Top {n} advance" msgstr "Los mejores {n} avanzan" -#: src/features/telemetry/components/usage-screen.tsx:194 +#: src/features/telemetry/components/usage-screen.tsx:195 msgid "Top functions by call count" msgstr "Funciones principales por cantidad de llamadas" @@ -2809,11 +2809,11 @@ msgid "Usage" msgstr "Uso" #: src/features/activities/components/activities-table.tsx:202 -#: src/features/telemetry/components/client-events-table.tsx:146 +#: src/features/telemetry/components/client-events-table.tsx:145 msgid "User Agent" msgstr "User Agent" -#: src/features/telemetry/components/client-events-table.tsx:131 +#: src/features/telemetry/components/client-events-table.tsx:130 msgid "Value" msgstr "Valor" @@ -2851,7 +2851,7 @@ msgstr "Ver predicciones" msgid "Viewport" msgstr "Viewport" -#: src/features/telemetry/components/usage-screen.tsx:218 +#: src/features/telemetry/components/usage-screen.tsx:219 msgid "Vitals" msgstr "Vitales" @@ -2890,7 +2890,7 @@ msgstr "Segmento de entrada ({segmentDuration}s)" msgid "Walkout Song" msgstr "Canción de entrada" -#: src/features/telemetry/components/usage-screen.tsx:169 +#: src/features/telemetry/components/usage-screen.tsx:170 msgid "WAU" msgstr "WAU" diff --git a/src/locales/ja/messages.po b/src/locales/ja/messages.po index 289f538..fc8e375 100644 --- a/src/locales/ja/messages.po +++ b/src/locales/ja/messages.po @@ -58,7 +58,7 @@ msgid "{0, plural, one {# total activity} other {# total activities}}" msgstr "{0, plural, one {アクティビティ合計#件} other {アクティビティ合計#件}}" #. placeholder {0}: result.totalItems -#: src/features/telemetry/components/client-events-table.tsx:87 +#: src/features/telemetry/components/client-events-table.tsx:86 msgid "{0, plural, one {# total event} other {# total events}}" msgstr "{0, plural, one {イベント合計#件} other {イベント合計#件}}" @@ -355,7 +355,7 @@ msgstr "2. 早い提出" msgid "2nd place" msgstr "2位" -#: src/features/telemetry/components/usage-screen.tsx:263 +#: src/features/telemetry/components/usage-screen.tsx:264 msgid "30d" msgstr "30日" @@ -371,7 +371,7 @@ msgstr "3位" msgid "5s" msgstr "5秒" -#: src/features/telemetry/components/usage-screen.tsx:262 +#: src/features/telemetry/components/usage-screen.tsx:263 msgid "7d" msgstr "7日" @@ -480,7 +480,7 @@ msgstr "予期しないエラーが発生しました。もう一度お試しく msgid "Announce matchup" msgstr "対戦カードを発表" -#: src/features/telemetry/components/client-events-table.tsx:49 +#: src/features/telemetry/components/client-events-table.tsx:48 msgid "Anonymous" msgstr "匿名" @@ -716,7 +716,7 @@ msgstr "選択したプレイヤーを解除" msgid "Client error spike" msgstr "クライアントエラー急増" -#: src/features/telemetry/components/usage-screen.tsx:207 +#: src/features/telemetry/components/usage-screen.tsx:208 msgid "Client errors" msgstr "クライアントエラー" @@ -846,7 +846,7 @@ msgid "Create Tournament" msgstr "トーナメントを作成" #: src/features/activities/components/activities-table.tsx:198 -#: src/features/telemetry/components/client-events-table.tsx:149 +#: src/features/telemetry/components/client-events-table.tsx:148 msgid "Created" msgstr "作成日" @@ -875,7 +875,7 @@ msgstr "決めたカップ" msgid "Custom Hex Code" msgstr "カスタムHEXコード" -#: src/features/telemetry/components/usage-screen.tsx:162 +#: src/features/telemetry/components/usage-screen.tsx:163 msgid "Daily active players" msgstr "デイリーアクティブプレイヤー数" @@ -934,7 +934,7 @@ msgstr "修正により勝ち上がりの相手が変わったため、以降の msgid "Duration" msgstr "所要時間" -#: src/features/telemetry/components/usage-screen.tsx:240 +#: src/features/telemetry/components/usage-screen.tsx:241 msgid "Duration p50 / p95" msgstr "所要時間 p50 / p95" @@ -996,7 +996,7 @@ msgstr "終了日(任意)" msgid "End Match" msgstr "試合を終了" -#: src/features/telemetry/components/usage-screen.tsx:159 +#: src/features/telemetry/components/usage-screen.tsx:160 msgid "Engagement" msgstr "エンゲージメント" @@ -1097,15 +1097,15 @@ msgid "Error: {errorMessage}" msgstr "エラー: {errorMessage}" #: src/app/routes/_authed/admin/telemetry.tsx:57 -#: src/features/telemetry/components/usage-screen.tsx:204 +#: src/features/telemetry/components/usage-screen.tsx:205 msgid "Errors" msgstr "エラー" -#: src/features/telemetry/components/client-events-table.tsx:113 +#: src/features/telemetry/components/client-events-table.tsx:112 msgid "Event Details" msgstr "イベント詳細" -#: src/features/telemetry/components/client-events-table.tsx:70 +#: src/features/telemetry/components/client-events-table.tsx:69 msgid "Event name" msgstr "イベント名" @@ -1238,7 +1238,7 @@ msgstr "フリーエージェント登録" msgid "Free Agents" msgstr "フリーエージェント" -#: src/features/telemetry/components/usage-screen.tsx:233 +#: src/features/telemetry/components/usage-screen.tsx:234 msgid "Function" msgstr "関数" @@ -1616,7 +1616,7 @@ msgstr "対戦カード" msgid "Matchup details" msgstr "対戦カードの詳細" -#: src/features/telemetry/components/usage-screen.tsx:173 +#: src/features/telemetry/components/usage-screen.tsx:174 msgid "MAU" msgstr "MAU" @@ -1638,7 +1638,7 @@ msgid "MMR Calculation:" msgstr "MMR計算:" #: src/features/teams/components/team-form/index.tsx:200 -#: src/features/telemetry/components/client-events-table.tsx:119 +#: src/features/telemetry/components/client-events-table.tsx:118 #: src/features/tournaments/components/tournament-form.tsx:137 msgid "Name" msgstr "名前" @@ -1734,7 +1734,7 @@ msgstr "利用可能なバッジがありません" msgid "No devices found." msgstr "デバイスが見つかりません。" -#: src/features/telemetry/components/client-events-table.tsx:94 +#: src/features/telemetry/components/client-events-table.tsx:93 msgid "No Events Found" msgstr "イベントが見つかりません" @@ -1929,15 +1929,15 @@ msgstr "概要" msgid "owo" msgstr "owo" -#: src/features/telemetry/components/usage-screen.tsx:100 +#: src/features/telemetry/components/usage-screen.tsx:101 msgid "p50" msgstr "p50" -#: src/features/telemetry/components/usage-screen.tsx:116 +#: src/features/telemetry/components/usage-screen.tsx:117 msgid "p95" msgstr "p95" -#: src/features/telemetry/components/usage-screen.tsx:180 +#: src/features/telemetry/components/usage-screen.tsx:181 msgid "Page views by route" msgstr "ルート別ページビュー数" @@ -1949,7 +1949,7 @@ msgstr "パートナー割り当て" msgid "Partners will be randomly assigned when enrollment closes" msgstr "登録が締め切られると、パートナーはランダムに割り当てられます" -#: src/features/telemetry/components/client-events-table.tsx:122 +#: src/features/telemetry/components/client-events-table.tsx:121 msgid "Path" msgstr "パス" @@ -1995,8 +1995,8 @@ msgstr "再生コントロールに失敗しました" #: src/features/activities/components/activities-table.tsx:98 #: src/features/activities/components/activities-table.tsx:188 #: src/features/admin/components/award-badges.tsx:31 -#: src/features/telemetry/components/client-events-table.tsx:73 -#: src/features/telemetry/components/client-events-table.tsx:120 +#: src/features/telemetry/components/client-events-table.tsx:72 +#: src/features/telemetry/components/client-events-table.tsx:119 msgid "Player" msgstr "プレイヤー" @@ -2076,7 +2076,7 @@ msgstr "プロフィール" msgid "Progress " msgstr "進行状況 " -#: src/features/telemetry/components/client-events-table.tsx:136 +#: src/features/telemetry/components/client-events-table.tsx:135 msgid "Props" msgstr "プロパティ" @@ -2167,7 +2167,7 @@ msgstr "大笑い" msgid "Round {0}" msgstr "ラウンド{0}" -#: src/features/telemetry/components/client-events-table.tsx:125 +#: src/features/telemetry/components/client-events-table.tsx:124 msgid "Route" msgstr "ルート" @@ -2343,7 +2343,7 @@ msgstr "シリーズはタイです" msgid "Server function failing" msgstr "サーバー関数が失敗しています" -#: src/features/telemetry/components/usage-screen.tsx:190 +#: src/features/telemetry/components/usage-screen.tsx:191 msgid "Server functions" msgstr "サーバー関数" @@ -2351,11 +2351,11 @@ msgstr "サーバー関数" msgid "serverFn name" msgstr "serverFn名" -#: src/features/telemetry/components/client-events-table.tsx:128 +#: src/features/telemetry/components/client-events-table.tsx:127 msgid "Session" msgstr "セッション" -#: src/features/telemetry/components/usage-screen.tsx:176 +#: src/features/telemetry/components/usage-screen.tsx:177 msgid "Sessions" msgstr "セッション数" @@ -2687,7 +2687,7 @@ msgstr "各グループの上位{0, plural, one {#チーム} other {#チーム}} msgid "Top {n} advance" msgstr "上位{n}チームが勝ち上がり" -#: src/features/telemetry/components/usage-screen.tsx:194 +#: src/features/telemetry/components/usage-screen.tsx:195 msgid "Top functions by call count" msgstr "呼び出し数の多い関数" @@ -2809,11 +2809,11 @@ msgid "Usage" msgstr "利用状況" #: src/features/activities/components/activities-table.tsx:202 -#: src/features/telemetry/components/client-events-table.tsx:146 +#: src/features/telemetry/components/client-events-table.tsx:145 msgid "User Agent" msgstr "ユーザーエージェント" -#: src/features/telemetry/components/client-events-table.tsx:131 +#: src/features/telemetry/components/client-events-table.tsx:130 msgid "Value" msgstr "値" @@ -2851,7 +2851,7 @@ msgstr "予想を見る" msgid "Viewport" msgstr "ビューポート" -#: src/features/telemetry/components/usage-screen.tsx:218 +#: src/features/telemetry/components/usage-screen.tsx:219 msgid "Vitals" msgstr "バイタル" @@ -2890,7 +2890,7 @@ msgstr "ウォークアウト区間({segmentDuration}秒)" msgid "Walkout Song" msgstr "ウォークアウトソング" -#: src/features/telemetry/components/usage-screen.tsx:169 +#: src/features/telemetry/components/usage-screen.tsx:170 msgid "WAU" msgstr "WAU" -- 2.54.0 From e4d1ba720a7f5f4b14a923fe7f2cfc05121ab532 Mon Sep 17 00:00:00 2001 From: yohlo Date: Tue, 25 Aug 2026 22:56:09 -0700 Subject: [PATCH 15/33] feat(telemetry): session journey view replaces raw events table - sessions listed from client_events grouped by session id (7d window) - timeline sheet merges page views, client events, errors, and server-fn activities joined by player within the session window - vitals excluded from timelines; gap markers over 5 minutes --- src/app/routeTree.gen.ts | 44 ++-- src/app/routes/_authed/admin/telemetry.tsx | 6 +- .../routes/_authed/admin/telemetry/events.tsx | 28 --- .../_authed/admin/telemetry/sessions.tsx | 25 +++ .../components/client-events-table.tsx | 157 ------------- .../components/sessions/session-timeline.tsx | 99 ++++++++ .../components/sessions/sessions-list.tsx | 81 +++++++ src/features/telemetry/index.ts | 3 +- src/features/telemetry/queries.ts | 20 ++ src/features/telemetry/server.ts | 211 ++++++++++++++++++ src/features/telemetry/types.ts | 27 +++ src/lib/pocketbase/services/activities.ts | 12 + src/lib/pocketbase/services/telemetry.ts | 4 +- src/locales/de/messages.po | 72 +++--- src/locales/en/messages.po | 72 +++--- src/locales/es/messages.po | 72 +++--- src/locales/ja/messages.po | 72 +++--- 17 files changed, 605 insertions(+), 400 deletions(-) delete mode 100644 src/app/routes/_authed/admin/telemetry/events.tsx create mode 100644 src/app/routes/_authed/admin/telemetry/sessions.tsx delete mode 100644 src/features/telemetry/components/client-events-table.tsx create mode 100644 src/features/telemetry/components/sessions/session-timeline.tsx create mode 100644 src/features/telemetry/components/sessions/sessions-list.tsx diff --git a/src/app/routeTree.gen.ts b/src/app/routeTree.gen.ts index 99fca68..298cff7 100644 --- a/src/app/routeTree.gen.ts +++ b/src/app/routeTree.gen.ts @@ -48,9 +48,9 @@ import { Route as AuthedTournamentsIdPredictionsRouteImport } from './routes/_au import { Route as AuthedTournamentsIdGroupsRouteImport } from './routes/_authed/tournaments/$id.groups' import { Route as AuthedTournamentsIdBracketRouteImport } from './routes/_authed/tournaments/$id.bracket' import { Route as AuthedAdminTelemetryUsageRouteImport } from './routes/_authed/admin/telemetry/usage' +import { Route as AuthedAdminTelemetrySessionsRouteImport } from './routes/_authed/admin/telemetry/sessions' import { Route as AuthedAdminTelemetryPlayersRouteImport } from './routes/_authed/admin/telemetry/players' import { Route as AuthedAdminTelemetryFunctionsRouteImport } from './routes/_authed/admin/telemetry/functions' -import { Route as AuthedAdminTelemetryEventsRouteImport } from './routes/_authed/admin/telemetry/events' import { Route as AuthedAdminTelemetryErrorsRouteImport } from './routes/_authed/admin/telemetry/errors' import { Route as AuthedAdminTournamentsIdIndexRouteImport } from './routes/_authed/admin/tournaments/$id/index' import { Route as ApiFilesCollectionRecordIdFileRouteImport } from './routes/api/files/$collection/$recordId/$file' @@ -263,6 +263,12 @@ const AuthedAdminTelemetryUsageRoute = path: '/usage', getParentRoute: () => AuthedAdminTelemetryRoute, } as any) +const AuthedAdminTelemetrySessionsRoute = + AuthedAdminTelemetrySessionsRouteImport.update({ + id: '/sessions', + path: '/sessions', + getParentRoute: () => AuthedAdminTelemetryRoute, + } as any) const AuthedAdminTelemetryPlayersRoute = AuthedAdminTelemetryPlayersRouteImport.update({ id: '/players', @@ -275,12 +281,6 @@ const AuthedAdminTelemetryFunctionsRoute = path: '/functions', getParentRoute: () => AuthedAdminTelemetryRoute, } as any) -const AuthedAdminTelemetryEventsRoute = - AuthedAdminTelemetryEventsRouteImport.update({ - id: '/events', - path: '/events', - getParentRoute: () => AuthedAdminTelemetryRoute, - } as any) const AuthedAdminTelemetryErrorsRoute = AuthedAdminTelemetryErrorsRouteImport.update({ id: '/errors', @@ -370,9 +370,9 @@ export interface FileRoutesByFullPath { '/admin/': typeof AuthedAdminIndexRoute '/tournaments/': typeof AuthedTournamentsIndexRoute '/admin/telemetry/errors': typeof AuthedAdminTelemetryErrorsRoute - '/admin/telemetry/events': typeof AuthedAdminTelemetryEventsRoute '/admin/telemetry/functions': typeof AuthedAdminTelemetryFunctionsRoute '/admin/telemetry/players': typeof AuthedAdminTelemetryPlayersRoute + '/admin/telemetry/sessions': typeof AuthedAdminTelemetrySessionsRoute '/admin/telemetry/usage': typeof AuthedAdminTelemetryUsageRoute '/tournaments/$id/bracket': typeof AuthedTournamentsIdBracketRoute '/tournaments/$id/groups': typeof AuthedTournamentsIdGroupsRoute @@ -420,9 +420,9 @@ export interface FileRoutesByTo { '/admin': typeof AuthedAdminIndexRoute '/tournaments': typeof AuthedTournamentsIndexRoute '/admin/telemetry/errors': typeof AuthedAdminTelemetryErrorsRoute - '/admin/telemetry/events': typeof AuthedAdminTelemetryEventsRoute '/admin/telemetry/functions': typeof AuthedAdminTelemetryFunctionsRoute '/admin/telemetry/players': typeof AuthedAdminTelemetryPlayersRoute + '/admin/telemetry/sessions': typeof AuthedAdminTelemetrySessionsRoute '/admin/telemetry/usage': typeof AuthedAdminTelemetryUsageRoute '/tournaments/$id/bracket': typeof AuthedTournamentsIdBracketRoute '/tournaments/$id/groups': typeof AuthedTournamentsIdGroupsRoute @@ -474,9 +474,9 @@ export interface FileRoutesById { '/_authed/admin/': typeof AuthedAdminIndexRoute '/_authed/tournaments/': typeof AuthedTournamentsIndexRoute '/_authed/admin/telemetry/errors': typeof AuthedAdminTelemetryErrorsRoute - '/_authed/admin/telemetry/events': typeof AuthedAdminTelemetryEventsRoute '/_authed/admin/telemetry/functions': typeof AuthedAdminTelemetryFunctionsRoute '/_authed/admin/telemetry/players': typeof AuthedAdminTelemetryPlayersRoute + '/_authed/admin/telemetry/sessions': typeof AuthedAdminTelemetrySessionsRoute '/_authed/admin/telemetry/usage': typeof AuthedAdminTelemetryUsageRoute '/_authed/tournaments/$id/bracket': typeof AuthedTournamentsIdBracketRoute '/_authed/tournaments/$id/groups': typeof AuthedTournamentsIdGroupsRoute @@ -528,9 +528,9 @@ export interface FileRouteTypes { | '/admin/' | '/tournaments/' | '/admin/telemetry/errors' - | '/admin/telemetry/events' | '/admin/telemetry/functions' | '/admin/telemetry/players' + | '/admin/telemetry/sessions' | '/admin/telemetry/usage' | '/tournaments/$id/bracket' | '/tournaments/$id/groups' @@ -578,9 +578,9 @@ export interface FileRouteTypes { | '/admin' | '/tournaments' | '/admin/telemetry/errors' - | '/admin/telemetry/events' | '/admin/telemetry/functions' | '/admin/telemetry/players' + | '/admin/telemetry/sessions' | '/admin/telemetry/usage' | '/tournaments/$id/bracket' | '/tournaments/$id/groups' @@ -631,9 +631,9 @@ export interface FileRouteTypes { | '/_authed/admin/' | '/_authed/tournaments/' | '/_authed/admin/telemetry/errors' - | '/_authed/admin/telemetry/events' | '/_authed/admin/telemetry/functions' | '/_authed/admin/telemetry/players' + | '/_authed/admin/telemetry/sessions' | '/_authed/admin/telemetry/usage' | '/_authed/tournaments/$id/bracket' | '/_authed/tournaments/$id/groups' @@ -948,6 +948,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof AuthedAdminTelemetryUsageRouteImport parentRoute: typeof AuthedAdminTelemetryRoute } + '/_authed/admin/telemetry/sessions': { + id: '/_authed/admin/telemetry/sessions' + path: '/sessions' + fullPath: '/admin/telemetry/sessions' + preLoaderRoute: typeof AuthedAdminTelemetrySessionsRouteImport + parentRoute: typeof AuthedAdminTelemetryRoute + } '/_authed/admin/telemetry/players': { id: '/_authed/admin/telemetry/players' path: '/players' @@ -962,13 +969,6 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof AuthedAdminTelemetryFunctionsRouteImport parentRoute: typeof AuthedAdminTelemetryRoute } - '/_authed/admin/telemetry/events': { - id: '/_authed/admin/telemetry/events' - path: '/events' - fullPath: '/admin/telemetry/events' - preLoaderRoute: typeof AuthedAdminTelemetryEventsRouteImport - parentRoute: typeof AuthedAdminTelemetryRoute - } '/_authed/admin/telemetry/errors': { id: '/_authed/admin/telemetry/errors' path: '/errors' @@ -1037,18 +1037,18 @@ declare module '@tanstack/react-router' { interface AuthedAdminTelemetryRouteChildren { AuthedAdminTelemetryErrorsRoute: typeof AuthedAdminTelemetryErrorsRoute - AuthedAdminTelemetryEventsRoute: typeof AuthedAdminTelemetryEventsRoute AuthedAdminTelemetryFunctionsRoute: typeof AuthedAdminTelemetryFunctionsRoute AuthedAdminTelemetryPlayersRoute: typeof AuthedAdminTelemetryPlayersRoute + AuthedAdminTelemetrySessionsRoute: typeof AuthedAdminTelemetrySessionsRoute AuthedAdminTelemetryUsageRoute: typeof AuthedAdminTelemetryUsageRoute AuthedAdminTelemetryIndexRoute: typeof AuthedAdminTelemetryIndexRoute } const AuthedAdminTelemetryRouteChildren: AuthedAdminTelemetryRouteChildren = { AuthedAdminTelemetryErrorsRoute: AuthedAdminTelemetryErrorsRoute, - AuthedAdminTelemetryEventsRoute: AuthedAdminTelemetryEventsRoute, AuthedAdminTelemetryFunctionsRoute: AuthedAdminTelemetryFunctionsRoute, AuthedAdminTelemetryPlayersRoute: AuthedAdminTelemetryPlayersRoute, + AuthedAdminTelemetrySessionsRoute: AuthedAdminTelemetrySessionsRoute, AuthedAdminTelemetryUsageRoute: AuthedAdminTelemetryUsageRoute, AuthedAdminTelemetryIndexRoute: AuthedAdminTelemetryIndexRoute, } diff --git a/src/app/routes/_authed/admin/telemetry.tsx b/src/app/routes/_authed/admin/telemetry.tsx index c4e6735..5093635 100644 --- a/src/app/routes/_authed/admin/telemetry.tsx +++ b/src/app/routes/_authed/admin/telemetry.tsx @@ -7,7 +7,7 @@ const TAB_ROUTES = { usage: "/admin/telemetry/usage", players: "/admin/telemetry/players", functions: "/admin/telemetry/functions", - events: "/admin/telemetry/events", + sessions: "/admin/telemetry/sessions", errors: "/admin/telemetry/errors", } as const; @@ -50,8 +50,8 @@ function TelemetryLayout() { Functions - - Events + + Sessions Errors diff --git a/src/app/routes/_authed/admin/telemetry/events.tsx b/src/app/routes/_authed/admin/telemetry/events.tsx deleted file mode 100644 index cc030b6..0000000 --- a/src/app/routes/_authed/admin/telemetry/events.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import { createFileRoute } from "@tanstack/react-router"; -import { Suspense } from "react"; -import { prefetchServerQuery } from "@/lib/tanstack-query/utils/prefetch"; -import { telemetryQueries, defaultClientEventParams, ClientEventsTable } from "@/features/telemetry"; -import { playerQueries } from "@/features/players"; -import { ListSkeleton } from "@/components/skeletons"; - -export const Route = createFileRoute("/_authed/admin/telemetry/events")({ - component: EventsTab, - beforeLoad: ({ context }) => { - const queryClient = context.queryClient; - prefetchServerQuery(queryClient, telemetryQueries.events(defaultClientEventParams)); - prefetchServerQuery(queryClient, playerQueries.list()); - }, - loader: () => ({ - withPadding: false, - fullWidth: true, - refresh: [telemetryQueries.events(defaultClientEventParams).queryKey], - }), -}); - -function EventsTab() { - return ( - }> - - - ); -} diff --git a/src/app/routes/_authed/admin/telemetry/sessions.tsx b/src/app/routes/_authed/admin/telemetry/sessions.tsx new file mode 100644 index 0000000..ea22cb7 --- /dev/null +++ b/src/app/routes/_authed/admin/telemetry/sessions.tsx @@ -0,0 +1,25 @@ +import { createFileRoute } from "@tanstack/react-router"; +import { Suspense } from "react"; +import { prefetchServerQuery } from "@/lib/tanstack-query/utils/prefetch"; +import { telemetryQueries, SessionsList, defaultSessionParams } from "@/features/telemetry"; +import { ListSkeleton } from "@/components/skeletons"; + +export const Route = createFileRoute("/_authed/admin/telemetry/sessions")({ + component: SessionsTab, + beforeLoad: ({ context }) => { + prefetchServerQuery(context.queryClient, telemetryQueries.sessions(defaultSessionParams)); + }, + loader: () => ({ + withPadding: false, + fullWidth: true, + refresh: [telemetryQueries.sessions(defaultSessionParams).queryKey], + }), +}); + +function SessionsTab() { + return ( + }> + + + ); +} diff --git a/src/features/telemetry/components/client-events-table.tsx b/src/features/telemetry/components/client-events-table.tsx deleted file mode 100644 index b69b695..0000000 --- a/src/features/telemetry/components/client-events-table.tsx +++ /dev/null @@ -1,157 +0,0 @@ -import { useState, useMemo } from "react"; -import { Trans, Plural, useLingui } from "@lingui/react/macro"; -import { Code, Container, Stack } from "@mantine/core"; -import { usePlayers } from "@/features/players/queries"; -import { useSheet } from "@/hooks/use-sheet"; -import { useClientEvents } from "../queries"; -import type { ClientEvent, ClientEventSearchParams } from "../types"; -import { - DetailSheet, - PaginatedList, - RelativeTime, - SearchFilterBar, - TelemetryListItem, -} from "./shared"; - -export const defaultClientEventParams: ClientEventSearchParams = { - page: 1, - perPage: 50, - sortBy: "-created", -}; - -export const ClientEventsTable = () => { - const { t, i18n } = useLingui(); - const [page, setPage] = useState(1); - const [search, setSearch] = useState(""); - const [playerFilter, setPlayerFilter] = useState(null); - const [selectedEvent, setSelectedEvent] = useState(null); - const { isOpen: detailsOpened, open: openDetails, close: closeDetails } = useSheet(); - - const { data: players } = usePlayers(); - - const searchParams: ClientEventSearchParams = useMemo( - () => ({ - page, - perPage: 50, - name: search || undefined, - player: playerFilter || undefined, - sortBy: "-created", - }), - [page, search, playerFilter] - ); - - const { data: result } = useClientEvents(searchParams); - - const playerName = (event: ClientEvent) => - typeof event.player === "object" && event.player - ? `${event.player.first_name} ${event.player.last_name}` - : t`Anonymous`; - - const handleEventClick = (event: ClientEvent) => { - setSelectedEvent(event); - openDetails(); - }; - - const handleCloseDetails = () => { - setSelectedEvent(null); - closeDetails(); - }; - - return ( - - - { - setSearch(value); - setPage(1); - }} - searchPlaceholder={t`Event name`} - filters={[ - { - placeholder: t`Player`, - value: playerFilter, - onChange: (value) => { - setPlayerFilter(value); - setPage(1); - }, - data: players.map((player) => ({ - value: player.id, - label: `${player.first_name} ${player.last_name}`, - })), - searchable: true, - }, - ]} - summary={ - - } - /> - - event.id} - empty={t`No Events Found`} - page={page} - totalPages={result.totalPages} - onPageChange={setPage} - renderItem={(event) => ( - , - ]} - onClick={() => handleEventClick(event)} - /> - )} - /> - - - Name, value: selectedEvent.name }, - { label: Player, value: playerName(selectedEvent) }, - ...(selectedEvent.path - ? [{ label: Path, value: selectedEvent.path }] - : []), - ...(selectedEvent.route_id - ? [{ label: Route, value: selectedEvent.route_id }] - : []), - ...(selectedEvent.session_id - ? [{ label: Session, value: selectedEvent.session_id }] - : []), - ...(selectedEvent.value !== undefined && selectedEvent.value !== null - ? [{ label: Value, value: selectedEvent.value }] - : []), - ...(selectedEvent.props - ? [ - { - label: Props, - value: ( - - {JSON.stringify(selectedEvent.props, null, 2)} - - ), - }, - ] - : []), - ...(selectedEvent.user_agent - ? [{ label: User Agent, value: selectedEvent.user_agent }] - : []), - { - label: Created, - value: new Date(selectedEvent.created).toLocaleString(i18n.locale), - }, - ] - : [] - } - /> - - ); -}; diff --git a/src/features/telemetry/components/sessions/session-timeline.tsx b/src/features/telemetry/components/sessions/session-timeline.tsx new file mode 100644 index 0000000..8237783 --- /dev/null +++ b/src/features/telemetry/components/sessions/session-timeline.tsx @@ -0,0 +1,99 @@ +import { Fragment } from "react"; +import { Box, Group, Stack, Text } from "@mantine/core"; +import { Plural, useLingui } from "@lingui/react/macro"; +import { useSessionTimeline } from "../../queries"; +import type { TimelineEntry } from "../../types"; + +const GAP_MARKER_MS = 5 * 60 * 1000; + +const parseTs = (ts: string) => Date.parse(ts.replace(" ", "T")); + +const dotColor = (entry: TimelineEntry): string => { + if (entry.kind === "error" || entry.success === false) return "var(--mantine-color-red-6)"; + if (entry.kind === "server_fn") return "var(--mantine-color-green-6)"; + return "var(--mantine-color-dimmed)"; +}; + +const Dot = ({ color }: { color: string }) => ( + +); + +export const SessionTimelineView = ({ sessionId }: { sessionId: string }) => { + const { i18n } = useLingui(); + const { data } = useSessionTimeline(sessionId); + + const clock = (ts: string) => + i18n.date(new Date(parseTs(ts)), { hour: "2-digit", minute: "2-digit", second: "2-digit" }); + + return ( + + + + {i18n.date(new Date(parseTs(data.session.started)), { + month: "short", + day: "numeric", + hour: "2-digit", + minute: "2-digit", + })} + + + + + {data.session.errors > 0 && ( + + + + )} + + + {data.entries.map((entry, index) => { + const prev = data.entries[index - 1]; + const gapMs = prev ? parseTs(entry.ts) - parseTs(prev.ts) : 0; + return ( + + {gapMs > GAP_MARKER_MS && ( + + +{Math.round(gapMs / 60000)}m + + )} + + + {clock(entry.ts)} + + + + {entry.label} + + {entry.kind === "server_fn" && entry.success === false && ( + + ✗ + + )} + {entry.kind === "server_fn" && typeof entry.duration === "number" && entry.duration > 0 && ( + + {entry.duration}ms + + )} + + + ); + })} + + ); +}; + +export default SessionTimelineView; diff --git a/src/features/telemetry/components/sessions/sessions-list.tsx b/src/features/telemetry/components/sessions/sessions-list.tsx new file mode 100644 index 0000000..6d08461 --- /dev/null +++ b/src/features/telemetry/components/sessions/sessions-list.tsx @@ -0,0 +1,81 @@ +import { Suspense, useState } from "react"; +import { Plural, Trans, useLingui } from "@lingui/react/macro"; +import { Text } from "@mantine/core"; +import Sheet from "@/components/sheet/sheet"; +import { ListSkeleton } from "@/components/skeletons"; +import { useSessions } from "../../queries"; +import type { SessionSummary } from "../../types"; +import { PaginatedList, RelativeTime, TelemetryListItem } from "../shared"; +import SessionTimelineView from "./session-timeline"; + +export const defaultSessionParams = { days: 7, page: 1 } as const; + +const playerName = (player?: SessionSummary["player"]): string | null => { + if (!player) return null; + return [player.first_name, player.last_name].filter(Boolean).join(" ") || null; +}; + +const durationLabel = (session: SessionSummary): string => { + const ms = + Date.parse(session.lastSeen.replace(" ", "T")) - + Date.parse(session.started.replace(" ", "T")); + const minutes = Math.round(ms / 60000); + return minutes < 1 ? "<1m" : `${minutes}m`; +}; + +export const SessionsList = () => { + const { t } = useLingui(); + const [page, setPage] = useState(1); + const [selected, setSelected] = useState(null); + + const { data } = useSessions({ days: 7, page }); + + return ( + <> + session.session_id} + empty={t`No sessions yet`} + page={data.page} + totalPages={data.totalPages} + onPageChange={setPage} + renderItem={(session) => ( + , + , + durationLabel(session), + ]} + error={ + session.errors > 0 ? ( + + ) : undefined + } + onClick={() => setSelected(session)} + /> + )} + /> + + setSelected(null)} + > + {selected && ( + }> + + + )} + + + {data.totalItems > 0 && ( + + Last 7 days + + )} + + ); +}; + +export default SessionsList; diff --git a/src/features/telemetry/index.ts b/src/features/telemetry/index.ts index 65ab60b..bb7200f 100644 --- a/src/features/telemetry/index.ts +++ b/src/features/telemetry/index.ts @@ -3,6 +3,7 @@ export * from "./queries"; export * from "./range"; export * from "./components/charts"; export * from "./components/shared"; -export { ClientEventsTable, defaultClientEventParams } from "./components/client-events-table"; +export { SessionsList, defaultSessionParams } from "./components/sessions/sessions-list"; +export { SessionTimelineView } from "./components/sessions/session-timeline"; export { ErrorBrowser } from "./components/error-browser"; export { UsageScreen, USAGE_METRICS } from "./components/usage-screen"; diff --git a/src/features/telemetry/queries.ts b/src/features/telemetry/queries.ts index 6f41bed..87f7952 100644 --- a/src/features/telemetry/queries.ts +++ b/src/features/telemetry/queries.ts @@ -2,7 +2,9 @@ import { useServerSuspenseQuery } from "@/lib/tanstack-query/hooks"; import { getClientErrorGroups, getRollupRange, + getSessionTimeline, getTelemetryRuntimeStatus, + listSessions, listTelemetryAlerts, searchClientErrors, searchClientEvents, @@ -30,6 +32,10 @@ export const telemetryKeys = { errorGroups: (params: ErrorGroupParams) => ["telemetry", "errorGroups", params] as const, rollups: (params: RollupRangeParams) => ["telemetry", "rollups", params] as const, alerts: (page: number) => ["telemetry", "alerts", page] as const, + sessions: (params: { days?: number; page?: number }) => + ["telemetry", "sessions", params] as const, + sessionTimeline: (sessionId: string) => + ["telemetry", "sessionTimeline", sessionId] as const, runtimeStatus: ["telemetry", "runtimeStatus"] as const, }; @@ -54,6 +60,14 @@ export const telemetryQueries = { queryKey: telemetryKeys.alerts(page), queryFn: () => listTelemetryAlerts({ data: { page } }), }), + sessions: (params: { days?: number; page?: number } = {}) => ({ + queryKey: telemetryKeys.sessions(params), + queryFn: () => listSessions({ data: params }), + }), + sessionTimeline: (sessionId: string) => ({ + queryKey: telemetryKeys.sessionTimeline(sessionId), + queryFn: () => getSessionTimeline({ data: { sessionId } }), + }), runtimeStatus: () => ({ queryKey: telemetryKeys.runtimeStatus, queryFn: () => getTelemetryRuntimeStatus(), @@ -72,5 +86,11 @@ export const useClientErrorGroups = (params: ErrorGroupParams) => export const useRollupRange = (params: RollupRangeParams) => useServerSuspenseQuery(telemetryQueries.rollups(params)); +export const useSessions = (params: { days?: number; page?: number } = {}) => + useServerSuspenseQuery(telemetryQueries.sessions(params)); + +export const useSessionTimeline = (sessionId: string) => + useServerSuspenseQuery(telemetryQueries.sessionTimeline(sessionId)); + export const useTelemetryRuntimeStatus = () => useServerSuspenseQuery(telemetryQueries.runtimeStatus()); diff --git a/src/features/telemetry/server.ts b/src/features/telemetry/server.ts index f239fa3..ec356dc 100644 --- a/src/features/telemetry/server.ts +++ b/src/features/telemetry/server.ts @@ -15,9 +15,12 @@ import type { ClientErrorGroup, ClientEvent, RollupRow, + SessionSummary, + SessionTimeline, TelemetryAlert, TelemetryListResult, TelemetryRuntimeStatus, + TimelineEntry, } from "./types"; const clientEventSearchSchema = z.object({ @@ -155,6 +158,214 @@ export const listTelemetryAlerts = createServerFn() ) ); +const pbTime = (date: Date): string => date.toISOString().replace("T", " "); + +const playerInfoById = async (ids: Set) => { + if (ids.size === 0) return new Map(); + const players = await pbAdmin.listPlayers(); + return new Map( + players + .filter((player) => ids.has(player.id)) + .map((player) => [ + player.id, + { id: player.id, first_name: player.first_name, last_name: player.last_name }, + ]) + ); +}; + +export const listSessions = createServerFn() + .validator( + z.object({ + days: z.number().min(1).max(90).optional(), + page: z.number().optional(), + perPage: z.number().max(100).optional(), + }) + ) + .middleware([superTokensAdminFunctionMiddleware]) + .handler(async ({ data }) => + toServerResult>(async () => { + const { days = 7, page = 1, perPage = 30 } = data; + const from = pbTime(new Date(Date.now() - days * 86400000)); + + const sessions = new Map< + string, + { playerId?: string; started: string; lastSeen: string; pageViews: number; events: number; errors: number; user_agent?: string } + >(); + + await pbAdmin.pageCollection<{ + session_id?: string; + player?: string; + name: string; + user_agent?: string; + created: string; + }>( + "client_events", + { filter: `created >= '${from}'`, fields: "session_id,player,name,user_agent,created" }, + (rows) => { + for (const row of rows) { + if (!row.session_id) continue; + const session = sessions.get(row.session_id) ?? { + playerId: row.player, + started: row.created, + lastSeen: row.created, + pageViews: 0, + events: 0, + errors: 0, + user_agent: row.user_agent, + }; + session.playerId ||= row.player; + if (row.created < session.started) session.started = row.created; + if (row.created > session.lastSeen) session.lastSeen = row.created; + if (row.name === "page_view") session.pageViews++; + else if (!row.name.startsWith("vital.")) session.events++; + sessions.set(row.session_id, session); + } + } + ); + + await pbAdmin.pageCollection<{ session_id?: string; created: string }>( + "client_errors", + { filter: `created >= '${from}'`, fields: "session_id,created" }, + (rows) => { + for (const row of rows) { + if (!row.session_id) continue; + const session = sessions.get(row.session_id); + if (session) session.errors++; + } + } + ); + + const playerIds = new Set( + [...sessions.values()].map((s) => s.playerId).filter((id): id is string => !!id) + ); + const players = await playerInfoById(playerIds); + + const all: SessionSummary[] = [...sessions.entries()] + .map(([session_id, s]) => ({ + session_id, + player: s.playerId ? players.get(s.playerId) : undefined, + started: s.started, + lastSeen: s.lastSeen, + pageViews: s.pageViews, + events: s.events, + errors: s.errors, + user_agent: s.user_agent, + })) + .sort((a, b) => (a.lastSeen < b.lastSeen ? 1 : -1)); + + const start = (page - 1) * perPage; + return { + items: all.slice(start, start + perPage), + page, + perPage, + totalPages: Math.max(1, Math.ceil(all.length / perPage)), + totalItems: all.length, + }; + }) + ); + +const SESSION_WINDOW_PAD_MS = 30_000; +const MAX_TIMELINE_ENTRIES = 500; + +export const getSessionTimeline = createServerFn() + .validator(z.object({ sessionId: z.string().min(8).max(64) })) + .middleware([superTokensAdminFunctionMiddleware]) + .handler(async ({ data }) => + toServerResult(async () => { + const events = await pbAdmin.searchClientEvents({ + sessionId: data.sessionId, + perPage: 200, + sortBy: "created", + }); + const errors = await pbAdmin.searchClientErrors({ + sessionId: data.sessionId, + perPage: 200, + sortBy: "created", + }); + + const sessionErrors = errors.items; + if (events.items.length === 0 && sessionErrors.length === 0) { + throw new Error("Session not found"); + } + + const timestamps = [ + ...events.items.map((e) => e.created), + ...sessionErrors.map((e) => e.created), + ].sort(); + const started = timestamps[0]; + const lastSeen = timestamps[timestamps.length - 1]; + + const playerId = events.items.find((e) => typeof e.player === "string" && e.player) + ?.player as string | undefined; + + const entries: TimelineEntry[] = []; + + for (const event of events.items) { + if (event.name.startsWith("vital.")) continue; + entries.push({ + ts: event.created, + kind: event.name === "page_view" ? "page_view" : "event", + label: event.name === "page_view" ? event.path || event.route_id || "?" : event.name, + detail: event.name === "page_view" ? undefined : event.path, + }); + } + + for (const error of sessionErrors) { + entries.push({ + ts: error.created, + kind: "error", + label: error.message, + detail: error.path, + success: false, + }); + } + + // Server-fn activities have no session id; join by player inside the + // session's padded time window. + if (playerId) { + const windowFrom = pbTime(new Date(Date.parse(started.replace(" ", "T")) - SESSION_WINDOW_PAD_MS)); + const windowTo = pbTime(new Date(Date.parse(lastSeen.replace(" ", "T")) + SESSION_WINDOW_PAD_MS)); + const activities = await pbAdmin.searchActivities({ + player: playerId, + from: windowFrom, + to: windowTo, + perPage: 200, + sortBy: "created", + }); + for (const activity of activities.items) { + entries.push({ + ts: activity.created, + kind: "server_fn", + label: activity.name, + detail: activity.error, + success: activity.success, + duration: activity.duration, + }); + } + } + + entries.sort((a, b) => (a.ts < b.ts ? -1 : 1)); + + const players = await playerInfoById(new Set(playerId ? [playerId] : [])); + + return { + session: { + session_id: data.sessionId, + player: playerId ? players.get(playerId) : undefined, + started, + lastSeen, + pageViews: events.items.filter((e) => e.name === "page_view").length, + events: events.items.filter( + (e) => e.name !== "page_view" && !e.name.startsWith("vital.") + ).length, + errors: sessionErrors.length, + user_agent: events.items[0]?.user_agent, + }, + entries: entries.slice(0, MAX_TIMELINE_ENTRIES), + }; + }) + ); + export const getTelemetryRuntimeStatus = createServerFn() .middleware([superTokensAdminFunctionMiddleware]) .handler(async () => diff --git a/src/features/telemetry/types.ts b/src/features/telemetry/types.ts index 716f077..0924763 100644 --- a/src/features/telemetry/types.ts +++ b/src/features/telemetry/types.ts @@ -22,6 +22,33 @@ export interface ClientErrorGroup { }; } +export interface SessionSummary { + session_id: string; + player?: { id: string; first_name?: string; last_name?: string }; + started: string; + lastSeen: string; + pageViews: number; + events: number; + errors: number; + user_agent?: string; +} + +export type TimelineKind = "page_view" | "event" | "error" | "server_fn"; + +export interface TimelineEntry { + ts: string; + kind: TimelineKind; + label: string; + detail?: string; + success?: boolean; + duration?: number; +} + +export interface SessionTimeline { + session: SessionSummary; + entries: TimelineEntry[]; +} + export interface TelemetryRuntimeStatus { health: { status: "ok" | "degraded"; diff --git a/src/lib/pocketbase/services/activities.ts b/src/lib/pocketbase/services/activities.ts index a6d9bfb..780a344 100644 --- a/src/lib/pocketbase/services/activities.ts +++ b/src/lib/pocketbase/services/activities.ts @@ -40,6 +40,8 @@ export interface ActivitySearchParams { name?: string; player?: string; success?: boolean; + from?: string; + to?: string; sortBy?: string; } @@ -57,6 +59,8 @@ export function createActivitiesService(pb: PocketBase) { name, player, success, + from, + to, sortBy = "-created" } = params; @@ -74,6 +78,14 @@ export function createActivitiesService(pb: PocketBase) { filters.push(pbFilter(pb, "success = {:success}", { success })); } + if (from) { + filters.push(pbFilter(pb, "created >= {:from}", { from })); + } + + if (to) { + filters.push(pbFilter(pb, "created < {:to}", { to })); + } + const filterString = filters.length > 0 ? filters.join(" && ") : ""; const result = await pb.collection("activities").getList(page, perPage, { diff --git a/src/lib/pocketbase/services/telemetry.ts b/src/lib/pocketbase/services/telemetry.ts index a29f900..9f001d9 100644 --- a/src/lib/pocketbase/services/telemetry.ts +++ b/src/lib/pocketbase/services/telemetry.ts @@ -125,6 +125,7 @@ export interface ClientErrorSearchParams { groupHash?: string; resolved?: boolean; player?: string; + sessionId?: string; from?: string; to?: string; sortBy?: string; @@ -175,12 +176,13 @@ export function createTelemetryService(pb: PocketBase) { async searchClientErrors( params: ClientErrorSearchParams = {} ): Promise> { - const { page = 1, perPage = 100, groupHash, resolved, player, from, to, sortBy = "-created" } = params; + const { page = 1, perPage = 100, groupHash, resolved, player, sessionId, from, to, sortBy = "-created" } = params; const filters: string[] = []; if (groupHash) filters.push(pbFilter(pb, "group_hash = {:groupHash}", { groupHash })); if (resolved !== undefined) filters.push(pbFilter(pb, "resolved = {:resolved}", { resolved })); if (player) filters.push(pbFilter(pb, "player = {:player}", { player })); + if (sessionId) filters.push(pbFilter(pb, "session_id = {:sessionId}", { sessionId })); if (from) filters.push(pbFilter(pb, "created >= {:from}", { from })); if (to) filters.push(pbFilter(pb, "created < {:to}", { to })); diff --git a/src/locales/de/messages.po b/src/locales/de/messages.po index a6e83e1..f6fa61a 100644 --- a/src/locales/de/messages.po +++ b/src/locales/de/messages.po @@ -35,6 +35,20 @@ msgstr "(Aktiv)" msgid "{0, plural, one {# bracket in} other {# brackets in}}" msgstr "{0, plural, one {# Bracket in} other {# Brackets in}}" +#. placeholder {0}: data.session.errors +#. placeholder {0}: session.errors +#: src/features/telemetry/components/sessions/session-timeline.tsx:48 +#: src/features/telemetry/components/sessions/sessions-list.tsx:52 +msgid "{0, plural, one {# error} other {# errors}}" +msgstr "{0, plural, one {# Fehler} other {# Fehler}}" + +#. placeholder {0}: data.session.pageViews +#. placeholder {0}: session.pageViews +#: src/features/telemetry/components/sessions/session-timeline.tsx:44 +#: src/features/telemetry/components/sessions/sessions-list.tsx:47 +msgid "{0, plural, one {# page} other {# pages}}" +msgstr "{0, plural, one {# Seite} other {# Seiten}}" + #. placeholder {0}: enrolledPlayers.length #. placeholder {0}: players.length #: src/features/players/components/players-activity-table.tsx:100 @@ -57,11 +71,6 @@ msgstr "{0, plural, one {# Team} other {# Teams}}" msgid "{0, plural, one {# total activity} other {# total activities}}" msgstr "{0, plural, one {# Aktivität insgesamt} other {# Aktivitäten insgesamt}}" -#. placeholder {0}: result.totalItems -#: src/features/telemetry/components/client-events-table.tsx:86 -msgid "{0, plural, one {# total event} other {# total events}}" -msgstr "{0, plural, one {# Ereignis insgesamt} other {# Ereignisse insgesamt}}" - #. placeholder {0}: selectedConfig.total_group_matches #: src/features/tournaments/components/setup-group-stage.tsx:249 msgid "{0, plural, one {# total group stage match} other {# total group stage matches}}" @@ -480,7 +489,8 @@ msgstr "Ein unerwarteter Fehler ist aufgetreten. Bitte versuche es erneut." msgid "Announce matchup" msgstr "Matchup ansagen" -#: src/features/telemetry/components/client-events-table.tsx:48 +#: src/features/telemetry/components/sessions/sessions-list.tsx:44 +#: src/features/telemetry/components/sessions/sessions-list.tsx:61 msgid "Anonymous" msgstr "Anonym" @@ -846,7 +856,6 @@ msgid "Create Tournament" msgstr "Turnier erstellen" #: src/features/activities/components/activities-table.tsx:198 -#: src/features/telemetry/components/client-events-table.tsx:148 msgid "Created" msgstr "Erstellt" @@ -1101,18 +1110,6 @@ msgstr "Fehler: {errorMessage}" msgid "Errors" msgstr "Fehler" -#: src/features/telemetry/components/client-events-table.tsx:112 -msgid "Event Details" -msgstr "Ereignisdetails" - -#: src/features/telemetry/components/client-events-table.tsx:69 -msgid "Event name" -msgstr "Ereignisname" - -#: src/app/routes/_authed/admin/telemetry.tsx:54 -msgid "Events" -msgstr "Ereignisse" - #: src/features/tournaments/components/group-stage-view.tsx:368 msgid "Expand standings" msgstr "Tabelle erweitern" @@ -1429,6 +1426,10 @@ msgstr "N" msgid "Language" msgstr "Sprache" +#: src/features/telemetry/components/sessions/sessions-list.tsx:74 +msgid "Last 7 days" +msgstr "Letzte 7 Tage" + #: src/features/login/components/name-prompt.tsx:43 #: src/features/players/components/profile/name-form.tsx:68 msgid "Last Name" @@ -1638,7 +1639,6 @@ msgid "MMR Calculation:" msgstr "MMR-Berechnung:" #: src/features/teams/components/team-form/index.tsx:200 -#: src/features/telemetry/components/client-events-table.tsx:118 #: src/features/tournaments/components/tournament-form.tsx:137 msgid "Name" msgstr "Name" @@ -1734,10 +1734,6 @@ msgstr "Keine Abzeichen verfügbar" msgid "No devices found." msgstr "Keine Geräte gefunden." -#: src/features/telemetry/components/client-events-table.tsx:93 -msgid "No Events Found" -msgstr "Keine Ereignisse gefunden" - #: src/features/tournaments/components/group-stage-view.tsx:294 msgid "No groups have been created yet" msgstr "Es wurden noch keine Gruppen erstellt" @@ -1776,6 +1772,10 @@ msgstr "Keine Tipps" msgid "No results found" msgstr "Keine Ergebnisse gefunden" +#: src/features/telemetry/components/sessions/sessions-list.tsx:38 +msgid "No sessions yet" +msgstr "Noch keine Sitzungen" + #: src/app/routes/api/spotify/resume.ts:37 msgid "No snapshot provided" msgstr "Kein Snapshot angegeben" @@ -1949,10 +1949,6 @@ msgstr "Partner-Zuweisungen" msgid "Partners will be randomly assigned when enrollment closes" msgstr "Partner werden nach Anmeldeschluss zufällig zugewiesen" -#: src/features/telemetry/components/client-events-table.tsx:121 -msgid "Path" -msgstr "Pfad" - #: src/features/spotify/components/spotify-controls-bar.tsx:116 msgid "Pause" msgstr "Pause" @@ -1995,8 +1991,6 @@ msgstr "Wiedergabesteuerung fehlgeschlagen" #: src/features/activities/components/activities-table.tsx:98 #: src/features/activities/components/activities-table.tsx:188 #: src/features/admin/components/award-badges.tsx:31 -#: src/features/telemetry/components/client-events-table.tsx:72 -#: src/features/telemetry/components/client-events-table.tsx:119 msgid "Player" msgstr "Spieler" @@ -2076,10 +2070,6 @@ msgstr "Profil" msgid "Progress " msgstr "Fortschritt " -#: src/features/telemetry/components/client-events-table.tsx:135 -msgid "Props" -msgstr "Props" - #: src/app/routes/_authed/tournaments/$id.predictions_.$playerId.tsx:106 #: src/features/predictions/components/prediction-leaderboard.tsx:261 msgid "PTS" @@ -2167,10 +2157,6 @@ msgstr "rofl" msgid "Round {0}" msgstr "Runde {0}" -#: src/features/telemetry/components/client-events-table.tsx:124 -msgid "Route" -msgstr "Route" - #: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:123 #: src/features/tournaments/components/upcoming-tournament/rules-list-button.tsx:23 msgid "Rules" @@ -2351,10 +2337,7 @@ msgstr "Serverfunktionen" msgid "serverFn name" msgstr "serverFn-Name" -#: src/features/telemetry/components/client-events-table.tsx:127 -msgid "Session" -msgstr "Sitzung" - +#: src/app/routes/_authed/admin/telemetry.tsx:54 #: src/features/telemetry/components/usage-screen.tsx:177 msgid "Sessions" msgstr "Sitzungen" @@ -2809,14 +2792,9 @@ msgid "Usage" msgstr "Nutzung" #: src/features/activities/components/activities-table.tsx:202 -#: src/features/telemetry/components/client-events-table.tsx:145 msgid "User Agent" msgstr "User-Agent" -#: src/features/telemetry/components/client-events-table.tsx:130 -msgid "Value" -msgstr "Wert" - #: src/features/bracket/components/match-card.tsx:201 #: src/features/bracket/components/match-card.tsx:226 msgid "versus" diff --git a/src/locales/en/messages.po b/src/locales/en/messages.po index da0c7e5..817065e 100644 --- a/src/locales/en/messages.po +++ b/src/locales/en/messages.po @@ -35,6 +35,20 @@ msgstr "(Active)" msgid "{0, plural, one {# bracket in} other {# brackets in}}" msgstr "{0, plural, one {# bracket in} other {# brackets in}}" +#. placeholder {0}: data.session.errors +#. placeholder {0}: session.errors +#: src/features/telemetry/components/sessions/session-timeline.tsx:48 +#: src/features/telemetry/components/sessions/sessions-list.tsx:52 +msgid "{0, plural, one {# error} other {# errors}}" +msgstr "{0, plural, one {# error} other {# errors}}" + +#. placeholder {0}: data.session.pageViews +#. placeholder {0}: session.pageViews +#: src/features/telemetry/components/sessions/session-timeline.tsx:44 +#: src/features/telemetry/components/sessions/sessions-list.tsx:47 +msgid "{0, plural, one {# page} other {# pages}}" +msgstr "{0, plural, one {# page} other {# pages}}" + #. placeholder {0}: enrolledPlayers.length #. placeholder {0}: players.length #: src/features/players/components/players-activity-table.tsx:100 @@ -57,11 +71,6 @@ msgstr "{0, plural, one {# team} other {# teams}}" msgid "{0, plural, one {# total activity} other {# total activities}}" msgstr "{0, plural, one {# total activity} other {# total activities}}" -#. placeholder {0}: result.totalItems -#: src/features/telemetry/components/client-events-table.tsx:86 -msgid "{0, plural, one {# total event} other {# total events}}" -msgstr "{0, plural, one {# total event} other {# total events}}" - #. placeholder {0}: selectedConfig.total_group_matches #: src/features/tournaments/components/setup-group-stage.tsx:249 msgid "{0, plural, one {# total group stage match} other {# total group stage matches}}" @@ -480,7 +489,8 @@ msgstr "An unexpected error occurred. Please try again." msgid "Announce matchup" msgstr "Announce matchup" -#: src/features/telemetry/components/client-events-table.tsx:48 +#: src/features/telemetry/components/sessions/sessions-list.tsx:44 +#: src/features/telemetry/components/sessions/sessions-list.tsx:61 msgid "Anonymous" msgstr "Anonymous" @@ -846,7 +856,6 @@ msgid "Create Tournament" msgstr "Create Tournament" #: src/features/activities/components/activities-table.tsx:198 -#: src/features/telemetry/components/client-events-table.tsx:148 msgid "Created" msgstr "Created" @@ -1101,18 +1110,6 @@ msgstr "Error: {errorMessage}" msgid "Errors" msgstr "Errors" -#: src/features/telemetry/components/client-events-table.tsx:112 -msgid "Event Details" -msgstr "Event Details" - -#: src/features/telemetry/components/client-events-table.tsx:69 -msgid "Event name" -msgstr "Event name" - -#: src/app/routes/_authed/admin/telemetry.tsx:54 -msgid "Events" -msgstr "Events" - #: src/features/tournaments/components/group-stage-view.tsx:368 msgid "Expand standings" msgstr "Expand standings" @@ -1429,6 +1426,10 @@ msgstr "L" msgid "Language" msgstr "Language" +#: src/features/telemetry/components/sessions/sessions-list.tsx:74 +msgid "Last 7 days" +msgstr "Last 7 days" + #: src/features/login/components/name-prompt.tsx:43 #: src/features/players/components/profile/name-form.tsx:68 msgid "Last Name" @@ -1638,7 +1639,6 @@ msgid "MMR Calculation:" msgstr "MMR Calculation:" #: src/features/teams/components/team-form/index.tsx:200 -#: src/features/telemetry/components/client-events-table.tsx:118 #: src/features/tournaments/components/tournament-form.tsx:137 msgid "Name" msgstr "Name" @@ -1734,10 +1734,6 @@ msgstr "No Badges Available" msgid "No devices found." msgstr "No devices found." -#: src/features/telemetry/components/client-events-table.tsx:93 -msgid "No Events Found" -msgstr "No Events Found" - #: src/features/tournaments/components/group-stage-view.tsx:294 msgid "No groups have been created yet" msgstr "No groups have been created yet" @@ -1776,6 +1772,10 @@ msgstr "No predictions" msgid "No results found" msgstr "No results found" +#: src/features/telemetry/components/sessions/sessions-list.tsx:38 +msgid "No sessions yet" +msgstr "No sessions yet" + #: src/app/routes/api/spotify/resume.ts:37 msgid "No snapshot provided" msgstr "No snapshot provided" @@ -1949,10 +1949,6 @@ msgstr "Partner Assignments" msgid "Partners will be randomly assigned when enrollment closes" msgstr "Partners will be randomly assigned when enrollment closes" -#: src/features/telemetry/components/client-events-table.tsx:121 -msgid "Path" -msgstr "Path" - #: src/features/spotify/components/spotify-controls-bar.tsx:116 msgid "Pause" msgstr "Pause" @@ -1995,8 +1991,6 @@ msgstr "Playback control failed" #: src/features/activities/components/activities-table.tsx:98 #: src/features/activities/components/activities-table.tsx:188 #: src/features/admin/components/award-badges.tsx:31 -#: src/features/telemetry/components/client-events-table.tsx:72 -#: src/features/telemetry/components/client-events-table.tsx:119 msgid "Player" msgstr "Player" @@ -2076,10 +2070,6 @@ msgstr "Profile" msgid "Progress " msgstr "Progress " -#: src/features/telemetry/components/client-events-table.tsx:135 -msgid "Props" -msgstr "Props" - #: src/app/routes/_authed/tournaments/$id.predictions_.$playerId.tsx:106 #: src/features/predictions/components/prediction-leaderboard.tsx:261 msgid "PTS" @@ -2167,10 +2157,6 @@ msgstr "rofl" msgid "Round {0}" msgstr "Round {0}" -#: src/features/telemetry/components/client-events-table.tsx:124 -msgid "Route" -msgstr "Route" - #: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:123 #: src/features/tournaments/components/upcoming-tournament/rules-list-button.tsx:23 msgid "Rules" @@ -2351,10 +2337,7 @@ msgstr "Server functions" msgid "serverFn name" msgstr "serverFn name" -#: src/features/telemetry/components/client-events-table.tsx:127 -msgid "Session" -msgstr "Session" - +#: src/app/routes/_authed/admin/telemetry.tsx:54 #: src/features/telemetry/components/usage-screen.tsx:177 msgid "Sessions" msgstr "Sessions" @@ -2809,14 +2792,9 @@ msgid "Usage" msgstr "Usage" #: src/features/activities/components/activities-table.tsx:202 -#: src/features/telemetry/components/client-events-table.tsx:145 msgid "User Agent" msgstr "User Agent" -#: src/features/telemetry/components/client-events-table.tsx:130 -msgid "Value" -msgstr "Value" - #: src/features/bracket/components/match-card.tsx:201 #: src/features/bracket/components/match-card.tsx:226 msgid "versus" diff --git a/src/locales/es/messages.po b/src/locales/es/messages.po index 9a42bbf..2039edb 100644 --- a/src/locales/es/messages.po +++ b/src/locales/es/messages.po @@ -35,6 +35,20 @@ msgstr "(Activo)" msgid "{0, plural, one {# bracket in} other {# brackets in}}" msgstr "{0, plural, one {# bracket en} other {# brackets en}}" +#. placeholder {0}: data.session.errors +#. placeholder {0}: session.errors +#: src/features/telemetry/components/sessions/session-timeline.tsx:48 +#: src/features/telemetry/components/sessions/sessions-list.tsx:52 +msgid "{0, plural, one {# error} other {# errors}}" +msgstr "{0, plural, one {# error} other {# errores}}" + +#. placeholder {0}: data.session.pageViews +#. placeholder {0}: session.pageViews +#: src/features/telemetry/components/sessions/session-timeline.tsx:44 +#: src/features/telemetry/components/sessions/sessions-list.tsx:47 +msgid "{0, plural, one {# page} other {# pages}}" +msgstr "{0, plural, one {# página} other {# páginas}}" + #. placeholder {0}: enrolledPlayers.length #. placeholder {0}: players.length #: src/features/players/components/players-activity-table.tsx:100 @@ -57,11 +71,6 @@ msgstr "{0, plural, one {# equipo} other {# equipos}}" msgid "{0, plural, one {# total activity} other {# total activities}}" msgstr "{0, plural, one {# actividad total} other {# actividades totales}}" -#. placeholder {0}: result.totalItems -#: src/features/telemetry/components/client-events-table.tsx:86 -msgid "{0, plural, one {# total event} other {# total events}}" -msgstr "{0, plural, one {# evento total} other {# eventos totales}}" - #. placeholder {0}: selectedConfig.total_group_matches #: src/features/tournaments/components/setup-group-stage.tsx:249 msgid "{0, plural, one {# total group stage match} other {# total group stage matches}}" @@ -480,7 +489,8 @@ msgstr "Ocurrió un error inesperado. Intenta de nuevo." msgid "Announce matchup" msgstr "Anunciar enfrentamiento" -#: src/features/telemetry/components/client-events-table.tsx:48 +#: src/features/telemetry/components/sessions/sessions-list.tsx:44 +#: src/features/telemetry/components/sessions/sessions-list.tsx:61 msgid "Anonymous" msgstr "Anónimo" @@ -846,7 +856,6 @@ msgid "Create Tournament" msgstr "Crear torneo" #: src/features/activities/components/activities-table.tsx:198 -#: src/features/telemetry/components/client-events-table.tsx:148 msgid "Created" msgstr "Creado" @@ -1101,18 +1110,6 @@ msgstr "Error: {errorMessage}" msgid "Errors" msgstr "Errores" -#: src/features/telemetry/components/client-events-table.tsx:112 -msgid "Event Details" -msgstr "Detalles del evento" - -#: src/features/telemetry/components/client-events-table.tsx:69 -msgid "Event name" -msgstr "Nombre del evento" - -#: src/app/routes/_authed/admin/telemetry.tsx:54 -msgid "Events" -msgstr "Eventos" - #: src/features/tournaments/components/group-stage-view.tsx:368 msgid "Expand standings" msgstr "Expandir tabla de posiciones" @@ -1429,6 +1426,10 @@ msgstr "D" msgid "Language" msgstr "Idioma" +#: src/features/telemetry/components/sessions/sessions-list.tsx:74 +msgid "Last 7 days" +msgstr "Últimos 7 días" + #: src/features/login/components/name-prompt.tsx:43 #: src/features/players/components/profile/name-form.tsx:68 msgid "Last Name" @@ -1638,7 +1639,6 @@ msgid "MMR Calculation:" msgstr "Cálculo de MMR:" #: src/features/teams/components/team-form/index.tsx:200 -#: src/features/telemetry/components/client-events-table.tsx:118 #: src/features/tournaments/components/tournament-form.tsx:137 msgid "Name" msgstr "Nombre" @@ -1734,10 +1734,6 @@ msgstr "No hay insignias disponibles" msgid "No devices found." msgstr "No se encontraron dispositivos." -#: src/features/telemetry/components/client-events-table.tsx:93 -msgid "No Events Found" -msgstr "No se encontraron eventos" - #: src/features/tournaments/components/group-stage-view.tsx:294 msgid "No groups have been created yet" msgstr "Aún no se han creado grupos" @@ -1776,6 +1772,10 @@ msgstr "Sin predicciones" msgid "No results found" msgstr "No se encontraron resultados" +#: src/features/telemetry/components/sessions/sessions-list.tsx:38 +msgid "No sessions yet" +msgstr "Aún no hay sesiones" + #: src/app/routes/api/spotify/resume.ts:37 msgid "No snapshot provided" msgstr "No se proporcionó ninguna instantánea" @@ -1949,10 +1949,6 @@ msgstr "Asignación de compañeros" msgid "Partners will be randomly assigned when enrollment closes" msgstr "Los compañeros se asignarán al azar cuando cierre la inscripción" -#: src/features/telemetry/components/client-events-table.tsx:121 -msgid "Path" -msgstr "Ruta" - #: src/features/spotify/components/spotify-controls-bar.tsx:116 msgid "Pause" msgstr "Pausar" @@ -1995,8 +1991,6 @@ msgstr "Falló el control de reproducción" #: src/features/activities/components/activities-table.tsx:98 #: src/features/activities/components/activities-table.tsx:188 #: src/features/admin/components/award-badges.tsx:31 -#: src/features/telemetry/components/client-events-table.tsx:72 -#: src/features/telemetry/components/client-events-table.tsx:119 msgid "Player" msgstr "Jugador" @@ -2076,10 +2070,6 @@ msgstr "Perfil" msgid "Progress " msgstr "Progreso " -#: src/features/telemetry/components/client-events-table.tsx:135 -msgid "Props" -msgstr "Props" - #: src/app/routes/_authed/tournaments/$id.predictions_.$playerId.tsx:106 #: src/features/predictions/components/prediction-leaderboard.tsx:261 msgid "PTS" @@ -2167,10 +2157,6 @@ msgstr "carcajada" msgid "Round {0}" msgstr "Ronda {0}" -#: src/features/telemetry/components/client-events-table.tsx:124 -msgid "Route" -msgstr "Ruta" - #: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:123 #: src/features/tournaments/components/upcoming-tournament/rules-list-button.tsx:23 msgid "Rules" @@ -2351,10 +2337,7 @@ msgstr "Funciones del servidor" msgid "serverFn name" msgstr "nombre de serverFn" -#: src/features/telemetry/components/client-events-table.tsx:127 -msgid "Session" -msgstr "Sesión" - +#: src/app/routes/_authed/admin/telemetry.tsx:54 #: src/features/telemetry/components/usage-screen.tsx:177 msgid "Sessions" msgstr "Sesiones" @@ -2809,14 +2792,9 @@ msgid "Usage" msgstr "Uso" #: src/features/activities/components/activities-table.tsx:202 -#: src/features/telemetry/components/client-events-table.tsx:145 msgid "User Agent" msgstr "User Agent" -#: src/features/telemetry/components/client-events-table.tsx:130 -msgid "Value" -msgstr "Valor" - #: src/features/bracket/components/match-card.tsx:201 #: src/features/bracket/components/match-card.tsx:226 msgid "versus" diff --git a/src/locales/ja/messages.po b/src/locales/ja/messages.po index fc8e375..fb0e762 100644 --- a/src/locales/ja/messages.po +++ b/src/locales/ja/messages.po @@ -35,6 +35,20 @@ msgstr "(アクティブ)" msgid "{0, plural, one {# bracket in} other {# brackets in}}" msgstr "{0, plural, one {#ブラケット参加} other {#ブラケット参加}}" +#. placeholder {0}: data.session.errors +#. placeholder {0}: session.errors +#: src/features/telemetry/components/sessions/session-timeline.tsx:48 +#: src/features/telemetry/components/sessions/sessions-list.tsx:52 +msgid "{0, plural, one {# error} other {# errors}}" +msgstr "{0, plural, one {# 件のエラー} other {# 件のエラー}}" + +#. placeholder {0}: data.session.pageViews +#. placeholder {0}: session.pageViews +#: src/features/telemetry/components/sessions/session-timeline.tsx:44 +#: src/features/telemetry/components/sessions/sessions-list.tsx:47 +msgid "{0, plural, one {# page} other {# pages}}" +msgstr "{0, plural, one {# ページ} other {# ページ}}" + #. placeholder {0}: enrolledPlayers.length #. placeholder {0}: players.length #: src/features/players/components/players-activity-table.tsx:100 @@ -57,11 +71,6 @@ msgstr "{0, plural, one {#チーム} other {#チーム}}" msgid "{0, plural, one {# total activity} other {# total activities}}" msgstr "{0, plural, one {アクティビティ合計#件} other {アクティビティ合計#件}}" -#. placeholder {0}: result.totalItems -#: src/features/telemetry/components/client-events-table.tsx:86 -msgid "{0, plural, one {# total event} other {# total events}}" -msgstr "{0, plural, one {イベント合計#件} other {イベント合計#件}}" - #. placeholder {0}: selectedConfig.total_group_matches #: src/features/tournaments/components/setup-group-stage.tsx:249 msgid "{0, plural, one {# total group stage match} other {# total group stage matches}}" @@ -480,7 +489,8 @@ msgstr "予期しないエラーが発生しました。もう一度お試しく msgid "Announce matchup" msgstr "対戦カードを発表" -#: src/features/telemetry/components/client-events-table.tsx:48 +#: src/features/telemetry/components/sessions/sessions-list.tsx:44 +#: src/features/telemetry/components/sessions/sessions-list.tsx:61 msgid "Anonymous" msgstr "匿名" @@ -846,7 +856,6 @@ msgid "Create Tournament" msgstr "トーナメントを作成" #: src/features/activities/components/activities-table.tsx:198 -#: src/features/telemetry/components/client-events-table.tsx:148 msgid "Created" msgstr "作成日" @@ -1101,18 +1110,6 @@ msgstr "エラー: {errorMessage}" msgid "Errors" msgstr "エラー" -#: src/features/telemetry/components/client-events-table.tsx:112 -msgid "Event Details" -msgstr "イベント詳細" - -#: src/features/telemetry/components/client-events-table.tsx:69 -msgid "Event name" -msgstr "イベント名" - -#: src/app/routes/_authed/admin/telemetry.tsx:54 -msgid "Events" -msgstr "イベント" - #: src/features/tournaments/components/group-stage-view.tsx:368 msgid "Expand standings" msgstr "順位表を展開" @@ -1429,6 +1426,10 @@ msgstr "L" msgid "Language" msgstr "言語" +#: src/features/telemetry/components/sessions/sessions-list.tsx:74 +msgid "Last 7 days" +msgstr "過去7日間" + #: src/features/login/components/name-prompt.tsx:43 #: src/features/players/components/profile/name-form.tsx:68 msgid "Last Name" @@ -1638,7 +1639,6 @@ msgid "MMR Calculation:" msgstr "MMR計算:" #: src/features/teams/components/team-form/index.tsx:200 -#: src/features/telemetry/components/client-events-table.tsx:118 #: src/features/tournaments/components/tournament-form.tsx:137 msgid "Name" msgstr "名前" @@ -1734,10 +1734,6 @@ msgstr "利用可能なバッジがありません" msgid "No devices found." msgstr "デバイスが見つかりません。" -#: src/features/telemetry/components/client-events-table.tsx:93 -msgid "No Events Found" -msgstr "イベントが見つかりません" - #: src/features/tournaments/components/group-stage-view.tsx:294 msgid "No groups have been created yet" msgstr "まだグループが作成されていません" @@ -1776,6 +1772,10 @@ msgstr "予想はありません" msgid "No results found" msgstr "結果が見つかりません" +#: src/features/telemetry/components/sessions/sessions-list.tsx:38 +msgid "No sessions yet" +msgstr "まだセッションがありません" + #: src/app/routes/api/spotify/resume.ts:37 msgid "No snapshot provided" msgstr "スナップショットが提供されていません" @@ -1949,10 +1949,6 @@ msgstr "パートナー割り当て" msgid "Partners will be randomly assigned when enrollment closes" msgstr "登録が締め切られると、パートナーはランダムに割り当てられます" -#: src/features/telemetry/components/client-events-table.tsx:121 -msgid "Path" -msgstr "パス" - #: src/features/spotify/components/spotify-controls-bar.tsx:116 msgid "Pause" msgstr "一時停止" @@ -1995,8 +1991,6 @@ msgstr "再生コントロールに失敗しました" #: src/features/activities/components/activities-table.tsx:98 #: src/features/activities/components/activities-table.tsx:188 #: src/features/admin/components/award-badges.tsx:31 -#: src/features/telemetry/components/client-events-table.tsx:72 -#: src/features/telemetry/components/client-events-table.tsx:119 msgid "Player" msgstr "プレイヤー" @@ -2076,10 +2070,6 @@ msgstr "プロフィール" msgid "Progress " msgstr "進行状況 " -#: src/features/telemetry/components/client-events-table.tsx:135 -msgid "Props" -msgstr "プロパティ" - #: src/app/routes/_authed/tournaments/$id.predictions_.$playerId.tsx:106 #: src/features/predictions/components/prediction-leaderboard.tsx:261 msgid "PTS" @@ -2167,10 +2157,6 @@ msgstr "大笑い" msgid "Round {0}" msgstr "ラウンド{0}" -#: src/features/telemetry/components/client-events-table.tsx:124 -msgid "Route" -msgstr "ルート" - #: src/features/tournaments/components/started-tournament/sections/nav-grid.tsx:123 #: src/features/tournaments/components/upcoming-tournament/rules-list-button.tsx:23 msgid "Rules" @@ -2351,10 +2337,7 @@ msgstr "サーバー関数" msgid "serverFn name" msgstr "serverFn名" -#: src/features/telemetry/components/client-events-table.tsx:127 -msgid "Session" -msgstr "セッション" - +#: src/app/routes/_authed/admin/telemetry.tsx:54 #: src/features/telemetry/components/usage-screen.tsx:177 msgid "Sessions" msgstr "セッション数" @@ -2809,14 +2792,9 @@ msgid "Usage" msgstr "利用状況" #: src/features/activities/components/activities-table.tsx:202 -#: src/features/telemetry/components/client-events-table.tsx:145 msgid "User Agent" msgstr "ユーザーエージェント" -#: src/features/telemetry/components/client-events-table.tsx:130 -msgid "Value" -msgstr "値" - #: src/features/bracket/components/match-card.tsx:201 #: src/features/bracket/components/match-card.tsx:226 msgid "versus" -- 2.54.0 From c05bdec252e43a2785ad0c84be23328768607a9f Mon Sep 17 00:00:00 2001 From: yohlo Date: Tue, 25 Aug 2026 22:59:00 -0700 Subject: [PATCH 16/33] fix(sheet): native scroll container so vaul drag detection works --- src/components/sheet/sheet.tsx | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/components/sheet/sheet.tsx b/src/components/sheet/sheet.tsx index 99494c4..96905d3 100644 --- a/src/components/sheet/sheet.tsx +++ b/src/components/sheet/sheet.tsx @@ -9,7 +9,7 @@ import { import { useIsMobile } from "@/hooks/use-is-mobile"; import Drawer from "./drawer"; import Modal from "./modal"; -import { ScrollArea, Flex, Loader } from "@mantine/core"; +import { Box, Flex, Loader } from "@mantine/core"; interface SheetProps extends PropsWithChildren { title?: string; @@ -60,14 +60,19 @@ const Sheet: React.FC = ({ title, children, opened, onChange }) => { }> - {children} - +
); -- 2.54.0 From 9f5fac27531baa47264ec7183abae5f580a1b16f Mon Sep 17 00:00:00 2001 From: yohlo Date: Tue, 25 Aug 2026 23:04:14 -0700 Subject: [PATCH 17/33] feat(telemetry): exclude configured phones from usage telemetry - TELEMETRY_EXCLUDE_PHONES resolves to player ids at runtime, cached 10m - excluded players write no activities, client events, or vitals - their client errors are still captured --- src/app/routes/api/telemetry/beacon.ts | 7 +++ src/lib/telemetry/auth-events.server.ts | 5 +++ src/lib/telemetry/exclusions.server.test.ts | 37 ++++++++++++++++ src/lib/telemetry/exclusions.server.ts | 49 +++++++++++++++++++++ src/utils/activities.test.ts | 19 ++++++++ src/utils/activities.ts | 13 +++--- 6 files changed, 125 insertions(+), 5 deletions(-) create mode 100644 src/lib/telemetry/exclusions.server.test.ts create mode 100644 src/lib/telemetry/exclusions.server.ts diff --git a/src/app/routes/api/telemetry/beacon.ts b/src/app/routes/api/telemetry/beacon.ts index 8ca7ec4..4c73e43 100644 --- a/src/app/routes/api/telemetry/beacon.ts +++ b/src/app/routes/api/telemetry/beacon.ts @@ -41,6 +41,13 @@ const writeEvents = async ( events: BeaconEvent[], base: { sessionId: string; playerId?: string; userAgent?: string } ) => { + const { isExcludedPlayerId } = await import("@/lib/telemetry/exclusions.server"); + if (base.playerId && (await isExcludedPlayerId(base.playerId))) { + // Excluded players still report errors; their usage stays out. + events = events.filter((event) => event.kind === "error"); + if (events.length === 0) return; + } + const { pbAdmin } = await import("@/lib/pocketbase/client"); await pbAdmin.authPromise; diff --git a/src/lib/telemetry/auth-events.server.ts b/src/lib/telemetry/auth-events.server.ts index 9623b72..a6610df 100644 --- a/src/lib/telemetry/auth-events.server.ts +++ b/src/lib/telemetry/auth-events.server.ts @@ -1,4 +1,5 @@ import { Logger } from "@/lib/logger"; +import { isExcludedPhone, isExcludedPlayerId } from "./exclusions.server"; const logger = new Logger("Telemetry"); @@ -12,6 +13,8 @@ interface AuthEventInput { export const recordAuthEvent = (name: string, input: AuthEventInput) => { void (async () => { try { + if (isExcludedPhone(input.phone)) return; + const { pbAdmin } = await import("@/lib/pocketbase/client"); await pbAdmin.authPromise; @@ -35,6 +38,8 @@ export const recordAuthEvent = (name: string, input: AuthEventInput) => { } catch {} } + if (await isExcludedPlayerId(playerId)) return; + await pbAdmin.createActivity({ name, player: playerId, diff --git a/src/lib/telemetry/exclusions.server.test.ts b/src/lib/telemetry/exclusions.server.test.ts new file mode 100644 index 0000000..d169b99 --- /dev/null +++ b/src/lib/telemetry/exclusions.server.test.ts @@ -0,0 +1,37 @@ +import { afterEach, describe, expect, it, vi } from "vitest"; + +vi.mock("@/lib/logger", () => ({ + Logger: class { + error() {} + info() {} + }, +})); + +import { isExcludedPhone } from "./exclusions.server"; + +const ENV_KEY = "TELEMETRY_EXCLUDE_PHONES"; + +afterEach(() => { + delete process.env[ENV_KEY]; +}); + +describe("isExcludedPhone", () => { + it("matches exact and formatted variants of a configured phone", () => { + process.env[ENV_KEY] = "+17135550142"; + expect(isExcludedPhone("+17135550142")).toBe(true); + expect(isExcludedPhone("+1 (713) 555-0142")).toBe(true); + expect(isExcludedPhone("+17135550143")).toBe(false); + }); + + it("supports a comma-separated list with whitespace", () => { + process.env[ENV_KEY] = "+17135550142, +14155550100"; + expect(isExcludedPhone("+14155550100")).toBe(true); + }); + + it("excludes nothing when unset or empty", () => { + expect(isExcludedPhone("+17135550142")).toBe(false); + process.env[ENV_KEY] = ""; + expect(isExcludedPhone("+17135550142")).toBe(false); + expect(isExcludedPhone(undefined)).toBe(false); + }); +}); diff --git a/src/lib/telemetry/exclusions.server.ts b/src/lib/telemetry/exclusions.server.ts new file mode 100644 index 0000000..c25dc7c --- /dev/null +++ b/src/lib/telemetry/exclusions.server.ts @@ -0,0 +1,49 @@ +import { Logger } from "@/lib/logger"; + +const logger = new Logger("Telemetry"); + +const CACHE_TTL_MS = 10 * 60 * 1000; + +const normalizePhone = (phone: string): string => phone.replace(/[^\d+]/g, ""); + +const excludedPhones = (): string[] => + (process.env.TELEMETRY_EXCLUDE_PHONES ?? "") + .split(",") + .map((phone) => normalizePhone(phone.trim())) + .filter(Boolean); + +export const isExcludedPhone = (phone?: string): boolean => + !!phone && excludedPhones().includes(normalizePhone(phone)); + +let cache: { playerIds: Set; expiresAt: number } | null = null; + +export const getExcludedPlayerIds = async (): Promise> => { + const now = Date.now(); + if (cache && cache.expiresAt > now) return cache.playerIds; + + const playerIds = new Set(); + const phones = excludedPhones(); + + if (phones.length > 0) { + try { + const SuperTokens = (await import("supertokens-node")).default; + const { pbAdmin } = await import("@/lib/pocketbase/client"); + for (const phoneNumber of phones) { + const users = await SuperTokens.listUsersByAccountInfo("public", { phoneNumber }); + const authId = users[0]?.id; + if (!authId) continue; + const player = await pbAdmin.getPlayerByAuthId(authId); + if (player) playerIds.add(player.id); + } + } catch (error) { + logger.error("Failed to resolve telemetry exclusions", error); + return playerIds; + } + } + + cache = { playerIds, expiresAt: now + CACHE_TTL_MS }; + return playerIds; +}; + +export const isExcludedPlayerId = async (playerId?: string): Promise => + !!playerId && (await getExcludedPlayerIds()).has(playerId); diff --git a/src/utils/activities.test.ts b/src/utils/activities.test.ts index f412025..ae18c22 100644 --- a/src/utils/activities.test.ts +++ b/src/utils/activities.test.ts @@ -20,6 +20,11 @@ vi.mock("@/lib/logger", () => ({ }, })); +vi.mock("@/lib/telemetry/exclusions.server", () => ({ + isExcludedPlayerId: async (playerId?: string) => playerId === "excluded-player", + isExcludedPhone: () => false, +})); + import { recordDeniedServerFn, serverFnLoggingMiddleware } from "./activities"; import { setRequestActor } from "@/lib/telemetry/request-context.server"; import { redirect } from "@tanstack/react-router"; @@ -303,6 +308,20 @@ describe("serverFnLoggingMiddleware control flow and dedup", () => { expect(h.createActivity.mock.calls[0][0].error).toContain("FORBIDDEN"); }); + it("does not record activity for excluded players", async () => { + setRequest("http://localhost:3000/_serverFn/doThing"); + setRequestActor(h.request as unknown as Request, { playerId: "excluded-player" }); + + await runMiddleware({ + next: async () => successEnvelope, + data: undefined, + context: {}, + }); + + await flushWrites(); + expect(h.createActivity).not.toHaveBeenCalled(); + }); + it("prefers the stashed request actor over middleware context", async () => { setRequest("http://localhost:3000/_serverFn/doThing"); setRequestActor(h.request as unknown as Request, { playerId: "p9" }); diff --git a/src/utils/activities.ts b/src/utils/activities.ts index 5bc92c5..5490a8d 100644 --- a/src/utils/activities.ts +++ b/src/utils/activities.ts @@ -13,14 +13,17 @@ import type { ActivityInput } from "@/lib/pocketbase/services/activities"; const logger = new Logger("Activities"); export const recordActivity = (activity: ActivityInput) => { - import("@/lib/pocketbase/client") - .then(async ({ pbAdmin }) => { + void (async () => { + try { + const { isExcludedPlayerId } = await import("@/lib/telemetry/exclusions.server"); + if (await isExcludedPlayerId(activity.player)) return; + const { pbAdmin } = await import("@/lib/pocketbase/client"); await pbAdmin.authPromise; await pbAdmin.createActivity(activity); - }) - .catch((activityError) => { + } catch (activityError) { logger.error("Failed to record activity", activityError); - }); + } + })(); }; const auditErrorMessage = ( -- 2.54.0 From 6dff5f19c1f960ec9c32ebb27b085aea99e31a07 Mon Sep 17 00:00:00 2001 From: yohlo Date: Tue, 25 Aug 2026 23:04:31 -0700 Subject: [PATCH 18/33] chore(k8s): optional telemetry_exclude_phones secret env --- k8s/base/app-deployment.yaml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/k8s/base/app-deployment.yaml b/k8s/base/app-deployment.yaml index 3a79c08..e4cc7fa 100644 --- a/k8s/base/app-deployment.yaml +++ b/k8s/base/app-deployment.yaml @@ -55,6 +55,12 @@ spec: value: "3000" - name: NODE_ENV value: "production" + - name: TELEMETRY_EXCLUDE_PHONES + valueFrom: + secretKeyRef: + name: flxn-secrets + key: telemetry_exclude_phones + optional: true - name: TWILIO_ACCOUNT_SID valueFrom: -- 2.54.0 From 31f62374461d20cc896d9fbf355149c6bba34d27 Mon Sep 17 00:00:00 2001 From: yohlo Date: Tue, 25 Aug 2026 23:22:21 -0700 Subject: [PATCH 19/33] refactor(admin): drop status strip from dashboard; full-bleed usage tab - admin home is tournament section + tools list only - usage route matches sibling tabs (fullWidth, no shell padding); screen pads its own content so the tab bar stays flush --- src/app/routes/_authed/admin/index.tsx | 13 ---- .../routes/_authed/admin/telemetry/usage.tsx | 2 + .../admin/components/dashboard/index.tsx | 23 +----- .../components/dashboard/status-strip.tsx | 74 ------------------- .../telemetry/components/usage-screen.tsx | 2 +- src/locales/de/messages.po | 14 +--- src/locales/en/messages.po | 14 +--- src/locales/es/messages.po | 14 +--- src/locales/ja/messages.po | 14 +--- 9 files changed, 8 insertions(+), 162 deletions(-) delete mode 100644 src/features/admin/components/dashboard/status-strip.tsx diff --git a/src/app/routes/_authed/admin/index.tsx b/src/app/routes/_authed/admin/index.tsx index ec660c1..30d57db 100644 --- a/src/app/routes/_authed/admin/index.tsx +++ b/src/app/routes/_authed/admin/index.tsx @@ -2,24 +2,11 @@ import { createFileRoute } from "@tanstack/react-router"; import { msg } from "@lingui/core/macro"; import { AdminDashboard } from "@/features/admin"; import { tournamentQueries } from "@/features/tournaments/queries"; -import { telemetryQueries } from "@/features/telemetry/queries"; -import { isoDay, lastNDays } from "@/features/telemetry/range"; import { prefetchServerQuery } from "@/lib/tanstack-query/utils/prefetch"; export const Route = createFileRoute("/_authed/admin/")({ beforeLoad: ({ context }) => { prefetchServerQuery(context.queryClient, tournamentQueries.current()); - prefetchServerQuery(context.queryClient, telemetryQueries.runtimeStatus()); - const month = lastNDays(30); - prefetchServerQuery( - context.queryClient, - telemetryQueries.errorGroups({ from: month.from, to: month.to }) - ); - const today = isoDay(new Date()); - prefetchServerQuery( - context.queryClient, - telemetryQueries.rollups({ metrics: ["server_fn.count"], from: today, to: today }) - ); }, loader: () => ({ header: { diff --git a/src/app/routes/_authed/admin/telemetry/usage.tsx b/src/app/routes/_authed/admin/telemetry/usage.tsx index 124caf9..e0c0742 100644 --- a/src/app/routes/_authed/admin/telemetry/usage.tsx +++ b/src/app/routes/_authed/admin/telemetry/usage.tsx @@ -10,6 +10,8 @@ export const Route = createFileRoute("/_authed/admin/telemetry/usage")({ prefetchServerQuery(context.queryClient, telemetryQueries.rollups(defaultUsageParams())); }, loader: () => ({ + withPadding: false, + fullWidth: true, refresh: [telemetryQueries.rollups(defaultUsageParams()).queryKey], }), }); diff --git a/src/features/admin/components/dashboard/index.tsx b/src/features/admin/components/dashboard/index.tsx index 4c3f3e0..f12fb96 100644 --- a/src/features/admin/components/dashboard/index.tsx +++ b/src/features/admin/components/dashboard/index.tsx @@ -1,24 +1,10 @@ import { Suspense } from "react"; -import { Group, SimpleGrid, Skeleton, Stack } from "@mantine/core"; +import { SimpleGrid, Skeleton } from "@mantine/core"; import QuietBoundary from "./quiet-boundary"; import SectionBox from "./section-box"; -import StatusStrip from "./status-strip"; import TournamentSection from "./tournament-section"; import ToolsList from "./tools-list"; -const StripSkeleton = () => ( - - - {[0, 1, 2].map((i) => ( - - - - - ))} - - -); - const TournamentSkeleton = () => ( @@ -32,13 +18,6 @@ const TournamentSkeleton = () => ( export const AdminDashboard = () => ( <> - - }> - - - - - }> diff --git a/src/features/admin/components/dashboard/status-strip.tsx b/src/features/admin/components/dashboard/status-strip.tsx deleted file mode 100644 index 1c7e13c..0000000 --- a/src/features/admin/components/dashboard/status-strip.tsx +++ /dev/null @@ -1,74 +0,0 @@ -import { useMemo } from "react"; -import { Group, Stack, Text, UnstyledButton } from "@mantine/core"; -import { useLingui } from "@lingui/react/macro"; -import { useNavigate } from "@tanstack/react-router"; -import { - useClientErrorGroups, - useRollupRange, - useTelemetryRuntimeStatus, -} from "@/features/telemetry/queries"; -import { isoDay, lastNDays } from "@/features/telemetry/range"; - -interface StatProps { - value: string | number; - label: string; - alert?: boolean; - onClick: () => void; -} - -const Stat = ({ value, label, alert, onClick }: StatProps) => ( - - - - {value} - - - {label} - - - -); - -export const StatusStrip = () => { - const { t } = useLingui(); - const navigate = useNavigate(); - const today = useMemo(() => isoDay(new Date()), []); - const month = useMemo(() => lastNDays(30), []); - - const { data: status } = useTelemetryRuntimeStatus(); - const { data: errorGroups } = useClientErrorGroups({ from: month.from, to: month.to }); - const { data: rollups } = useRollupRange({ - metrics: ["server_fn.count"], - from: today, - to: today, - }); - - const callsToday = useMemo( - () => (rollups ?? []).reduce((sum, row) => sum + row.value, 0), - [rollups] - ); - const openErrors = errorGroups?.length ?? 0; - - return ( - - navigate({ to: "/admin/telemetry/players" })} - /> - 0} - onClick={() => navigate({ to: "/admin/telemetry/errors" })} - /> - navigate({ to: "/admin/telemetry/usage" })} - /> - - ); -}; - -export default StatusStrip; diff --git a/src/features/telemetry/components/usage-screen.tsx b/src/features/telemetry/components/usage-screen.tsx index 825e54b..df7f9c9 100644 --- a/src/features/telemetry/components/usage-screen.tsx +++ b/src/features/telemetry/components/usage-screen.tsx @@ -255,7 +255,7 @@ export const UsageScreen = () => { const range = useMemo(() => lastNDays(Number(rangeDays)), [rangeDays]); return ( - + setRangeDays(value as "7" | "30")} diff --git a/src/locales/de/messages.po b/src/locales/de/messages.po index f6fa61a..c145154 100644 --- a/src/locales/de/messages.po +++ b/src/locales/de/messages.po @@ -446,7 +446,7 @@ msgid "Add Team" msgstr "Team hinzufügen" #: src/app/routes/_authed/admin.tsx:15 -#: src/app/routes/_authed/admin/index.tsx:27 +#: src/app/routes/_authed/admin/index.tsx:14 #: src/features/core/hooks/use-links.ts:37 msgid "Admin" msgstr "Admin" @@ -660,10 +660,6 @@ msgstr "Brutal" msgid "CA" msgstr "CA" -#: src/features/admin/components/dashboard/status-strip.tsx:67 -msgid "Calls today" -msgstr "Aufrufe heute" - #: src/components/sheet/slide-panel/slide-panel.tsx:41 #: src/components/sheet/slide-panel/slide-panel.tsx:190 #: src/features/bracket/components/match-form.tsx:160 @@ -1879,14 +1875,6 @@ msgstr "Sobald du angemeldet bist, kannst du andere Free Agents und ihre Telefon msgid "One time code" msgstr "Einmalcode" -#: src/features/admin/components/dashboard/status-strip.tsx:56 -msgid "Online" -msgstr "Online" - -#: src/features/admin/components/dashboard/status-strip.tsx:61 -msgid "Open errors" -msgstr "Offene Fehler" - #: src/features/admin/components/dashboard/tools-list.tsx:95 msgid "Open Pocketbase" msgstr "Pocketbase öffnen" diff --git a/src/locales/en/messages.po b/src/locales/en/messages.po index 817065e..8a0f32b 100644 --- a/src/locales/en/messages.po +++ b/src/locales/en/messages.po @@ -446,7 +446,7 @@ msgid "Add Team" msgstr "Add Team" #: src/app/routes/_authed/admin.tsx:15 -#: src/app/routes/_authed/admin/index.tsx:27 +#: src/app/routes/_authed/admin/index.tsx:14 #: src/features/core/hooks/use-links.ts:37 msgid "Admin" msgstr "Admin" @@ -660,10 +660,6 @@ msgstr "Brutal" msgid "CA" msgstr "CA" -#: src/features/admin/components/dashboard/status-strip.tsx:67 -msgid "Calls today" -msgstr "Calls today" - #: src/components/sheet/slide-panel/slide-panel.tsx:41 #: src/components/sheet/slide-panel/slide-panel.tsx:190 #: src/features/bracket/components/match-form.tsx:160 @@ -1879,14 +1875,6 @@ msgstr "Once enrolled, you can view other free agents and their phone number in msgid "One time code" msgstr "One time code" -#: src/features/admin/components/dashboard/status-strip.tsx:56 -msgid "Online" -msgstr "Online" - -#: src/features/admin/components/dashboard/status-strip.tsx:61 -msgid "Open errors" -msgstr "Open errors" - #: src/features/admin/components/dashboard/tools-list.tsx:95 msgid "Open Pocketbase" msgstr "Open Pocketbase" diff --git a/src/locales/es/messages.po b/src/locales/es/messages.po index 2039edb..eaf7e8d 100644 --- a/src/locales/es/messages.po +++ b/src/locales/es/messages.po @@ -446,7 +446,7 @@ msgid "Add Team" msgstr "Agregar Equipo" #: src/app/routes/_authed/admin.tsx:15 -#: src/app/routes/_authed/admin/index.tsx:27 +#: src/app/routes/_authed/admin/index.tsx:14 #: src/features/core/hooks/use-links.ts:37 msgid "Admin" msgstr "Admin" @@ -660,10 +660,6 @@ msgstr "Brutal" msgid "CA" msgstr "VC" -#: src/features/admin/components/dashboard/status-strip.tsx:67 -msgid "Calls today" -msgstr "Llamadas hoy" - #: src/components/sheet/slide-panel/slide-panel.tsx:41 #: src/components/sheet/slide-panel/slide-panel.tsx:190 #: src/features/bracket/components/match-form.tsx:160 @@ -1879,14 +1875,6 @@ msgstr "Una vez inscrito, puedes ver a otros agentes libres y su número de tel msgid "One time code" msgstr "Código de un solo uso" -#: src/features/admin/components/dashboard/status-strip.tsx:56 -msgid "Online" -msgstr "En línea" - -#: src/features/admin/components/dashboard/status-strip.tsx:61 -msgid "Open errors" -msgstr "Errores abiertos" - #: src/features/admin/components/dashboard/tools-list.tsx:95 msgid "Open Pocketbase" msgstr "Abrir Pocketbase" diff --git a/src/locales/ja/messages.po b/src/locales/ja/messages.po index fb0e762..e8aaef5 100644 --- a/src/locales/ja/messages.po +++ b/src/locales/ja/messages.po @@ -446,7 +446,7 @@ msgid "Add Team" msgstr "チームを追加" #: src/app/routes/_authed/admin.tsx:15 -#: src/app/routes/_authed/admin/index.tsx:27 +#: src/app/routes/_authed/admin/index.tsx:14 #: src/features/core/hooks/use-links.ts:37 msgid "Admin" msgstr "管理者" @@ -660,10 +660,6 @@ msgstr "ブルータル" msgid "CA" msgstr "CA" -#: src/features/admin/components/dashboard/status-strip.tsx:67 -msgid "Calls today" -msgstr "本日の呼び出し数" - #: src/components/sheet/slide-panel/slide-panel.tsx:41 #: src/components/sheet/slide-panel/slide-panel.tsx:190 #: src/features/bracket/components/match-form.tsx:160 @@ -1879,14 +1875,6 @@ msgstr "登録が完了すると、他のフリーエージェントとその電 msgid "One time code" msgstr "ワンタイムコード" -#: src/features/admin/components/dashboard/status-strip.tsx:56 -msgid "Online" -msgstr "オンライン" - -#: src/features/admin/components/dashboard/status-strip.tsx:61 -msgid "Open errors" -msgstr "未解決のエラー" - #: src/features/admin/components/dashboard/tools-list.tsx:95 msgid "Open Pocketbase" msgstr "Pocketbaseを開く" -- 2.54.0 From 1ddcffc51156d9bea3c55bb6963c6cd41629fb96 Mon Sep 17 00:00:00 2001 From: yohlo Date: Tue, 25 Aug 2026 23:26:34 -0700 Subject: [PATCH 20/33] fix(telemetry): review findings - wau/mau trailing scans run hourly and on day finalization, not every tick - exclusion lookup failures cached 30s so an st outage cannot amplify - unverified relation filter dropped; player emptiness checked in js - anonymous unauthenticated read failures no longer logged - alerts and exclusions ensure supertokens init on cold pods --- src/lib/telemetry/alerts.server.ts | 2 ++ src/lib/telemetry/exclusions.server.ts | 6 +++++ src/lib/telemetry/rollup-core.test.ts | 12 +++++++++ src/lib/telemetry/rollup-core.ts | 8 +++--- src/lib/telemetry/rollup.server.ts | 31 +++++++++++++++------- src/lib/telemetry/scheduler.server.ts | 7 ++++- src/utils/activities.test.ts | 36 ++++++++++++++++++++++++++ src/utils/activities.ts | 6 +++++ 8 files changed, 93 insertions(+), 15 deletions(-) diff --git a/src/lib/telemetry/alerts.server.ts b/src/lib/telemetry/alerts.server.ts index eb0863b..95b8138 100644 --- a/src/lib/telemetry/alerts.server.ts +++ b/src/lib/telemetry/alerts.server.ts @@ -31,6 +31,8 @@ const getAdminPlayerIds = async (): Promise => { return adminPlayersCache.playerIds; } + const { ensureSuperTokensBackend } = await import("@/lib/supertokens/server"); + ensureSuperTokensBackend(); const UserRoles = (await import("supertokens-node/recipe/userroles")).default; const response = await UserRoles.getUsersThatHaveRole("public", ADMIN_ROLE); const users = response.status === "OK" ? response.users : []; diff --git a/src/lib/telemetry/exclusions.server.ts b/src/lib/telemetry/exclusions.server.ts index c25dc7c..e95ad68 100644 --- a/src/lib/telemetry/exclusions.server.ts +++ b/src/lib/telemetry/exclusions.server.ts @@ -3,6 +3,7 @@ import { Logger } from "@/lib/logger"; const logger = new Logger("Telemetry"); const CACHE_TTL_MS = 10 * 60 * 1000; +const FAILURE_TTL_MS = 30 * 1000; const normalizePhone = (phone: string): string => phone.replace(/[^\d+]/g, ""); @@ -26,6 +27,8 @@ export const getExcludedPlayerIds = async (): Promise> => { if (phones.length > 0) { try { + const { ensureSuperTokensBackend } = await import("@/lib/supertokens/server"); + ensureSuperTokensBackend(); const SuperTokens = (await import("supertokens-node")).default; const { pbAdmin } = await import("@/lib/pocketbase/client"); for (const phoneNumber of phones) { @@ -37,6 +40,9 @@ export const getExcludedPlayerIds = async (): Promise> => { } } catch (error) { logger.error("Failed to resolve telemetry exclusions", error); + // Short-lived failure cache: an outage must not amplify into a + // per-request SuperTokens lookup storm. + cache = { playerIds, expiresAt: now + FAILURE_TTL_MS }; return playerIds; } } diff --git a/src/lib/telemetry/rollup-core.test.ts b/src/lib/telemetry/rollup-core.test.ts index 1267578..01d727c 100644 --- a/src/lib/telemetry/rollup-core.test.ts +++ b/src/lib/telemetry/rollup-core.test.ts @@ -95,6 +95,18 @@ describe("buildDayRollups", () => { } }); + it("omits wau/mau rows when trailing scans were skipped", () => { + const rollups = buildDayRollups("2026-08-25", { + activities: newActivityAccumulator(), + clientEvents: newClientEventAccumulator(), + clientErrors: newClientErrorAccumulator(), + partial: true, + }); + expect(rollupFor(rollups, "wau")).toBeUndefined(); + expect(rollupFor(rollups, "mau")).toBeUndefined(); + expect(rollupFor(rollups, "dau")?.value).toBe(0); + }); + it("marks partial days and is deterministic across re-runs", () => { const build = () => buildDayRollups("2026-08-25", { diff --git a/src/lib/telemetry/rollup-core.ts b/src/lib/telemetry/rollup-core.ts index 22a91aa..ed8ec3a 100644 --- a/src/lib/telemetry/rollup-core.ts +++ b/src/lib/telemetry/rollup-core.ts @@ -113,8 +113,8 @@ export const buildDayRollups = ( activities: ActivityAccumulator; clientEvents: ClientEventAccumulator; clientErrors: ClientErrorAccumulator; - wau: number; - mau: number; + wau?: number; + mau?: number; partial: boolean; } ): RollupInput[] => { @@ -133,8 +133,8 @@ export const buildDayRollups = ( const dayPlayers = new Set([...activities.players, ...clientEvents.players]); push("dau", "", dayPlayers.size); - push("wau", "", wau); - push("mau", "", mau); + if (wau !== undefined) push("wau", "", wau); + if (mau !== undefined) push("mau", "", mau); for (const [name, fn] of activities.perFn) { push("server_fn.count", name, fn.count); diff --git a/src/lib/telemetry/rollup.server.ts b/src/lib/telemetry/rollup.server.ts index 579e1f1..756c67d 100644 --- a/src/lib/telemetry/rollup.server.ts +++ b/src/lib/telemetry/rollup.server.ts @@ -30,7 +30,7 @@ const dayFilter = (dateStr: string): string => const distinctPlayersInWindow = async (fromDate: string, toDateExclusive: string): Promise => { const players = new Set(); - const filter = `created >= '${bound(fromDate)}' && created < '${bound(toDateExclusive)}' && player != ''`; + const filter = `created >= '${bound(fromDate)}' && created < '${bound(toDateExclusive)}'`; await pbAdmin.pageCollection<{ player?: string }>("activities", { filter, fields: "player,created" }, (rows) => { for (const row of rows) if (row.player) players.add(row.player); @@ -42,7 +42,10 @@ const distinctPlayersInWindow = async (fromDate: string, toDateExclusive: string return players.size; }; -export const runRollups = async (dateStr: string): Promise => { +export const runRollups = async ( + dateStr: string, + opts: { trailing?: boolean } = {} +): Promise => { await pbAdmin.authPromise; const activities = newActivityAccumulator(); @@ -65,11 +68,16 @@ export const runRollups = async (dateStr: string): Promise => { (rows) => accumulateClientErrors(clientErrors, rows) ); - const nextDay = addDays(dateStr, 1); - const [wau, mau] = await Promise.all([ - distinctPlayersInWindow(addDays(dateStr, -6), nextDay), - distinctPlayersInWindow(addDays(dateStr, -29), nextDay), - ]); + // Trailing scans re-read the full 7/30-day windows; hourly at most. + let wau: number | undefined; + let mau: number | undefined; + if (opts.trailing !== false) { + const nextDay = addDays(dateStr, 1); + [wau, mau] = await Promise.all([ + distinctPlayersInWindow(addDays(dateStr, -6), nextDay), + distinctPlayersInWindow(addDays(dateStr, -29), nextDay), + ]); + } const partial = dateStr >= utcDay(new Date()); const rollups = buildDayRollups(dateStr, { @@ -89,11 +97,14 @@ export const runRollups = async (dateStr: string): Promise => { return rollups.length; }; -export const runScheduledRollups = async (lastRunDay?: string): Promise => { +export const runScheduledRollups = async ( + lastRunDay: string | undefined, + opts: { trailing?: boolean } = {} +): Promise => { const today = utcDay(new Date()); if (lastRunDay && lastRunDay !== today) { - await runRollups(lastRunDay); + await runRollups(lastRunDay, { trailing: true }); } - await runRollups(today); + await runRollups(today, opts); return today; }; diff --git a/src/lib/telemetry/scheduler.server.ts b/src/lib/telemetry/scheduler.server.ts index b19cd57..e950f3d 100644 --- a/src/lib/telemetry/scheduler.server.ts +++ b/src/lib/telemetry/scheduler.server.ts @@ -6,6 +6,7 @@ const logger = new Logger("Telemetry"); const ROLLUP_INTERVAL_MS = 15 * 60 * 1000; const ALERT_INTERVAL_MS = 5 * 60 * 1000; +const TRAILING_INTERVAL_MS = 60 * 60 * 1000; interface SchedulerStatus { running: boolean; @@ -21,12 +22,16 @@ let initialized = false; let rollupInFlight = false; let alertsInFlight = false; let lastRollupDay: string | undefined; +let lastTrailingAt = 0; const tickRollups = async () => { if (rollupInFlight) return; rollupInFlight = true; try { - lastRollupDay = await runScheduledRollups(lastRollupDay); + const now = Date.now(); + const trailing = now - lastTrailingAt >= TRAILING_INTERVAL_MS; + lastRollupDay = await runScheduledRollups(lastRollupDay, { trailing }); + if (trailing) lastTrailingAt = now; status.lastRollupAt = new Date().toISOString(); status.lastRollupError = undefined; } catch (error) { diff --git a/src/utils/activities.test.ts b/src/utils/activities.test.ts index ae18c22..08c6b9a 100644 --- a/src/utils/activities.test.ts +++ b/src/utils/activities.test.ts @@ -308,6 +308,42 @@ describe("serverFnLoggingMiddleware control flow and dedup", () => { expect(h.createActivity.mock.calls[0][0].error).toContain("FORBIDDEN"); }); + it("does not record anonymous Unauthenticated failures", async () => { + setRequest("http://localhost:3000/_serverFn/x"); + + await expect( + runMiddleware({ + next: async () => { + throw new Error("Unauthenticated"); + }, + data: undefined, + context: {}, + serverFnMeta: { id: "x", name: "fetchMe" }, + }) + ).rejects.toThrow("Unauthenticated"); + + await flushWrites(); + expect(h.createActivity).not.toHaveBeenCalled(); + }); + + it("still records Unauthenticated failures for identified actors", async () => { + setRequest("http://localhost:3000/_serverFn/x"); + setRequestActor(h.request as unknown as Request, { playerId: "p1" }); + + await expect( + runMiddleware({ + next: async () => { + throw new Error("Unauthenticated"); + }, + data: undefined, + context: {}, + serverFnMeta: { id: "x", name: "fetchMe" }, + }) + ).rejects.toThrow("Unauthenticated"); + + await vi.waitFor(() => expect(h.createActivity).toHaveBeenCalledTimes(1)); + }); + it("does not record activity for excluded players", async () => { setRequest("http://localhost:3000/_serverFn/doThing"); setRequestActor(h.request as unknown as Request, { playerId: "excluded-player" }); diff --git a/src/utils/activities.ts b/src/utils/activities.ts index 5490a8d..f1a6d76 100644 --- a/src/utils/activities.ts +++ b/src/utils/activities.ts @@ -129,6 +129,12 @@ export const serverFnLoggingMiddleware = createMiddleware({ const duration = Date.now() - startTime; const errorMessage = error instanceof Error ? error.message : String(error); + // Anonymous visitors (crawlers included) hitting authed reads are noise, + // not failures worth a row each. + if (errorMessage === "Unauthenticated" && !resolveActor()) { + throw error; + } + recordActivity({ name, player: resolveActor(), -- 2.54.0 From 3d8ee52a154633fad76c596d73873e40558134f9 Mon Sep 17 00:00:00 2001 From: yohlo Date: Tue, 25 Aug 2026 23:33:01 -0700 Subject: [PATCH 21/33] feat(telemetry): opt-in admin alert pushes - telemetryAlerts metadata flag, admin-gated server fn - alerts sent only to admins with the flag enabled - settings toggle visible to admins only, under device notifications --- src/contexts/auth-context.tsx | 1 + .../components/notifications-section.tsx | 36 +++++++++++++ src/features/settings/server.ts | 28 +++++++++- src/lib/telemetry/alerts.server.ts | 4 ++ src/locales/de/messages.po | 54 +++++++++++-------- src/locales/en/messages.po | 54 +++++++++++-------- src/locales/es/messages.po | 54 +++++++++++-------- src/locales/ja/messages.po | 54 +++++++++++-------- 8 files changed, 200 insertions(+), 85 deletions(-) diff --git a/src/contexts/auth-context.tsx b/src/contexts/auth-context.tsx index a7e0e07..a5073d7 100644 --- a/src/contexts/auth-context.tsx +++ b/src/contexts/auth-context.tsx @@ -18,6 +18,7 @@ interface AuthData { theme?: string; locale?: string; seenThemesHint?: boolean; + telemetryAlerts?: boolean; }; roles: string[]; phone: string; diff --git a/src/features/settings/components/notifications-section.tsx b/src/features/settings/components/notifications-section.tsx index 2cd40f3..7153bf2 100644 --- a/src/features/settings/components/notifications-section.tsx +++ b/src/features/settings/components/notifications-section.tsx @@ -1,11 +1,46 @@ +import { useState } from "react"; import { Box, Title, Stack, Switch, Button, Text, Group } from "@mantine/core"; import { Trans, useLingui } from "@lingui/react/macro"; import { useWebPush } from "@/hooks/use-web-push"; +import { useAuth } from "@/contexts/auth-context"; +import { useIsAdmin } from "@/features/core/hooks/use-is-admin"; +import { updateUserTelemetryAlerts } from "@/features/settings/server"; import toast from "@/lib/sonner"; +const TelemetryAlertsSwitch = () => { + const { t } = useLingui(); + const { metadata, set } = useAuth(); + const [saving, setSaving] = useState(false); + + const handleToggle = async (checked: boolean) => { + setSaving(true); + try { + const result = await updateUserTelemetryAlerts({ data: checked }); + if (result?.metadata) { + set({ metadata: result.metadata }); + } + } catch { + toast.error(t`Couldn't update telemetry alerts`); + } finally { + setSaving(false); + } + }; + + return ( + handleToggle(e.currentTarget.checked)} + disabled={saving} + label={t`Telemetry alerts`} + description={t`Error spikes and failing functions, pushed to this account`} + /> + ); +}; + export function NotificationsSection() { const { supported, configured, permission, subscribed, busy, enable, disable, sendTest } = useWebPush(); + const isAdmin = useIsAdmin(); const { t } = useLingui(); if (!supported || !configured) { @@ -64,6 +99,7 @@ export function NotificationsSection() { disabled={busy} label={t`Enable notifications on this device`} /> + {subscribed && isAdmin && } {subscribed && (