Compare commits
35 Commits
cs2-port
...
webpack-to
| Author | SHA1 | Date | |
|---|---|---|---|
| afed42de49 | |||
| 9ac3228f5d | |||
| 106ef97ede | |||
| 552188c8a9 | |||
| ce70fa2e6f | |||
| 5591e75c86 | |||
| 1236c2ca2d | |||
| 9cbbfe9393 | |||
| f6dd2ea1c4 | |||
| 70fb352d7f | |||
| 67cc06abdf | |||
| 3ad55c7fc4 | |||
| 0a355ff2bd | |||
| 16addf0bca | |||
| 5cb339483a | |||
| 7daa47bb64 | |||
| 8ed371d5fb | |||
| a45215dce1 | |||
| a03dad2a0e | |||
| 18cd1ecdc9 | |||
| 7a866c9d50 | |||
| fe7b851157 | |||
| 53225dffd4 | |||
| 0c9d6e7975 | |||
| 640eddc365 | |||
| 190064497e | |||
| d0d17ccd3d | |||
| d479573f41 | |||
| 012b56f184 | |||
| 0e727716a3 | |||
| 7523286236 | |||
| 2c3685f594 | |||
| cbe770ecd7 | |||
| 328f463cdb | |||
| 9a6d24193d |
80
.env.example
@@ -1,80 +0,0 @@
|
|||||||
# CS2.WTF Environment Configuration
|
|
||||||
# Copy this file to .env for local development
|
|
||||||
# DO NOT commit .env to version control
|
|
||||||
|
|
||||||
# ============================================
|
|
||||||
# API Configuration
|
|
||||||
# ============================================
|
|
||||||
|
|
||||||
# Backend API Base URL
|
|
||||||
# Development: Vite proxy forwards /api to this URL (default: http://localhost:8000)
|
|
||||||
# Production: Set to your actual backend URL (e.g., https://api.csgow.tf)
|
|
||||||
# Note: In development, the frontend uses /api and Vite proxies to this URL
|
|
||||||
VITE_API_BASE_URL=http://localhost:8000
|
|
||||||
|
|
||||||
# API request timeout in milliseconds
|
|
||||||
# Default: 10000 (10 seconds)
|
|
||||||
VITE_API_TIMEOUT=10000
|
|
||||||
|
|
||||||
# ============================================
|
|
||||||
# Feature Flags
|
|
||||||
# ============================================
|
|
||||||
|
|
||||||
# Enable live match updates (polling/WebSocket)
|
|
||||||
# Default: false
|
|
||||||
VITE_ENABLE_LIVE_MATCHES=false
|
|
||||||
|
|
||||||
# Enable analytics tracking
|
|
||||||
# Default: true (respects user consent)
|
|
||||||
VITE_ENABLE_ANALYTICS=true
|
|
||||||
|
|
||||||
# Enable debug mode (verbose logging, dev tools)
|
|
||||||
# Default: false
|
|
||||||
VITE_DEBUG_MODE=false
|
|
||||||
|
|
||||||
# ============================================
|
|
||||||
# Analytics & Tracking (Optional)
|
|
||||||
# ============================================
|
|
||||||
|
|
||||||
# Plausible Analytics
|
|
||||||
# Only required if analytics is enabled
|
|
||||||
# VITE_PLAUSIBLE_DOMAIN=cs2.wtf
|
|
||||||
# VITE_PLAUSIBLE_API_HOST=https://plausible.io
|
|
||||||
|
|
||||||
# Umami Analytics (alternative)
|
|
||||||
# VITE_UMAMI_WEBSITE_ID=your-website-id
|
|
||||||
# VITE_UMAMI_SRC=https://analytics.example.com/script.js
|
|
||||||
|
|
||||||
# ============================================
|
|
||||||
# Experimental Features
|
|
||||||
# ============================================
|
|
||||||
|
|
||||||
# Enable WebGL-based heatmaps (high performance)
|
|
||||||
# Default: false (use Canvas fallback)
|
|
||||||
# VITE_ENABLE_WEBGL_HEATMAPS=false
|
|
||||||
|
|
||||||
# Enable MSW API mocking in development
|
|
||||||
# Useful for frontend development without backend
|
|
||||||
# Default: false
|
|
||||||
# VITE_ENABLE_MSW_MOCKING=false
|
|
||||||
|
|
||||||
# ============================================
|
|
||||||
# Build Configuration
|
|
||||||
# ============================================
|
|
||||||
|
|
||||||
# App version (auto-populated from package.json)
|
|
||||||
# VITE_APP_VERSION=2.0.0
|
|
||||||
|
|
||||||
# Build timestamp (auto-populated during build)
|
|
||||||
# VITE_BUILD_TIMESTAMP=2024-11-04T12:00:00Z
|
|
||||||
|
|
||||||
# ============================================
|
|
||||||
# SSR/Deployment (Advanced)
|
|
||||||
# ============================================
|
|
||||||
|
|
||||||
# Public base URL for the application
|
|
||||||
# Used for canonical URLs, sitemaps, etc.
|
|
||||||
# PUBLIC_BASE_URL=https://cs2.wtf
|
|
||||||
|
|
||||||
# Origin whitelist for CORS (if handling API in same domain)
|
|
||||||
# PUBLIC_CORS_ORIGINS=https://cs2.wtf,https://www.cs2.wtf
|
|
||||||
15
.eslintrc.cjs
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
/* eslint-env node */
|
||||||
|
require("@rushstack/eslint-patch/modern-module-resolution");
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
"root": true,
|
||||||
|
"extends": [
|
||||||
|
"plugin:vue/vue3-essential",
|
||||||
|
"eslint:recommended",
|
||||||
|
"@vue/eslint-config-typescript/recommended",
|
||||||
|
"@vue/eslint-config-prettier"
|
||||||
|
],
|
||||||
|
"env": {
|
||||||
|
"vue/setup-compiler-macros": true,
|
||||||
|
}
|
||||||
|
}
|
||||||
305
.gitignore
vendored
@@ -1,52 +1,287 @@
|
|||||||
.DS_Store
|
# Created by https://www.toptal.com/developers/gitignore/api/webstorm+all,yarn,windows,linux,node,vuejs
|
||||||
node_modules
|
# Edit at https://www.toptal.com/developers/gitignore?templates=webstorm+all,yarn,windows,linux,node,vuejs
|
||||||
/build
|
|
||||||
/.svelte-kit
|
|
||||||
/package
|
|
||||||
.env
|
|
||||||
.env.*
|
|
||||||
!.env.example
|
|
||||||
vite.config.js.timestamp-*
|
|
||||||
vite.config.ts.timestamp-*
|
|
||||||
|
|
||||||
|
### Linux ###
|
||||||
|
*~
|
||||||
|
.env
|
||||||
|
|
||||||
|
# temporary files which can be created if a process still has a handle open of a deleted file
|
||||||
|
.fuse_hidden*
|
||||||
|
|
||||||
|
# KDE directory preferences
|
||||||
|
.directory
|
||||||
|
|
||||||
|
# Linux trash folder which might appear on any partition or disk
|
||||||
|
.Trash-*
|
||||||
|
|
||||||
|
# .nfs files are created when an open file is removed but is still being accessed
|
||||||
|
.nfs*
|
||||||
|
|
||||||
|
### Node ###
|
||||||
# Logs
|
# Logs
|
||||||
logs
|
logs
|
||||||
*.log
|
*.log
|
||||||
npm-debug.log*
|
npm-debug.log*
|
||||||
yarn-debug.log*
|
yarn-debug.log*
|
||||||
yarn-error.log*
|
yarn-error.log*
|
||||||
pnpm-debug.log*
|
|
||||||
lerna-debug.log*
|
lerna-debug.log*
|
||||||
|
.pnpm-debug.log*
|
||||||
|
|
||||||
# Editor directories and files
|
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||||
.vscode/*
|
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||||
!.vscode/extensions.json
|
|
||||||
.idea
|
|
||||||
*.suo
|
|
||||||
*.ntvs*
|
|
||||||
*.njsproj
|
|
||||||
*.sln
|
|
||||||
*.sw?
|
|
||||||
|
|
||||||
# Build artifacts
|
# Runtime data
|
||||||
dist
|
pids
|
||||||
dist-ssr
|
*.pid
|
||||||
*.local
|
*.seed
|
||||||
|
*.pid.lock
|
||||||
|
|
||||||
# Test coverage
|
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||||
|
lib-cov
|
||||||
|
|
||||||
|
# Coverage directory used by tools like istanbul
|
||||||
coverage
|
coverage
|
||||||
*.lcov
|
*.lcov
|
||||||
|
|
||||||
|
# nyc test coverage
|
||||||
.nyc_output
|
.nyc_output
|
||||||
|
|
||||||
# Playwright
|
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||||
/test-results/
|
.grunt
|
||||||
/playwright-report/
|
|
||||||
/playwright/.cache/
|
|
||||||
|
|
||||||
# Vercel
|
# Bower dependency directory (https://bower.io/)
|
||||||
.vercel
|
bower_components
|
||||||
|
|
||||||
# Temporary files
|
# node-waf configuration
|
||||||
.tmp
|
.lock-wscript
|
||||||
tmp
|
|
||||||
*.tmp
|
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||||
|
build/Release
|
||||||
|
|
||||||
|
# Dependency directories
|
||||||
|
node_modules/
|
||||||
|
jspm_packages/
|
||||||
|
|
||||||
|
# Snowpack dependency directory (https://snowpack.dev/)
|
||||||
|
web_modules/
|
||||||
|
|
||||||
|
# TypeScript cache
|
||||||
|
*.tsbuildinfo
|
||||||
|
|
||||||
|
# Optional npm cache directory
|
||||||
|
.npm
|
||||||
|
|
||||||
|
# Optional eslint cache
|
||||||
|
.eslintcache
|
||||||
|
|
||||||
|
# Microbundle cache
|
||||||
|
.rpt2_cache/
|
||||||
|
.rts2_cache_cjs/
|
||||||
|
.rts2_cache_es/
|
||||||
|
.rts2_cache_umd/
|
||||||
|
|
||||||
|
# Optional REPL history
|
||||||
|
.node_repl_history
|
||||||
|
|
||||||
|
# Output of 'npm pack'
|
||||||
|
*.tgz
|
||||||
|
|
||||||
|
# Yarn Integrity file
|
||||||
|
.yarn-integrity
|
||||||
|
|
||||||
|
# dotenv environment variables file
|
||||||
|
.env.local
|
||||||
|
.env.test
|
||||||
|
.env.production
|
||||||
|
|
||||||
|
# parcel-bundler cache (https://parceljs.org/)
|
||||||
|
.cache
|
||||||
|
.parcel-cache
|
||||||
|
|
||||||
|
# Next.js build output
|
||||||
|
.next
|
||||||
|
out
|
||||||
|
|
||||||
|
# Nuxt.js build / generate output
|
||||||
|
.nuxt
|
||||||
|
dist
|
||||||
|
|
||||||
|
# Gatsby files
|
||||||
|
.cache/
|
||||||
|
# Comment in the public line in if your project uses Gatsby and not Next.js
|
||||||
|
# https://nextjs.org/blog/next-9-1#public-directory-support
|
||||||
|
# public
|
||||||
|
|
||||||
|
# vuepress build output
|
||||||
|
.vuepress/dist
|
||||||
|
|
||||||
|
# Serverless directories
|
||||||
|
.serverless/
|
||||||
|
|
||||||
|
# FuseBox cache
|
||||||
|
.fusebox/
|
||||||
|
|
||||||
|
# DynamoDB Local files
|
||||||
|
.dynamodb/
|
||||||
|
|
||||||
|
# TernJS port file
|
||||||
|
.tern-port
|
||||||
|
|
||||||
|
# Stores VSCode versions used for testing VSCode extensions
|
||||||
|
.vscode-test
|
||||||
|
|
||||||
|
# yarn v2
|
||||||
|
.yarn/cache
|
||||||
|
.yarn/unplugged
|
||||||
|
.yarn/build-state.yml
|
||||||
|
.yarn/install-state.gz
|
||||||
|
.pnp.*
|
||||||
|
|
||||||
|
### Node Patch ###
|
||||||
|
# Serverless Webpack directories
|
||||||
|
.webpack/
|
||||||
|
|
||||||
|
### Vuejs ###
|
||||||
|
# Recommended template: Node.gitignore
|
||||||
|
|
||||||
|
dist/
|
||||||
|
npm-debug.log
|
||||||
|
yarn-error.log
|
||||||
|
|
||||||
|
### WebStorm+all ###
|
||||||
|
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
|
||||||
|
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||||
|
|
||||||
|
# User-specific stuff
|
||||||
|
.idea/**/workspace.xml
|
||||||
|
.idea/**/tasks.xml
|
||||||
|
.idea/**/usage.statistics.xml
|
||||||
|
.idea/**/dictionaries
|
||||||
|
.idea/**/shelf
|
||||||
|
|
||||||
|
# AWS User-specific
|
||||||
|
.idea/**/aws.xml
|
||||||
|
|
||||||
|
# Generated files
|
||||||
|
.idea/**/contentModel.xml
|
||||||
|
|
||||||
|
# Sensitive or high-churn files
|
||||||
|
.idea/**/dataSources/
|
||||||
|
.idea/**/dataSources.ids
|
||||||
|
.idea/**/dataSources.local.xml
|
||||||
|
.idea/**/sqlDataSources.xml
|
||||||
|
.idea/**/dynamic.xml
|
||||||
|
.idea/**/uiDesigner.xml
|
||||||
|
.idea/**/dbnavigator.xml
|
||||||
|
|
||||||
|
# Gradle
|
||||||
|
.idea/**/gradle.xml
|
||||||
|
.idea/**/libraries
|
||||||
|
|
||||||
|
# Gradle and Maven with auto-import
|
||||||
|
# When using Gradle or Maven with auto-import, you should exclude module files,
|
||||||
|
# since they will be recreated, and may cause churn. Uncomment if using
|
||||||
|
# auto-import.
|
||||||
|
# .idea/artifacts
|
||||||
|
# .idea/compiler.xml
|
||||||
|
# .idea/jarRepositories.xml
|
||||||
|
# .idea/modules.xml
|
||||||
|
# .idea/*.iml
|
||||||
|
# .idea/modules
|
||||||
|
# *.iml
|
||||||
|
# *.ipr
|
||||||
|
|
||||||
|
# CMake
|
||||||
|
cmake-build-*/
|
||||||
|
|
||||||
|
# Mongo Explorer plugin
|
||||||
|
.idea/**/mongoSettings.xml
|
||||||
|
|
||||||
|
# File-based project format
|
||||||
|
*.iws
|
||||||
|
|
||||||
|
# IntelliJ
|
||||||
|
out/
|
||||||
|
|
||||||
|
# mpeltonen/sbt-idea plugin
|
||||||
|
.idea_modules/
|
||||||
|
|
||||||
|
# JIRA plugin
|
||||||
|
atlassian-ide-plugin.xml
|
||||||
|
|
||||||
|
# Cursive Clojure plugin
|
||||||
|
.idea/replstate.xml
|
||||||
|
|
||||||
|
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||||
|
com_crashlytics_export_strings.xml
|
||||||
|
crashlytics.properties
|
||||||
|
crashlytics-build.properties
|
||||||
|
fabric.properties
|
||||||
|
|
||||||
|
# Editor-based Rest Client
|
||||||
|
.idea/httpRequests
|
||||||
|
|
||||||
|
# Android studio 3.1+ serialized cche file
|
||||||
|
.idea/caches/build_file_checksums.ser
|
||||||
|
|
||||||
|
### WebStorm+all Patch ###
|
||||||
|
# Ignores the whole .idea folder and all .iml files
|
||||||
|
# See https://github.com/joeblau/gitignore.io/issues/186 and https://github.com/joeblau/gitignore.io/issues/360
|
||||||
|
|
||||||
|
.idea/
|
||||||
|
|
||||||
|
# Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023
|
||||||
|
|
||||||
|
*.iml
|
||||||
|
modules.xml
|
||||||
|
.idea/misc.xml
|
||||||
|
*.ipr
|
||||||
|
|
||||||
|
# Sonarlint plugin
|
||||||
|
.idea/sonarlint
|
||||||
|
|
||||||
|
### Windows ###
|
||||||
|
# Windows thumbnail cache files
|
||||||
|
Thumbs.db
|
||||||
|
Thumbs.db:encryptable
|
||||||
|
ehthumbs.db
|
||||||
|
ehthumbs_vista.db
|
||||||
|
|
||||||
|
# Dump file
|
||||||
|
*.stackdump
|
||||||
|
|
||||||
|
# Folder config file
|
||||||
|
[Dd]esktop.ini
|
||||||
|
|
||||||
|
# Recycle Bin used on file shares
|
||||||
|
$RECYCLE.BIN/
|
||||||
|
|
||||||
|
# Windows Installer files
|
||||||
|
*.cab
|
||||||
|
*.msi
|
||||||
|
*.msix
|
||||||
|
*.msm
|
||||||
|
*.msp
|
||||||
|
|
||||||
|
# Windows shortcuts
|
||||||
|
*.lnk
|
||||||
|
|
||||||
|
### yarn ###
|
||||||
|
# https://yarnpkg.com/advanced/qa#which-files-should-be-gitignored
|
||||||
|
|
||||||
|
.yarn/*
|
||||||
|
!.yarn/releases
|
||||||
|
!.yarn/plugins
|
||||||
|
!.yarn/sdks
|
||||||
|
!.yarn/versions
|
||||||
|
|
||||||
|
# if you are NOT using Zero-installs, then:
|
||||||
|
# comment the following lines
|
||||||
|
#!.yarn/cache
|
||||||
|
|
||||||
|
# and uncomment the following lines
|
||||||
|
.pnp.*
|
||||||
|
|
||||||
|
# End of https://www.toptal.com/developers/gitignore/api/webstorm+all,yarn,windows,linux,node,vuejs
|
||||||
|
|
||||||
|
a
|
||||||
|
|||||||
@@ -1,4 +0,0 @@
|
|||||||
#!/usr/bin/env sh
|
|
||||||
. "$(dirname -- "$0")/_/husky.sh"
|
|
||||||
|
|
||||||
npx lint-staged
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
.DS_Store
|
|
||||||
node_modules
|
|
||||||
/build
|
|
||||||
/.svelte-kit
|
|
||||||
/package
|
|
||||||
.env
|
|
||||||
.env.*
|
|
||||||
!.env.example
|
|
||||||
|
|
||||||
# Ignore files for PNPM, NPM and YARN
|
|
||||||
pnpm-lock.yaml
|
|
||||||
package-lock.json
|
|
||||||
yarn.lock
|
|
||||||
|
|
||||||
# Build artifacts
|
|
||||||
dist/
|
|
||||||
.vercel/
|
|
||||||
.netlify/
|
|
||||||
.output/
|
|
||||||
|
|
||||||
# Generated files
|
|
||||||
src-tauri/target/
|
|
||||||
**/.svelte-kit/
|
|
||||||
|
|
||||||
# IDE
|
|
||||||
.vscode/
|
|
||||||
.idea/
|
|
||||||
|
|
||||||
# Logs
|
|
||||||
*.log
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
{
|
|
||||||
"useTabs": true,
|
|
||||||
"tabWidth": 2,
|
|
||||||
"singleQuote": true,
|
|
||||||
"trailingComma": "none",
|
|
||||||
"printWidth": 100,
|
|
||||||
"semi": true,
|
|
||||||
"plugins": ["prettier-plugin-svelte", "prettier-plugin-tailwindcss"],
|
|
||||||
"overrides": [
|
|
||||||
{
|
|
||||||
"files": "*.svelte",
|
|
||||||
"options": {
|
|
||||||
"parser": "svelte"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
node_modules/
|
|
||||||
build/
|
|
||||||
.svelte-kit/
|
|
||||||
dist/
|
|
||||||
**/*.js
|
|
||||||
**/*.ts
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
extends: ['stylelint-config-standard'],
|
|
||||||
rules: {
|
|
||||||
'at-rule-no-unknown': [
|
|
||||||
true,
|
|
||||||
{
|
|
||||||
ignoreAtRules: ['tailwind', 'apply', 'variants', 'responsive', 'screen', 'layer']
|
|
||||||
}
|
|
||||||
],
|
|
||||||
'selector-class-pattern': null,
|
|
||||||
'custom-property-pattern': null
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
nodejs 20.11.0
|
|
||||||
100
.woodpecker.yml
@@ -1,100 +0,0 @@
|
|||||||
pipeline:
|
|
||||||
install:
|
|
||||||
image: node:20
|
|
||||||
commands:
|
|
||||||
- npm ci
|
|
||||||
pull: true
|
|
||||||
|
|
||||||
lint:
|
|
||||||
image: node:20
|
|
||||||
commands:
|
|
||||||
- npm run lint
|
|
||||||
depends_on:
|
|
||||||
- install
|
|
||||||
pull: true
|
|
||||||
|
|
||||||
type-check:
|
|
||||||
image: node:20
|
|
||||||
commands:
|
|
||||||
- npm run check
|
|
||||||
depends_on:
|
|
||||||
- install
|
|
||||||
pull: true
|
|
||||||
|
|
||||||
test:
|
|
||||||
image: node:20
|
|
||||||
commands:
|
|
||||||
- npm run test
|
|
||||||
depends_on:
|
|
||||||
- install
|
|
||||||
pull: true
|
|
||||||
|
|
||||||
build:
|
|
||||||
image: node:20
|
|
||||||
commands:
|
|
||||||
- npm run build
|
|
||||||
environment:
|
|
||||||
- VITE_API_BASE_URL=https://api.csgow.tf
|
|
||||||
secrets:
|
|
||||||
- vite_plausible_domain
|
|
||||||
- vite_sentry_dsn
|
|
||||||
depends_on:
|
|
||||||
- lint
|
|
||||||
- type-check
|
|
||||||
- test
|
|
||||||
pull: true
|
|
||||||
|
|
||||||
# E2E tests (optional - can be resource intensive)
|
|
||||||
# test-e2e:
|
|
||||||
# image: mcr.microsoft.com/playwright:v1.40.0-jammy
|
|
||||||
# commands:
|
|
||||||
# - npm run test:e2e
|
|
||||||
# depends_on:
|
|
||||||
# - build
|
|
||||||
|
|
||||||
deploy:
|
|
||||||
image: cschlosser/drone-ftps
|
|
||||||
settings:
|
|
||||||
hostname:
|
|
||||||
from_secret: ftp_host
|
|
||||||
src_dir: '/build/'
|
|
||||||
clean_dir: true
|
|
||||||
secrets: [ftp_username, ftp_password]
|
|
||||||
when:
|
|
||||||
branch: master
|
|
||||||
event: [push, tag]
|
|
||||||
status: success
|
|
||||||
|
|
||||||
deploy-dev:
|
|
||||||
image: cschlosser/drone-ftps
|
|
||||||
settings:
|
|
||||||
hostname:
|
|
||||||
from_secret: ftp_host
|
|
||||||
src_dir: '/build/'
|
|
||||||
clean_dir: true
|
|
||||||
secrets:
|
|
||||||
- source: ftp_username_dev
|
|
||||||
target: ftp_username
|
|
||||||
- source: ftp_password_dev
|
|
||||||
target: ftp_password
|
|
||||||
when:
|
|
||||||
branch: dev
|
|
||||||
event: [push, tag]
|
|
||||||
status: success
|
|
||||||
|
|
||||||
deploy-cs2:
|
|
||||||
image: cschlosser/drone-ftps
|
|
||||||
settings:
|
|
||||||
hostname:
|
|
||||||
from_secret: ftp_host_cs2
|
|
||||||
src_dir: '/build/'
|
|
||||||
clean_dir: true
|
|
||||||
secrets:
|
|
||||||
- source: ftp_username_cs2
|
|
||||||
target: ftp_username
|
|
||||||
- source: ftp_password_cs2
|
|
||||||
target: ftp_password
|
|
||||||
when:
|
|
||||||
branch: cs2-port
|
|
||||||
event: [push]
|
|
||||||
status: success
|
|
||||||
546
.yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs
vendored
Normal file
785
.yarn/releases/yarn-3.2.0.cjs
vendored
Normal file
7
.yarnrc.yml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
nodeLinker: node-modules
|
||||||
|
|
||||||
|
plugins:
|
||||||
|
- path: .yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs
|
||||||
|
spec: "@yarnpkg/plugin-interactive-tools"
|
||||||
|
|
||||||
|
yarnPath: .yarn/releases/yarn-3.2.0.cjs
|
||||||
60
Jenkinsfile
vendored
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
pipeline {
|
||||||
|
agent any
|
||||||
|
|
||||||
|
environment {
|
||||||
|
FTP_HOST = credentials('csgowtf-deploy-host')
|
||||||
|
LFTP_PASSWORD = credentials('csgowtf-deploy-password')
|
||||||
|
API_HOST = credentials('csgowtf-api-host')
|
||||||
|
TRACK_HOST = credentials('csgowtf-track-host')
|
||||||
|
TRACK_ID = credentials('csgowtf-track-id')
|
||||||
|
TRACK_DOMAINS = credentials('csgowtf-track-domains')
|
||||||
|
TRACK = credentials('csgowtf-track')
|
||||||
|
}
|
||||||
|
|
||||||
|
stages {
|
||||||
|
stage('Prepare') {
|
||||||
|
steps {
|
||||||
|
writeFile file: '.env.production', text: 'VUE_APP_API_URL=$API_HOST\nVUE_APP_TRACK_URL=$TRACK_HOST\nVUE_APP_TRACK_ID=$TRACK_ID\nVUE_APP_TRACK_DOMAINS=$TRACK_DOMAINS\nVUE_APP_TRACKING=$TRACK'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stage('Install Dependencies') {
|
||||||
|
steps {
|
||||||
|
sh 'yarn install'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stage('Build') {
|
||||||
|
steps {
|
||||||
|
sh 'yarn build'
|
||||||
|
archiveArtifacts artifacts: '**/dist/**', excludes: '**/node_modules/**'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stage('Deploy') {
|
||||||
|
when {
|
||||||
|
branch 'master'
|
||||||
|
expression {
|
||||||
|
currentBuild.result == null || currentBuild.result == 'SUCCESS'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
environment {
|
||||||
|
FTP_USERNAME = credentials('csgowtf-deploy-user')
|
||||||
|
}
|
||||||
|
steps {
|
||||||
|
sh 'lftp -u $FTP_USERNAME --env-password -e \'mirror --reverse --verbose --delete --recursion=always dist/ /\' $FTP_HOST'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stage('Deploy Dev') {
|
||||||
|
when {
|
||||||
|
branch 'dev'
|
||||||
|
expression {
|
||||||
|
currentBuild.result == null || currentBuild.result == 'SUCCESS'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
environment {
|
||||||
|
FTP_USERNAME = credentials('csgowtf-deploy-user-dev')
|
||||||
|
}
|
||||||
|
steps {
|
||||||
|
sh 'lftp -u $FTP_USERNAME --env-password -e \'mirror --reverse --verbose --delete --recursion=always dist/ /\' $FTP_HOST'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
327
README.md
@@ -1,314 +1,27 @@
|
|||||||
# CS2.WTF
|
# CSGOW.TF
|
||||||
|
|
||||||
[](https://kit.svelte.dev/)
|
[](https://vuejs.org/)
|
||||||
[](https://www.typescriptlang.org/)
|
[](https://go.dev/)
|
||||||
[](https://tailwindcss.com/)
|
|
||||||
[](https://git.harting.dev/CSGOWTF/csgowtf/src/branch/master/LICENSE)
|
[](https://git.harting.dev/CSGOWTF/csgowtf/src/branch/master/LICENSE)
|
||||||
[](https://liberapay.com/CSGOWTF/)
|
[](https://liberapay.com/CSGOWTF/)
|
||||||
[](https://ci.somegit.dev/CSGOWTF/csgowtf)
|
[](https://liberapay.com/CSGOWTF/)
|
||||||
|
[](https://csgow.tf/)
|
||||||
|
<!--[](https://www.typescriptlang.org/)-->
|
||||||
|
|
||||||
**Statistics for CS2 matchmaking matches** - A complete rewrite of CSGOW.TF with modern web technologies.
|
### Statistics for CS:GO matchmaking matches.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 🚀 Quick Start
|
## Backend
|
||||||
|
This is the frontend to the [csgowtfd](https://git.harting.dev/CSGOWTF/csgowtfd) backend.
|
||||||
### Prerequisites
|
|
||||||
|
## Tips on how to contribute
|
||||||
- **Node.js** ≥ 18.0.0 (v20.11.0 recommended - see `.nvmrc`)
|
- If you are implementing or fixing an issue, please comment on the issue so work is not duplicated.
|
||||||
- **npm** or **yarn**
|
- If you want to implement a new feature, create an issue first describing the issue, so we know about it.
|
||||||
|
- Don't commit unnecessary changes to the codebase or debugging code.
|
||||||
### Installation
|
- Write meaningful commits or squash them.
|
||||||
|
- Please try to follow the code style of the rest of the codebase.
|
||||||
```bash
|
- Only make pull requests to the dev branch.
|
||||||
# Clone the repository
|
- Only implement one feature per pull request to keep it easy to understand.
|
||||||
git clone https://somegit.dev/CSGOWTF/csgowtf.git
|
- Expect comments or questions on your pull request from the project maintainers. We try to keep the code as consistent and maintainable as possible.
|
||||||
cd csgowtf
|
- Each pull request should come from a new branch in your fork, it should have a meaningful name.
|
||||||
|
|
||||||
# Switch to the cs2-port branch
|
|
||||||
git checkout cs2-port
|
|
||||||
|
|
||||||
# Install dependencies
|
|
||||||
npm install
|
|
||||||
|
|
||||||
# Copy environment variables
|
|
||||||
cp .env.example .env
|
|
||||||
|
|
||||||
# Start development server
|
|
||||||
npm run dev
|
|
||||||
```
|
|
||||||
|
|
||||||
The app will be available at `http://localhost:5173`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📦 Tech Stack
|
|
||||||
|
|
||||||
### Core Framework
|
|
||||||
|
|
||||||
- **SvelteKit 2.0** - Full-stack framework with SSR/SSG
|
|
||||||
- **Svelte 5** - Reactive UI framework
|
|
||||||
- **TypeScript 5.3** - Type safety (strict mode)
|
|
||||||
- **Vite 5** - Build tool and dev server
|
|
||||||
|
|
||||||
### Styling
|
|
||||||
|
|
||||||
- **Tailwind CSS 3.4** - Utility-first CSS framework
|
|
||||||
- **DaisyUI 4.0** - Component library with CS2 custom themes
|
|
||||||
- **PostCSS** - CSS processing
|
|
||||||
|
|
||||||
### Data & State
|
|
||||||
|
|
||||||
- **Axios** - HTTP client for API requests
|
|
||||||
- **Zod** - Runtime type validation and parsing
|
|
||||||
- **Svelte Stores** - State management
|
|
||||||
|
|
||||||
### Testing
|
|
||||||
|
|
||||||
- **Vitest** - Unit and component testing
|
|
||||||
- **Playwright** - End-to-end testing
|
|
||||||
- **Testing Library** - Component testing utilities
|
|
||||||
- **MSW** - API mocking
|
|
||||||
|
|
||||||
### Code Quality
|
|
||||||
|
|
||||||
- **ESLint** - Linting (TypeScript + Svelte)
|
|
||||||
- **Prettier** - Code formatting
|
|
||||||
- **Stylelint** - CSS linting
|
|
||||||
- **Husky** - Git hooks
|
|
||||||
- **lint-staged** - Pre-commit linting
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🛠️ Development
|
|
||||||
|
|
||||||
### Available Scripts
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Development
|
|
||||||
npm run dev # Start dev server
|
|
||||||
npm run dev -- --host # Expose to network
|
|
||||||
|
|
||||||
# Type Checking
|
|
||||||
npm run check # Run type check
|
|
||||||
npm run check:watch # Type check in watch mode
|
|
||||||
|
|
||||||
# Linting & Formatting
|
|
||||||
npm run lint # Run ESLint + Prettier check
|
|
||||||
npm run lint:fix # Auto-fix linting issues
|
|
||||||
npm run format # Format code with Prettier
|
|
||||||
|
|
||||||
# Testing
|
|
||||||
npm run test # Run unit tests
|
|
||||||
npm run test:watch # Run tests in watch mode
|
|
||||||
npm run test:coverage # Generate coverage report
|
|
||||||
npm run test:e2e # Run E2E tests (headless)
|
|
||||||
npm run test:e2e:ui # Run E2E tests with UI
|
|
||||||
npm run test:e2e:debug # Debug E2E tests
|
|
||||||
|
|
||||||
# Building
|
|
||||||
npm run build # Build for production
|
|
||||||
npm run preview # Preview production build
|
|
||||||
```
|
|
||||||
|
|
||||||
### Project Structure
|
|
||||||
|
|
||||||
```
|
|
||||||
csgowtf/
|
|
||||||
├── src/
|
|
||||||
│ ├── lib/
|
|
||||||
│ │ ├── api/ # API client & endpoints
|
|
||||||
│ │ ├── components/ # Reusable Svelte components
|
|
||||||
│ │ │ ├── layout/ # Header, Footer, Nav
|
|
||||||
│ │ │ ├── ui/ # Base UI components
|
|
||||||
│ │ │ ├── charts/ # Data visualization
|
|
||||||
│ │ │ ├── match/ # Match-specific components
|
|
||||||
│ │ │ └── player/ # Player-specific components
|
|
||||||
│ │ ├── stores/ # Svelte stores (state)
|
|
||||||
│ │ ├── types/ # TypeScript types
|
|
||||||
│ │ ├── utils/ # Helper functions
|
|
||||||
│ │ └── i18n/ # Internationalization
|
|
||||||
│ ├── routes/ # SvelteKit routes (pages)
|
|
||||||
│ ├── mocks/ # MSW mock handlers
|
|
||||||
│ ├── tests/ # Test setup
|
|
||||||
│ ├── app.html # HTML shell
|
|
||||||
│ └── app.css # Global styles
|
|
||||||
├── tests/
|
|
||||||
│ ├── unit/ # Unit tests
|
|
||||||
│ ├── integration/ # Integration tests
|
|
||||||
│ └── e2e/ # E2E tests
|
|
||||||
├── docs/ # Documentation
|
|
||||||
│ ├── API.md # Backend API reference
|
|
||||||
│ └── TODO.md # Project roadmap
|
|
||||||
├── public/ # Static assets
|
|
||||||
└── static/ # Additional static files
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🎨 Features
|
|
||||||
|
|
||||||
### Current (Phase 1 - ✅ Complete)
|
|
||||||
|
|
||||||
- ✅ SvelteKit project scaffolded with TypeScript strict mode
|
|
||||||
- ✅ Tailwind CSS + DaisyUI with CS2-themed color palette
|
|
||||||
- ✅ Complete development tooling (ESLint, Prettier, Husky)
|
|
||||||
- ✅ Testing infrastructure (Vitest + Playwright)
|
|
||||||
- ✅ CI/CD pipeline (Woodpecker)
|
|
||||||
- ✅ Backend API documented
|
|
||||||
|
|
||||||
### Planned (See `docs/TODO.md` for details)
|
|
||||||
|
|
||||||
- 🏠 Homepage with featured matches
|
|
||||||
- 📊 Match listing with advanced filters
|
|
||||||
- 👤 Player profiles with stats & charts
|
|
||||||
- 🎮 Match detail pages (overview, economy, flashes, damage, chat)
|
|
||||||
- 🌍 Multi-language support (i18n)
|
|
||||||
- 🌙 Dark/Light theme toggle (default: dark)
|
|
||||||
- 📱 Mobile-responsive design
|
|
||||||
- ♿ WCAG 2.1 AA accessibility
|
|
||||||
- 🎯 CS2-specific features (MR12, Premier rating, volumetric smokes)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🔗 Backend
|
|
||||||
|
|
||||||
This frontend connects to the [csgowtfd](https://somegit.dev/CSGOWTF/csgowtfd) backend.
|
|
||||||
|
|
||||||
- **Language**: Go
|
|
||||||
- **Framework**: Gin
|
|
||||||
- **Database**: PostgreSQL
|
|
||||||
- **Cache**: Redis
|
|
||||||
- **API Docs**: See `docs/API.md`
|
|
||||||
|
|
||||||
Default API endpoint: `http://localhost:8000`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🧪 Testing
|
|
||||||
|
|
||||||
### Unit & Component Tests
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Run all tests
|
|
||||||
npm run test
|
|
||||||
|
|
||||||
# Watch mode for TDD
|
|
||||||
npm run test:watch
|
|
||||||
|
|
||||||
# Generate coverage report
|
|
||||||
npm run test:coverage
|
|
||||||
```
|
|
||||||
|
|
||||||
### End-to-End Tests
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Run E2E tests (headless)
|
|
||||||
npm run test:e2e
|
|
||||||
|
|
||||||
# Run with Playwright UI
|
|
||||||
npm run test:e2e:ui
|
|
||||||
|
|
||||||
# Debug mode
|
|
||||||
npm run test:e2e:debug
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🚢 Deployment
|
|
||||||
|
|
||||||
### Build for Production
|
|
||||||
|
|
||||||
```bash
|
|
||||||
npm run build
|
|
||||||
```
|
|
||||||
|
|
||||||
The built app will be in the `build/` directory, ready to be deployed to any Node.js hosting platform.
|
|
||||||
|
|
||||||
### Environment Variables
|
|
||||||
|
|
||||||
See `.env.example` for all available configuration options:
|
|
||||||
|
|
||||||
- `VITE_API_BASE_URL` - Backend API URL
|
|
||||||
- `VITE_API_TIMEOUT` - API request timeout
|
|
||||||
- `VITE_ENABLE_LIVE_MATCHES` - Feature flag for live matches
|
|
||||||
- `VITE_ENABLE_ANALYTICS` - Feature flag for analytics
|
|
||||||
|
|
||||||
### CI/CD
|
|
||||||
|
|
||||||
Woodpecker CI automatically builds and deploys:
|
|
||||||
|
|
||||||
- **`master`** branch → Production
|
|
||||||
- **`dev`** branch → Development/Staging
|
|
||||||
- **`cs2-port`** branch → CS2 Preview (during rewrite)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🤝 Contributing
|
|
||||||
|
|
||||||
We welcome contributions! Please follow these guidelines:
|
|
||||||
|
|
||||||
### Before You Start
|
|
||||||
|
|
||||||
- Check existing issues or create one describing your feature/fix
|
|
||||||
- Comment on the issue to avoid duplicate work
|
|
||||||
- Fork the repository and create a feature branch
|
|
||||||
|
|
||||||
### Code Standards
|
|
||||||
|
|
||||||
- Follow TypeScript strict mode (no `any` types)
|
|
||||||
- Write tests for new features
|
|
||||||
- Follow existing code style (enforced by ESLint/Prettier)
|
|
||||||
- Keep components under 300 lines
|
|
||||||
- Write meaningful commit messages (Conventional Commits)
|
|
||||||
|
|
||||||
### Pull Request Process
|
|
||||||
|
|
||||||
1. Create a feature branch: `feature/your-feature-name`
|
|
||||||
2. Make your changes and commit with clear messages
|
|
||||||
3. Run linting and tests: `npm run lint && npm run test`
|
|
||||||
4. Push to your fork and create a PR to the `cs2-port` branch
|
|
||||||
5. Ensure CI passes and address review feedback
|
|
||||||
|
|
||||||
### Git Workflow
|
|
||||||
|
|
||||||
- Branch naming: `feature/`, `fix/`, `refactor/`, `docs/`
|
|
||||||
- Commit messages: `feat:`, `fix:`, `docs:`, `test:`, `refactor:`
|
|
||||||
- Only one feature/fix per PR
|
|
||||||
- Squash commits before merging
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📚 Documentation
|
|
||||||
|
|
||||||
- **API Reference**: [`docs/API.md`](docs/API.md) - Complete backend API documentation
|
|
||||||
- **Project Roadmap**: [`docs/TODO.md`](docs/TODO.md) - Detailed implementation plan
|
|
||||||
- **SvelteKit Docs**: [kit.svelte.dev](https://kit.svelte.dev/)
|
|
||||||
- **Tailwind CSS**: [tailwindcss.com](https://tailwindcss.com/)
|
|
||||||
- **DaisyUI**: [daisyui.com](https://daisyui.com/)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📄 License
|
|
||||||
|
|
||||||
[GPL-3.0](LICENSE) © CSGOW.TF Team
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 💖 Support
|
|
||||||
|
|
||||||
If you find this project helpful, consider supporting us:
|
|
||||||
|
|
||||||
[](https://liberapay.com/CSGOWTF/)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🔗 Links
|
|
||||||
|
|
||||||
- **Website**: [csgow.tf](https://csgow.tf) (legacy CS:GO version)
|
|
||||||
- **Backend**: [csgowtfd](https://somegit.dev/CSGOWTF/csgowtfd)
|
|
||||||
- **Issues**: [Report a bug](https://somegit.dev/CSGOWTF/csgowtf/issues)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**Status**: 🚧 **Phase 1 Complete** - Active rewrite for CS2 support
|
|
||||||
|
|||||||
1087
docs/API.md
@@ -1,393 +0,0 @@
|
|||||||
# API Proxying with SvelteKit Server Routes
|
|
||||||
|
|
||||||
This document explains how API requests are proxied to the backend using SvelteKit server routes.
|
|
||||||
|
|
||||||
## Why Use Server Routes?
|
|
||||||
|
|
||||||
The CS2.WTF frontend uses **SvelteKit server routes** to proxy API requests to the backend. This approach provides several benefits:
|
|
||||||
|
|
||||||
- ✅ **Works in all environments**: Development, preview, and production
|
|
||||||
- ✅ **No CORS issues**: Requests are server-side
|
|
||||||
- ✅ **Single code path**: Same behavior everywhere
|
|
||||||
- ✅ **Flexible backend switching**: Change one environment variable
|
|
||||||
- ✅ **Future-proof**: Can add caching, rate limiting, auth later
|
|
||||||
- ✅ **Better security**: Backend URL not exposed to client
|
|
||||||
|
|
||||||
## Architecture
|
|
||||||
|
|
||||||
### Request Flow
|
|
||||||
|
|
||||||
```
|
|
||||||
Browser → /api/matches → SvelteKit Server Route → Backend → Response
|
|
||||||
```
|
|
||||||
|
|
||||||
**Detailed Flow**:
|
|
||||||
|
|
||||||
```
|
|
||||||
1. Browser: GET http://localhost:5173/api/matches?limit=20
|
|
||||||
↓
|
|
||||||
2. SvelteKit: Routes to src/routes/api/[...path]/+server.ts
|
|
||||||
↓
|
|
||||||
3. Server Handler: Reads VITE_API_BASE_URL environment variable
|
|
||||||
↓
|
|
||||||
4. Backend Call: GET https://api.csgow.tf/matches?limit=20
|
|
||||||
↓
|
|
||||||
5. Backend: Returns JSON response
|
|
||||||
↓
|
|
||||||
6. Server Handler: Forwards response to browser
|
|
||||||
↓
|
|
||||||
7. Browser: Receives response (no CORS issues!)
|
|
||||||
```
|
|
||||||
|
|
||||||
**SSR (Server-Side Rendering) Flow**:
|
|
||||||
|
|
||||||
```
|
|
||||||
1. Page Load: +page.ts calls api.matches.getMatches()
|
|
||||||
↓
|
|
||||||
2. API Client: Detects import.meta.env.SSR === true
|
|
||||||
↓
|
|
||||||
3. Direct Call: GET https://api.csgow.tf/matches?limit=20
|
|
||||||
↓
|
|
||||||
4. Backend: Returns JSON response
|
|
||||||
↓
|
|
||||||
5. SSR: Renders page with data
|
|
||||||
```
|
|
||||||
|
|
||||||
**Note**: SSR bypasses the SvelteKit route and calls the backend directly because relative URLs (`/api`) don't work during server-side rendering.
|
|
||||||
|
|
||||||
### Key Components
|
|
||||||
|
|
||||||
**1. SvelteKit Server Route** (`src/routes/api/[...path]/+server.ts`)
|
|
||||||
|
|
||||||
- Catch-all route that matches `/api/*`
|
|
||||||
- Forwards requests to backend
|
|
||||||
- Supports GET, POST, DELETE methods
|
|
||||||
- Handles errors gracefully
|
|
||||||
|
|
||||||
**2. API Client** (`src/lib/api/client.ts`)
|
|
||||||
|
|
||||||
- Browser: Uses `/api` base URL (routes to SvelteKit)
|
|
||||||
- SSR: Uses `VITE_API_BASE_URL` directly (bypasses SvelteKit route)
|
|
||||||
- Automatically detects environment with `import.meta.env.SSR`
|
|
||||||
|
|
||||||
**3. Environment Variable** (`.env`)
|
|
||||||
|
|
||||||
- `VITE_API_BASE_URL` controls which backend to use
|
|
||||||
- Switch between local and production easily
|
|
||||||
|
|
||||||
## Configuration
|
|
||||||
|
|
||||||
### Environment Variables
|
|
||||||
|
|
||||||
**`.env`**:
|
|
||||||
|
|
||||||
```env
|
|
||||||
# Production API (default)
|
|
||||||
VITE_API_BASE_URL=https://api.csgow.tf
|
|
||||||
|
|
||||||
# Local backend (for development)
|
|
||||||
# VITE_API_BASE_URL=http://localhost:8000
|
|
||||||
```
|
|
||||||
|
|
||||||
**Switching Backends**:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Use production API
|
|
||||||
echo "VITE_API_BASE_URL=https://api.csgow.tf" > .env
|
|
||||||
npm run dev
|
|
||||||
|
|
||||||
# Use local backend
|
|
||||||
echo "VITE_API_BASE_URL=http://localhost:8000" > .env
|
|
||||||
npm run dev
|
|
||||||
```
|
|
||||||
|
|
||||||
### Server Route Implementation
|
|
||||||
|
|
||||||
**File**: `src/routes/api/[...path]/+server.ts`
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
import { error, json } from '@sveltejs/kit';
|
|
||||||
import type { RequestHandler } from './$types';
|
|
||||||
|
|
||||||
const API_BASE_URL = import.meta.env.VITE_API_BASE_URL || 'https://api.csgow.tf';
|
|
||||||
|
|
||||||
export const GET: RequestHandler = async ({ params, url }) => {
|
|
||||||
const path = params.path; // e.g., "matches"
|
|
||||||
const queryString = url.search; // e.g., "?limit=20"
|
|
||||||
|
|
||||||
const backendUrl = `${API_BASE_URL}/${path}${queryString}`;
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await fetch(backendUrl);
|
|
||||||
const data = await response.json();
|
|
||||||
return json(data);
|
|
||||||
} catch (err) {
|
|
||||||
throw error(503, 'Unable to connect to backend');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
### API Client Configuration
|
|
||||||
|
|
||||||
**File**: `src/lib/api/client.ts`
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
// Simple, single configuration
|
|
||||||
const API_BASE_URL = '/api';
|
|
||||||
|
|
||||||
// Always routes to SvelteKit server routes
|
|
||||||
// No environment detection needed
|
|
||||||
```
|
|
||||||
|
|
||||||
## Testing the Setup
|
|
||||||
|
|
||||||
### 1. Check Environment Variable
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cat .env
|
|
||||||
|
|
||||||
# Should show:
|
|
||||||
VITE_API_BASE_URL=https://api.csgow.tf
|
|
||||||
# or
|
|
||||||
VITE_API_BASE_URL=http://localhost:8000
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. Start Development Server
|
|
||||||
|
|
||||||
```bash
|
|
||||||
npm run dev
|
|
||||||
|
|
||||||
# Server starts on http://localhost:5173
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3. Check Network Requests
|
|
||||||
|
|
||||||
Open DevTools → Network tab:
|
|
||||||
|
|
||||||
- ✅ Requests go to `/api/matches`, `/api/player/123`, etc.
|
|
||||||
- ✅ Status should be `200 OK`
|
|
||||||
- ✅ No CORS errors in console
|
|
||||||
|
|
||||||
### 4. Test Both Backends
|
|
||||||
|
|
||||||
**Test Production API**:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Set production API
|
|
||||||
echo "VITE_API_BASE_URL=https://api.csgow.tf" > .env
|
|
||||||
|
|
||||||
# Start dev server
|
|
||||||
npm run dev
|
|
||||||
|
|
||||||
# Visit http://localhost:5173/matches
|
|
||||||
# Should load matches from production API
|
|
||||||
```
|
|
||||||
|
|
||||||
**Test Local Backend**:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Start local backend first
|
|
||||||
cd ../csgowtfd
|
|
||||||
go run main.go
|
|
||||||
|
|
||||||
# In another terminal, set local API
|
|
||||||
echo "VITE_API_BASE_URL=http://localhost:8000" > .env
|
|
||||||
|
|
||||||
# Start dev server
|
|
||||||
npm run dev
|
|
||||||
|
|
||||||
# Visit http://localhost:5173/matches
|
|
||||||
# Should load matches from local backend
|
|
||||||
```
|
|
||||||
|
|
||||||
## Common Issues
|
|
||||||
|
|
||||||
### Issue 1: 503 Service Unavailable
|
|
||||||
|
|
||||||
**Symptom**: API requests return 503 error
|
|
||||||
|
|
||||||
**Possible Causes**:
|
|
||||||
|
|
||||||
1. Backend is not running
|
|
||||||
2. Wrong `VITE_API_BASE_URL` in `.env`
|
|
||||||
3. Network connectivity issues
|
|
||||||
|
|
||||||
**Fix**:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Check .env file
|
|
||||||
cat .env
|
|
||||||
|
|
||||||
# If using local backend, make sure it's running
|
|
||||||
curl http://localhost:8000/matches
|
|
||||||
|
|
||||||
# If using production API, check connectivity
|
|
||||||
curl https://api.csgow.tf/matches
|
|
||||||
|
|
||||||
# Restart dev server after changing .env
|
|
||||||
npm run dev
|
|
||||||
```
|
|
||||||
|
|
||||||
### Issue 2: 404 Not Found
|
|
||||||
|
|
||||||
**Symptom**: `/api/*` routes return 404
|
|
||||||
|
|
||||||
**Cause**: SvelteKit server route file missing or not loaded
|
|
||||||
|
|
||||||
**Fix**:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Check file exists
|
|
||||||
ls src/routes/api/[...path]/+server.ts
|
|
||||||
|
|
||||||
# If missing, create it
|
|
||||||
mkdir -p src/routes/api/'[...path]'
|
|
||||||
# Then create +server.ts file
|
|
||||||
|
|
||||||
# Restart dev server
|
|
||||||
npm run dev
|
|
||||||
```
|
|
||||||
|
|
||||||
### Issue 3: Environment Variable Not Loading
|
|
||||||
|
|
||||||
**Symptom**: Server route uses wrong backend URL
|
|
||||||
|
|
||||||
**Cause**: Changes to `.env` require server restart
|
|
||||||
|
|
||||||
**Fix**:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Stop dev server (Ctrl+C)
|
|
||||||
|
|
||||||
# Update .env
|
|
||||||
echo "VITE_API_BASE_URL=http://localhost:8000" > .env
|
|
||||||
|
|
||||||
# Start dev server again
|
|
||||||
npm run dev
|
|
||||||
```
|
|
||||||
|
|
||||||
### Issue 4: CORS Errors Still Appearing
|
|
||||||
|
|
||||||
**Symptom**: Browser console shows CORS errors
|
|
||||||
|
|
||||||
**Cause**: API client is not using `/api` prefix
|
|
||||||
|
|
||||||
**Fix**:
|
|
||||||
Check `src/lib/api/client.ts`:
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
// Should be:
|
|
||||||
const API_BASE_URL = '/api';
|
|
||||||
|
|
||||||
// Not:
|
|
||||||
const API_BASE_URL = 'https://api.csgow.tf'; // ❌ Wrong
|
|
||||||
```
|
|
||||||
|
|
||||||
## How It Works Compared to Vite Proxy
|
|
||||||
|
|
||||||
### Old Approach (Vite Proxy)
|
|
||||||
|
|
||||||
```
|
|
||||||
Development:
|
|
||||||
Browser → /api → Vite Proxy → Backend
|
|
||||||
|
|
||||||
Production:
|
|
||||||
Browser → Backend (direct, different code path)
|
|
||||||
```
|
|
||||||
|
|
||||||
**Problems**:
|
|
||||||
|
|
||||||
- Two different code paths (dev vs prod)
|
|
||||||
- Proxy only works in development
|
|
||||||
- SSR has to bypass proxy
|
|
||||||
- Complex configuration
|
|
||||||
|
|
||||||
### New Approach (SvelteKit Server Routes)
|
|
||||||
|
|
||||||
```
|
|
||||||
All Environments:
|
|
||||||
Browser → /api → SvelteKit Route → Backend
|
|
||||||
```
|
|
||||||
|
|
||||||
**Benefits**:
|
|
||||||
|
|
||||||
- Single code path
|
|
||||||
- Works in dev, preview, and production
|
|
||||||
- Consistent behavior everywhere
|
|
||||||
- Simpler configuration
|
|
||||||
|
|
||||||
## Adding Features
|
|
||||||
|
|
||||||
### Add Request Caching
|
|
||||||
|
|
||||||
**File**: `src/routes/api/[...path]/+server.ts`
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
const cache = new Map<string, { data: any; expires: number }>();
|
|
||||||
|
|
||||||
export const GET: RequestHandler = async ({ params, url }) => {
|
|
||||||
const cacheKey = `${params.path}${url.search}`;
|
|
||||||
|
|
||||||
// Check cache
|
|
||||||
const cached = cache.get(cacheKey);
|
|
||||||
if (cached && Date.now() < cached.expires) {
|
|
||||||
return json(cached.data);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fetch from backend
|
|
||||||
const data = await fetch(`${API_BASE_URL}/${params.path}${url.search}`).then((r) => r.json());
|
|
||||||
|
|
||||||
// Cache for 5 minutes
|
|
||||||
cache.set(cacheKey, {
|
|
||||||
data,
|
|
||||||
expires: Date.now() + 5 * 60 * 1000
|
|
||||||
});
|
|
||||||
|
|
||||||
return json(data);
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
### Add Rate Limiting
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
import { rateLimit } from '$lib/server/rateLimit';
|
|
||||||
|
|
||||||
export const GET: RequestHandler = async ({ request, params, url }) => {
|
|
||||||
// Check rate limit
|
|
||||||
await rateLimit(request);
|
|
||||||
|
|
||||||
// Continue with normal flow...
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
### Add Authentication
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
export const GET: RequestHandler = async ({ request, params, url }) => {
|
|
||||||
// Get auth token from cookie
|
|
||||||
const token = request.headers.get('cookie')?.includes('auth_token');
|
|
||||||
|
|
||||||
// Forward to backend with auth
|
|
||||||
const response = await fetch(backendUrl, {
|
|
||||||
headers: {
|
|
||||||
Authorization: `Bearer ${token}`
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// ...
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
## Summary
|
|
||||||
|
|
||||||
| Feature | Vite Proxy | SvelteKit Routes |
|
|
||||||
| --------------------- | ---------- | ---------------- |
|
|
||||||
| Works in dev | ✅ | ✅ |
|
|
||||||
| Works in production | ❌ | ✅ |
|
|
||||||
| Single code path | ❌ | ✅ |
|
|
||||||
| Can add caching | ❌ | ✅ |
|
|
||||||
| Can add rate limiting | ❌ | ✅ |
|
|
||||||
| Can add auth | ❌ | ✅ |
|
|
||||||
| SSR compatible | ❌ | ✅ |
|
|
||||||
|
|
||||||
**SvelteKit server routes provide a production-ready, maintainable solution for API proxying that works in all environments.**
|
|
||||||
587
docs/DESIGN.md
@@ -1,587 +0,0 @@
|
|||||||
# CS2.WTF Design System
|
|
||||||
|
|
||||||
A modern, tactical design language inspired by Counter-Strike 2's in-game aesthetics.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🎨 Design Philosophy
|
|
||||||
|
|
||||||
### Core Principles
|
|
||||||
|
|
||||||
1. **Tactical & Data-Dense**: Inspired by CS2's HUD - information at a glance
|
|
||||||
2. **Dark-First**: Gaming-optimized dark theme as default
|
|
||||||
3. **Team Identity**: Leverage T-side (orange) and CT-side (blue) throughout
|
|
||||||
4. **Performance**: Smooth animations, no bloat
|
|
||||||
5. **Accessible**: WCAG 2.1 AA compliant
|
|
||||||
|
|
||||||
### Visual Language
|
|
||||||
|
|
||||||
- **Sharp Corners**: Minimal border radius (2-4px) for tactical feel
|
|
||||||
- **Neon Accents**: Subtle glows on interactive elements
|
|
||||||
- **Grid-Based**: 8px base unit for consistent spacing
|
|
||||||
- **Monospace Numbers**: Stats feel more tactical
|
|
||||||
- **Depth Through Layers**: Elevated cards with subtle shadows
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🎨 Color Palette
|
|
||||||
|
|
||||||
### Brand Colors
|
|
||||||
|
|
||||||
```css
|
|
||||||
/* Primary (CT Blue) */
|
|
||||||
--ct-blue: #5e98d9;
|
|
||||||
--ct-blue-light: #7eaee5;
|
|
||||||
--ct-blue-dark: #4a7ab3;
|
|
||||||
|
|
||||||
/* Secondary (T Orange) */
|
|
||||||
--t-orange: #d4a74a;
|
|
||||||
--t-orange-light: #e5c674;
|
|
||||||
--t-orange-dark: #b38a3a;
|
|
||||||
|
|
||||||
/* Accent (Success Green) */
|
|
||||||
--accent-green: #36d399;
|
|
||||||
```
|
|
||||||
|
|
||||||
### Base Colors (Dark Theme)
|
|
||||||
|
|
||||||
```css
|
|
||||||
/* Backgrounds */
|
|
||||||
--bg-primary: #0f172a; /* Slate 900 - Main background */
|
|
||||||
--bg-secondary: #1e293b; /* Slate 800 - Card background */
|
|
||||||
--bg-tertiary: #334155; /* Slate 700 - Hover states */
|
|
||||||
|
|
||||||
/* Text */
|
|
||||||
--text-primary: #e2e8f0; /* Slate 200 - Main text */
|
|
||||||
--text-secondary: #94a3b8; /* Slate 400 - Muted text */
|
|
||||||
--text-tertiary: #64748b; /* Slate 500 - Disabled text */
|
|
||||||
|
|
||||||
/* Borders */
|
|
||||||
--border-default: #334155; /* Slate 700 */
|
|
||||||
--border-accent: #475569; /* Slate 600 - Hover */
|
|
||||||
```
|
|
||||||
|
|
||||||
### Semantic Colors
|
|
||||||
|
|
||||||
```css
|
|
||||||
/* Status */
|
|
||||||
--success: #36d399; /* Win, positive stats */
|
|
||||||
--warning: #fbbd23; /* Neutral, info */
|
|
||||||
--error: #f87272; /* Loss, negative stats */
|
|
||||||
--info: #3abff8; /* Information, CT-related */
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📐 Typography
|
|
||||||
|
|
||||||
### Font Families
|
|
||||||
|
|
||||||
**Primary (UI Text):**
|
|
||||||
|
|
||||||
```css
|
|
||||||
font-family:
|
|
||||||
'Inter',
|
|
||||||
system-ui,
|
|
||||||
-apple-system,
|
|
||||||
sans-serif;
|
|
||||||
```
|
|
||||||
|
|
||||||
**Monospace (Stats & Numbers):**
|
|
||||||
|
|
||||||
```css
|
|
||||||
font-family: 'JetBrains Mono', 'Fira Code', Consolas, monospace;
|
|
||||||
```
|
|
||||||
|
|
||||||
### Type Scale
|
|
||||||
|
|
||||||
```css
|
|
||||||
/* Display */
|
|
||||||
--text-6xl: 3.75rem; /* 60px - Hero headings */
|
|
||||||
--text-5xl: 3rem; /* 48px - Page titles */
|
|
||||||
--text-4xl: 2.25rem; /* 36px - Section headers */
|
|
||||||
|
|
||||||
/* Headings */
|
|
||||||
--text-3xl: 1.875rem; /* 30px - Card titles */
|
|
||||||
--text-2xl: 1.5rem; /* 24px - Subsection headers */
|
|
||||||
--text-xl: 1.25rem; /* 20px - Large body */
|
|
||||||
|
|
||||||
/* Body */
|
|
||||||
--text-lg: 1.125rem; /* 18px - Prominent text */
|
|
||||||
--text-base: 1rem; /* 16px - Default body */
|
|
||||||
--text-sm: 0.875rem; /* 14px - Small text */
|
|
||||||
--text-xs: 0.75rem; /* 12px - Captions */
|
|
||||||
```
|
|
||||||
|
|
||||||
### Font Weights
|
|
||||||
|
|
||||||
```css
|
|
||||||
--font-normal: 400;
|
|
||||||
--font-medium: 500;
|
|
||||||
--font-semibold: 600;
|
|
||||||
--font-bold: 700;
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🏗️ Layout
|
|
||||||
|
|
||||||
### Spacing System (8px Grid)
|
|
||||||
|
|
||||||
```css
|
|
||||||
--space-1: 0.25rem; /* 4px */
|
|
||||||
--space-2: 0.5rem; /* 8px */
|
|
||||||
--space-3: 0.75rem; /* 12px */
|
|
||||||
--space-4: 1rem; /* 16px */
|
|
||||||
--space-6: 1.5rem; /* 24px */
|
|
||||||
--space-8: 2rem; /* 32px */
|
|
||||||
--space-12: 3rem; /* 48px */
|
|
||||||
--space-16: 4rem; /* 64px */
|
|
||||||
--space-24: 6rem; /* 96px */
|
|
||||||
```
|
|
||||||
|
|
||||||
### Container Widths
|
|
||||||
|
|
||||||
```css
|
|
||||||
--container-sm: 640px; /* Mobile landscape */
|
|
||||||
--container-md: 768px; /* Tablet */
|
|
||||||
--container-lg: 1024px; /* Desktop */
|
|
||||||
--container-xl: 1280px; /* Large desktop */
|
|
||||||
--container-2xl: 1536px; /* Extra large */
|
|
||||||
```
|
|
||||||
|
|
||||||
### Breakpoints
|
|
||||||
|
|
||||||
```css
|
|
||||||
/* Mobile first approach */
|
|
||||||
sm: 640px /* Tablet */
|
|
||||||
md: 768px /* Small desktop */
|
|
||||||
lg: 1024px /* Desktop */
|
|
||||||
xl: 1280px /* Large desktop */
|
|
||||||
2xl: 1536px /* Extra large */
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🎭 Components
|
|
||||||
|
|
||||||
### Cards
|
|
||||||
|
|
||||||
**Default Card:**
|
|
||||||
|
|
||||||
```css
|
|
||||||
background: var(--bg-secondary);
|
|
||||||
border: 1px solid var(--border-default);
|
|
||||||
border-radius: 4px;
|
|
||||||
padding: 1.5rem;
|
|
||||||
box-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1);
|
|
||||||
```
|
|
||||||
|
|
||||||
**Elevated Card:**
|
|
||||||
|
|
||||||
```css
|
|
||||||
box-shadow:
|
|
||||||
0 10px 15px -3px rgb(0 0 0 / 0.2),
|
|
||||||
0 4px 6px -4px rgb(0 0 0 / 0.1);
|
|
||||||
```
|
|
||||||
|
|
||||||
**Interactive Card (Hover):**
|
|
||||||
|
|
||||||
```css
|
|
||||||
transition: all 0.2s ease;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
border-color: var(--ct-blue);
|
|
||||||
box-shadow: 0 0 0 2px rgb(94 152 217 / 0.2);
|
|
||||||
transform: translateY(-2px);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Buttons
|
|
||||||
|
|
||||||
**Primary (CT Blue):**
|
|
||||||
|
|
||||||
```css
|
|
||||||
background: var(--ct-blue);
|
|
||||||
color: white;
|
|
||||||
padding: 0.75rem 1.5rem;
|
|
||||||
border-radius: 4px;
|
|
||||||
font-weight: 600;
|
|
||||||
transition: all 0.2s;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background: var(--ct-blue-dark);
|
|
||||||
box-shadow: 0 0 20px rgb(94 152 217 / 0.3);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**Secondary (T Orange):**
|
|
||||||
|
|
||||||
```css
|
|
||||||
background: var(--t-orange);
|
|
||||||
color: white;
|
|
||||||
/* Similar styling */
|
|
||||||
```
|
|
||||||
|
|
||||||
**Ghost:**
|
|
||||||
|
|
||||||
```css
|
|
||||||
background: transparent;
|
|
||||||
border: 1px solid var(--border-default);
|
|
||||||
color: var(--text-primary);
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background: var(--bg-tertiary);
|
|
||||||
border-color: var(--ct-blue);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Badges
|
|
||||||
|
|
||||||
**Team Badge:**
|
|
||||||
|
|
||||||
```css
|
|
||||||
/* T-Side */
|
|
||||||
background: rgb(212 167 74 / 0.1);
|
|
||||||
color: var(--t-orange-light);
|
|
||||||
border: 1px solid var(--t-orange-dark);
|
|
||||||
|
|
||||||
/* CT-Side */
|
|
||||||
background: rgb(94 152 217 / 0.1);
|
|
||||||
color: var(--ct-blue-light);
|
|
||||||
border: 1px solid var(--ct-blue-dark);
|
|
||||||
```
|
|
||||||
|
|
||||||
**Status Badge:**
|
|
||||||
|
|
||||||
```css
|
|
||||||
/* Win */
|
|
||||||
background: rgb(54 211 153 / 0.1);
|
|
||||||
color: var(--success);
|
|
||||||
|
|
||||||
/* Loss */
|
|
||||||
background: rgb(248 114 114 / 0.1);
|
|
||||||
color: var(--error);
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🌊 Animations
|
|
||||||
|
|
||||||
### Transitions
|
|
||||||
|
|
||||||
```css
|
|
||||||
/* Standard */
|
|
||||||
transition: all 0.2s ease;
|
|
||||||
|
|
||||||
/* Slow */
|
|
||||||
transition: all 0.3s ease;
|
|
||||||
|
|
||||||
/* Fast */
|
|
||||||
transition: all 0.15s ease;
|
|
||||||
```
|
|
||||||
|
|
||||||
### Keyframes
|
|
||||||
|
|
||||||
**Fade In:**
|
|
||||||
|
|
||||||
```css
|
|
||||||
@keyframes fadeIn {
|
|
||||||
from {
|
|
||||||
opacity: 0;
|
|
||||||
transform: translateY(-10px);
|
|
||||||
}
|
|
||||||
to {
|
|
||||||
opacity: 1;
|
|
||||||
transform: translateY(0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**Pulse (Live Indicator):**
|
|
||||||
|
|
||||||
```css
|
|
||||||
@keyframes pulse {
|
|
||||||
0%,
|
|
||||||
100% {
|
|
||||||
opacity: 1;
|
|
||||||
}
|
|
||||||
50% {
|
|
||||||
opacity: 0.5;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**Glow:**
|
|
||||||
|
|
||||||
```css
|
|
||||||
@keyframes glow {
|
|
||||||
0%,
|
|
||||||
100% {
|
|
||||||
box-shadow: 0 0 10px rgb(94 152 217 / 0.3);
|
|
||||||
}
|
|
||||||
50% {
|
|
||||||
box-shadow: 0 0 20px rgb(94 152 217 / 0.5);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🎯 Iconography
|
|
||||||
|
|
||||||
**Icon Library:** Lucide Icons (clean, modern, consistent)
|
|
||||||
|
|
||||||
**Icon Sizes:**
|
|
||||||
|
|
||||||
```css
|
|
||||||
--icon-xs: 16px;
|
|
||||||
--icon-sm: 20px;
|
|
||||||
--icon-md: 24px;
|
|
||||||
--icon-lg: 32px;
|
|
||||||
--icon-xl: 48px;
|
|
||||||
```
|
|
||||||
|
|
||||||
**Icon Colors:**
|
|
||||||
|
|
||||||
- Default: `text-slate-400`
|
|
||||||
- Active: `text-primary` or `text-secondary`
|
|
||||||
- Success: `text-success`
|
|
||||||
- Error: `text-error`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📊 Data Visualization
|
|
||||||
|
|
||||||
### Chart Colors
|
|
||||||
|
|
||||||
**Team Performance:**
|
|
||||||
|
|
||||||
- T-Side: `#d4a74a`
|
|
||||||
- CT-Side: `#5e98d9`
|
|
||||||
|
|
||||||
**Heatmaps:**
|
|
||||||
|
|
||||||
- Low: `#334155` (Slate 700)
|
|
||||||
- Medium: `#f59e0b` (Amber 500)
|
|
||||||
- High: `#ef4444` (Red 500)
|
|
||||||
|
|
||||||
**Line Charts:**
|
|
||||||
|
|
||||||
- Primary line: `#5e98d9`
|
|
||||||
- Secondary line: `#d4a74a`
|
|
||||||
- Grid: `rgb(51 65 85 / 0.3)`
|
|
||||||
|
|
||||||
### Tables
|
|
||||||
|
|
||||||
**Header:**
|
|
||||||
|
|
||||||
```css
|
|
||||||
background: var(--bg-primary);
|
|
||||||
font-weight: 600;
|
|
||||||
text-transform: uppercase;
|
|
||||||
font-size: 0.75rem;
|
|
||||||
letter-spacing: 0.05em;
|
|
||||||
color: var(--text-secondary);
|
|
||||||
```
|
|
||||||
|
|
||||||
**Row:**
|
|
||||||
|
|
||||||
```css
|
|
||||||
border-bottom: 1px solid var(--border-default);
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
background: var(--bg-tertiary);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**Stats (Numbers):**
|
|
||||||
|
|
||||||
```css
|
|
||||||
font-family: var(--font-mono);
|
|
||||||
font-variant-numeric: tabular-nums;
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## ♿ Accessibility
|
|
||||||
|
|
||||||
### Focus States
|
|
||||||
|
|
||||||
```css
|
|
||||||
&:focus-visible {
|
|
||||||
outline: 2px solid var(--ct-blue);
|
|
||||||
outline-offset: 2px;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Color Contrast
|
|
||||||
|
|
||||||
- Text on dark bg: Minimum 4.5:1 (WCAG AA)
|
|
||||||
- Large text: Minimum 3:1
|
|
||||||
- UI components: Minimum 3:1
|
|
||||||
|
|
||||||
### Motion
|
|
||||||
|
|
||||||
```css
|
|
||||||
@media (prefers-reduced-motion: reduce) {
|
|
||||||
* {
|
|
||||||
animation-duration: 0.01ms !important;
|
|
||||||
transition-duration: 0.01ms !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🎮 CS2-Specific Elements
|
|
||||||
|
|
||||||
### Rank Display
|
|
||||||
|
|
||||||
- Show Premier rating (0-30,000) with color coding
|
|
||||||
- Bronze: `#cd7f32`
|
|
||||||
- Silver: `#c0c0c0`
|
|
||||||
- Gold: `#ffd700`
|
|
||||||
- Legend: `#9b59b6`
|
|
||||||
|
|
||||||
### Map Thumbnails
|
|
||||||
|
|
||||||
- 16:9 aspect ratio
|
|
||||||
- Slight overlay gradient (bottom to top)
|
|
||||||
- Map name in bottom-left corner
|
|
||||||
|
|
||||||
### Weapon Icons
|
|
||||||
|
|
||||||
- Monochrome with subtle glow
|
|
||||||
- Size: 32x32px default
|
|
||||||
- Color: Match rarity (Consumer White, Mil-Spec Blue, etc.)
|
|
||||||
|
|
||||||
### Kill Feed
|
|
||||||
|
|
||||||
```css
|
|
||||||
.kill-feed-item {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 0.5rem;
|
|
||||||
padding: 0.25rem 0.5rem;
|
|
||||||
background: rgb(15 23 42 / 0.9);
|
|
||||||
border-left: 2px solid var(--t-orange);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📱 Responsive Design
|
|
||||||
|
|
||||||
### Mobile (< 768px)
|
|
||||||
|
|
||||||
- Stack layouts vertically
|
|
||||||
- Reduce padding/spacing by 25%
|
|
||||||
- Hide secondary information
|
|
||||||
- Larger tap targets (min 44x44px)
|
|
||||||
- Bottom navigation for main actions
|
|
||||||
|
|
||||||
### Tablet (768px - 1024px)
|
|
||||||
|
|
||||||
- Two-column layouts
|
|
||||||
- Collapsible sidebar
|
|
||||||
- Touch-optimized interactions
|
|
||||||
|
|
||||||
### Desktop (> 1024px)
|
|
||||||
|
|
||||||
- Three-column layouts where appropriate
|
|
||||||
- Hover states and tooltips
|
|
||||||
- Keyboard shortcuts
|
|
||||||
- Dense data tables
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🎨 Example Compositions
|
|
||||||
|
|
||||||
### Hero Section (Homepage)
|
|
||||||
|
|
||||||
```
|
|
||||||
┌─────────────────────────────────────────┐
|
|
||||||
│ │
|
|
||||||
│ CS2.WTF (Large Logo) │
|
|
||||||
│ Statistics for CS2 Matches │
|
|
||||||
│ │
|
|
||||||
│ [Search Match] [Browse Players] │
|
|
||||||
│ │
|
|
||||||
│ Featured Matches (Carousel) ────> │
|
|
||||||
│ │
|
|
||||||
└─────────────────────────────────────────┘
|
|
||||||
```
|
|
||||||
|
|
||||||
### Match Card
|
|
||||||
|
|
||||||
```
|
|
||||||
┌─────────────────────────────────────┐
|
|
||||||
│ de_inferno 13 - 10 LIVE │
|
|
||||||
│ ─────────────────────────────────── │
|
|
||||||
│ 👤 Player1 24K 18D ⭐⭐⭐ │
|
|
||||||
│ 👤 Player2 21K 20D ⭐⭐ │
|
|
||||||
│ ... │
|
|
||||||
│ ─────────────────────────────────── │
|
|
||||||
│ 📅 2 hours ago ⏱️ 42:33 │
|
|
||||||
└─────────────────────────────────────┘
|
|
||||||
```
|
|
||||||
|
|
||||||
### Stats Table
|
|
||||||
|
|
||||||
```
|
|
||||||
┌──────────────────────────────────────────────┐
|
|
||||||
│ PLAYER K D A HS% ADR RATING │
|
|
||||||
├──────────────────────────────────────────────┤
|
|
||||||
│ 👤 Player1 24 18 6 50% 98 1.32 🥇 │
|
|
||||||
│ 👤 Player2 21 20 8 48% 87 1.12 │
|
|
||||||
│ 👤 Player3 19 22 5 44% 82 0.98 │
|
|
||||||
└──────────────────────────────────────────────┘
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 🚀 Performance Guidelines
|
|
||||||
|
|
||||||
- Lazy load images and charts
|
|
||||||
- Use CSS transforms for animations (GPU-accelerated)
|
|
||||||
- Debounce search inputs (300ms)
|
|
||||||
- Virtual scrolling for large tables (> 100 rows)
|
|
||||||
- Optimize bundle size (< 200KB initial)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 📝 Naming Conventions
|
|
||||||
|
|
||||||
### CSS Classes
|
|
||||||
|
|
||||||
```css
|
|
||||||
/* Component */
|
|
||||||
.match-card {
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Element */
|
|
||||||
.match-card__header {
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Modifier */
|
|
||||||
.match-card--featured {
|
|
||||||
}
|
|
||||||
|
|
||||||
/* State */
|
|
||||||
.match-card.is-active {
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Tailwind Utilities
|
|
||||||
|
|
||||||
Prefer utility classes for spacing, colors, and common patterns:
|
|
||||||
|
|
||||||
```html
|
|
||||||
<div class="rounded-lg bg-base-200 p-6 shadow-lg"></div>
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**Last Updated**: 2025-11-04
|
|
||||||
**Status**: Active Development
|
|
||||||
@@ -1,480 +0,0 @@
|
|||||||
# CS2.WTF Feature Implementation Status
|
|
||||||
|
|
||||||
**Last Updated:** 2025-11-12
|
|
||||||
**Branch:** cs2-port
|
|
||||||
**Status:** In Progress (~70% Complete)
|
|
||||||
|
|
||||||
## Overview
|
|
||||||
|
|
||||||
This document tracks the implementation status of missing features from the original CS:GO WTF frontend that need to be ported to the new CS2.WTF SvelteKit application.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Phase 1: Critical Features (HIGH PRIORITY)
|
|
||||||
|
|
||||||
### ✅ 1. Player Tracking System
|
|
||||||
|
|
||||||
**Status:** COMPLETED
|
|
||||||
|
|
||||||
- ✅ Added `tracked` field to Player type
|
|
||||||
- ✅ Updated player schema validation
|
|
||||||
- ✅ Updated API transformer to pass through `tracked` field
|
|
||||||
- ✅ Created `TrackPlayerModal.svelte` component
|
|
||||||
- Auth code input
|
|
||||||
- Optional share code input
|
|
||||||
- Track/Untrack functionality
|
|
||||||
- Help text with instructions
|
|
||||||
- Loading states and error handling
|
|
||||||
- ✅ Integrated modal into player profile page
|
|
||||||
- ✅ Added tracking status indicator button
|
|
||||||
- ✅ Connected to API endpoints: `POST /player/:id/track` and `DELETE /player/:id/track`
|
|
||||||
|
|
||||||
**Files Modified:**
|
|
||||||
|
|
||||||
- `src/lib/types/Player.ts`
|
|
||||||
- `src/lib/schemas/player.schema.ts`
|
|
||||||
- `src/lib/api/transformers.ts`
|
|
||||||
- `src/routes/player/[id]/+page.svelte`
|
|
||||||
|
|
||||||
**Files Created:**
|
|
||||||
|
|
||||||
- `src/lib/components/player/TrackPlayerModal.svelte`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### ✅ 2. Match Share Code Parsing
|
|
||||||
|
|
||||||
**Status:** COMPLETED
|
|
||||||
|
|
||||||
- ✅ Created `ShareCodeInput.svelte` component
|
|
||||||
- Share code input with validation
|
|
||||||
- Submit button with loading state
|
|
||||||
- Parse status feedback (parsing/success/error)
|
|
||||||
- Auto-redirect to match page on success
|
|
||||||
- Help text with instructions
|
|
||||||
- ✅ Added component to matches page
|
|
||||||
- ✅ Connected to API endpoint: `GET /match/parse/:sharecode`
|
|
||||||
- ✅ Share code format validation
|
|
||||||
|
|
||||||
**Files Created:**
|
|
||||||
|
|
||||||
- `src/lib/components/match/ShareCodeInput.svelte`
|
|
||||||
|
|
||||||
**Files Modified:**
|
|
||||||
|
|
||||||
- `src/routes/matches/+page.svelte`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### ✅ 3. VAC/Game Ban Status Display (Player Profile)
|
|
||||||
|
|
||||||
**Status:** COMPLETED
|
|
||||||
|
|
||||||
- ✅ Added VAC ban badge with count and date
|
|
||||||
- ✅ Added Game ban badge with count and date
|
|
||||||
- ✅ Styled with error/warning colors
|
|
||||||
- ✅ Displays on player profile header
|
|
||||||
- ✅ Shows ban dates when available
|
|
||||||
|
|
||||||
**Files Modified:**
|
|
||||||
|
|
||||||
- `src/routes/player/[id]/+page.svelte`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 🔄 4. VAC Status Column on Match Scoreboard
|
|
||||||
|
|
||||||
**Status:** NOT STARTED
|
|
||||||
|
|
||||||
**TODO:**
|
|
||||||
|
|
||||||
- Add VAC status indicator column to scoreboard in `src/routes/match/[id]/+page.svelte`
|
|
||||||
- Add VAC status indicator to details tab table
|
|
||||||
- Style with red warning icon for players with VAC bans
|
|
||||||
- Tooltip with ban date on hover
|
|
||||||
|
|
||||||
**Files to Modify:**
|
|
||||||
|
|
||||||
- `src/routes/match/[id]/+page.svelte`
|
|
||||||
- `src/routes/match/[id]/details/+page.svelte`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 🔄 5. Weapons Statistics Tab
|
|
||||||
|
|
||||||
**Status:** NOT STARTED
|
|
||||||
|
|
||||||
**Requires:**
|
|
||||||
|
|
||||||
- New tab on match detail page
|
|
||||||
- Component to display weapon statistics
|
|
||||||
- Hitgroup visualization (similar to old HitgroupPuppet.vue)
|
|
||||||
- Weapon breakdown table with kills, damage, hits per weapon
|
|
||||||
- API endpoint already exists: `GET /match/:id/weapons`
|
|
||||||
- API method already exists: `matchesAPI.getMatchWeapons()`
|
|
||||||
|
|
||||||
**TODO:**
|
|
||||||
|
|
||||||
- Create `src/routes/match/[id]/weapons/+page.svelte`
|
|
||||||
- Create `src/routes/match/[id]/weapons/+page.ts` (load function)
|
|
||||||
- Create `src/lib/components/match/WeaponStats.svelte`
|
|
||||||
- Create `src/lib/components/match/HitgroupVisualization.svelte`
|
|
||||||
- Update match layout tabs to include weapons tab
|
|
||||||
|
|
||||||
**Estimated Effort:** 8-16 hours
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 🔄 6. Recently Visited Players (Home Page)
|
|
||||||
|
|
||||||
**Status:** NOT STARTED
|
|
||||||
|
|
||||||
**Requires:**
|
|
||||||
|
|
||||||
- localStorage tracking of visited player profiles
|
|
||||||
- Display on home page as cards
|
|
||||||
- Delete/clear functionality
|
|
||||||
- Limit to last 6-10 players
|
|
||||||
|
|
||||||
**TODO:**
|
|
||||||
|
|
||||||
- Create utility functions for localStorage management
|
|
||||||
- Create `src/lib/components/player/RecentlyVisitedPlayers.svelte`
|
|
||||||
- Add to home page (`src/routes/+page.svelte`)
|
|
||||||
- Track player visits in player profile page
|
|
||||||
- Add to preferences store
|
|
||||||
|
|
||||||
**Estimated Effort:** 4-6 hours
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Phase 2: Important Features (MEDIUM-HIGH PRIORITY)
|
|
||||||
|
|
||||||
### 🔄 7. Complete Scoreboard Columns
|
|
||||||
|
|
||||||
**Status:** NOT STARTED
|
|
||||||
|
|
||||||
**Missing Columns:**
|
|
||||||
|
|
||||||
- Player avatars (Steam avatar images)
|
|
||||||
- Color indicators (in-game player colors)
|
|
||||||
- In-game score column
|
|
||||||
- MVP stars column
|
|
||||||
- K/D ratio column (separate from K/D difference)
|
|
||||||
- Multi-kill indicators on scoreboard (currently only in Details tab)
|
|
||||||
|
|
||||||
**TODO:**
|
|
||||||
|
|
||||||
- Update `src/routes/match/[id]/+page.svelte` scoreboard table
|
|
||||||
- Add avatar column with Steam profile images
|
|
||||||
- Add color-coded player indicators
|
|
||||||
- Add Score, MVP, K/D ratio columns
|
|
||||||
- Move multi-kill indicators to scoreboard or add as tooltips
|
|
||||||
|
|
||||||
**Estimated Effort:** 6-8 hours
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 🔄 8. Sitemap Generation
|
|
||||||
|
|
||||||
**Status:** NOT STARTED
|
|
||||||
|
|
||||||
**Requires:**
|
|
||||||
|
|
||||||
- Dynamic sitemap generation based on players and matches
|
|
||||||
- XML sitemap endpoint
|
|
||||||
- Sitemap index for pagination
|
|
||||||
- robots.txt configuration
|
|
||||||
|
|
||||||
**TODO:**
|
|
||||||
|
|
||||||
- Create `src/routes/sitemap.xml/+server.ts`
|
|
||||||
- Create `src/routes/sitemap/[id]/+server.ts`
|
|
||||||
- Implement sitemap generation logic
|
|
||||||
- Add robots.txt to static folder
|
|
||||||
- Connect to backend sitemap endpoints if they exist
|
|
||||||
|
|
||||||
**Estimated Effort:** 6-8 hours
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 🔄 9. Team Average Rank Badges (Match Header)
|
|
||||||
|
|
||||||
**Status:** NOT STARTED
|
|
||||||
|
|
||||||
**Requires:**
|
|
||||||
|
|
||||||
- Calculate average Premier rating per team
|
|
||||||
- Display in match header/layout
|
|
||||||
- Show tier badges for each team
|
|
||||||
- Rank change indicators
|
|
||||||
|
|
||||||
**TODO:**
|
|
||||||
|
|
||||||
- Add calculation logic in `src/routes/match/[id]/+layout.svelte`
|
|
||||||
- Create component for team rank display
|
|
||||||
- Style with tier colors
|
|
||||||
|
|
||||||
**Estimated Effort:** 3-4 hours
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 🔄 10. Chat Message Translation
|
|
||||||
|
|
||||||
**Status:** NOT STARTED
|
|
||||||
|
|
||||||
**Requires:**
|
|
||||||
|
|
||||||
- Translation API integration (Google Translate, DeepL, or similar)
|
|
||||||
- Translate button on each chat message
|
|
||||||
- Language detection
|
|
||||||
- Cache translations
|
|
||||||
|
|
||||||
**TODO:**
|
|
||||||
|
|
||||||
- Choose translation API provider
|
|
||||||
- Add API key configuration
|
|
||||||
- Create translation service in `src/lib/services/translation.ts`
|
|
||||||
- Update `src/routes/match/[id]/chat/+page.svelte`
|
|
||||||
- Add translate button to chat messages
|
|
||||||
- Handle loading and error states
|
|
||||||
|
|
||||||
**Estimated Effort:** 8-12 hours
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Phase 3: Polish & Nice-to-Have (MEDIUM-LOW PRIORITY)
|
|
||||||
|
|
||||||
### 🔄 11. Steam Profile Links
|
|
||||||
|
|
||||||
**Status:** NOT STARTED
|
|
||||||
|
|
||||||
**TODO:**
|
|
||||||
|
|
||||||
- Add Steam profile link to player name on player profile page
|
|
||||||
- Add links to scoreboard player names
|
|
||||||
- Support for vanity URLs
|
|
||||||
- Open in new tab
|
|
||||||
|
|
||||||
**Files to Modify:**
|
|
||||||
|
|
||||||
- `src/routes/player/[id]/+page.svelte`
|
|
||||||
- `src/routes/match/[id]/+page.svelte`
|
|
||||||
- `src/routes/match/[id]/details/+page.svelte`
|
|
||||||
|
|
||||||
**Estimated Effort:** 2-3 hours
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 🔄 12. Win/Loss/Tie Statistics
|
|
||||||
|
|
||||||
**Status:** NOT STARTED
|
|
||||||
|
|
||||||
**TODO:**
|
|
||||||
|
|
||||||
- Display total wins, losses, ties on player profile
|
|
||||||
- Calculate win rate from these totals
|
|
||||||
- Add to player stats cards section
|
|
||||||
|
|
||||||
**Files to Modify:**
|
|
||||||
|
|
||||||
- `src/routes/player/[id]/+page.svelte`
|
|
||||||
|
|
||||||
**Estimated Effort:** 1-2 hours
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 🔄 13. Privacy Policy Page
|
|
||||||
|
|
||||||
**Status:** NOT STARTED
|
|
||||||
|
|
||||||
**TODO:**
|
|
||||||
|
|
||||||
- Create `src/routes/privacy-policy/+page.svelte`
|
|
||||||
- Write privacy policy content
|
|
||||||
- Add GDPR compliance information
|
|
||||||
- Link from footer
|
|
||||||
|
|
||||||
**Estimated Effort:** 2-4 hours
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 🔄 14. Player Color Indicators (Scoreboard)
|
|
||||||
|
|
||||||
**Status:** NOT STARTED
|
|
||||||
|
|
||||||
**TODO:**
|
|
||||||
|
|
||||||
- Display in-game player colors on scoreboard
|
|
||||||
- Color-code player rows or names
|
|
||||||
- Match CS2 color scheme (green/yellow/purple/blue/orange)
|
|
||||||
|
|
||||||
**Files to Modify:**
|
|
||||||
|
|
||||||
- `src/routes/match/[id]/+page.svelte`
|
|
||||||
|
|
||||||
**Estimated Effort:** 1-2 hours
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
### 🔄 15. Additional Utility Statistics
|
|
||||||
|
|
||||||
**Status:** NOT STARTED
|
|
||||||
|
|
||||||
**Missing Stats:**
|
|
||||||
|
|
||||||
- Self-flash statistics
|
|
||||||
- Smoke grenade usage
|
|
||||||
- Decoy grenade usage
|
|
||||||
- Team flash statistics
|
|
||||||
|
|
||||||
**TODO:**
|
|
||||||
|
|
||||||
- Display in match details or player profile
|
|
||||||
- Add to utility effectiveness section
|
|
||||||
|
|
||||||
**Estimated Effort:** 2-3 hours
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Feature Parity Comparison
|
|
||||||
|
|
||||||
### What's BETTER in Current Implementation ✨
|
|
||||||
|
|
||||||
- Modern SvelteKit architecture with TypeScript
|
|
||||||
- Superior filtering and search functionality
|
|
||||||
- Data export (CSV/JSON)
|
|
||||||
- Better data visualizations (Chart.js)
|
|
||||||
- Premier rating system (CS2-specific)
|
|
||||||
- Dark/light theme toggle
|
|
||||||
- Infinite scroll
|
|
||||||
- Better responsive design
|
|
||||||
|
|
||||||
### What's Currently Missing ⚠️
|
|
||||||
|
|
||||||
- Weapon statistics page (high impact)
|
|
||||||
- Complete scoreboard columns (medium impact)
|
|
||||||
- Recently visited players (medium impact)
|
|
||||||
- Sitemap/SEO (medium impact)
|
|
||||||
- Chat translation (low-medium impact)
|
|
||||||
- Various polish features (low impact)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Estimated Remaining Effort
|
|
||||||
|
|
||||||
### By Priority
|
|
||||||
|
|
||||||
| Priority | Tasks Remaining | Est. Hours | Status |
|
|
||||||
| ------------------- | --------------- | --------------- | ---------------- |
|
|
||||||
| Phase 1 (Critical) | 3 | 16-30 hours | 50% Complete |
|
|
||||||
| Phase 2 (Important) | 4 | 23-36 hours | 0% Complete |
|
|
||||||
| Phase 3 (Polish) | 5 | 8-14 hours | 0% Complete |
|
|
||||||
| **TOTAL** | **12** | **47-80 hours** | **25% Complete** |
|
|
||||||
|
|
||||||
### Overall Project Status
|
|
||||||
|
|
||||||
- **Completed:** 3 critical features
|
|
||||||
- **In Progress:** API cleanup and optimization
|
|
||||||
- **Remaining:** 12 features across 3 phases
|
|
||||||
- **Estimated Completion:** 2-3 weeks of full-time development
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Next Steps
|
|
||||||
|
|
||||||
### Immediate (This Session)
|
|
||||||
|
|
||||||
1. ✅ Player tracking UI - DONE
|
|
||||||
2. ✅ Share code parsing UI - DONE
|
|
||||||
3. ✅ VAC/ban status display (profile) - DONE
|
|
||||||
4. ⏭️ VAC status on scoreboard - NEXT
|
|
||||||
5. ⏭️ Weapons statistics tab - NEXT
|
|
||||||
6. ⏭️ Recently visited players - NEXT
|
|
||||||
|
|
||||||
### Short Term (Next Session)
|
|
||||||
|
|
||||||
- Complete remaining Phase 1 features
|
|
||||||
- Start Phase 2 features (scoreboard completion, sitemap)
|
|
||||||
|
|
||||||
### Medium Term
|
|
||||||
|
|
||||||
- Complete Phase 2 features
|
|
||||||
- Begin Phase 3 polish features
|
|
||||||
|
|
||||||
### Long Term
|
|
||||||
|
|
||||||
- Full feature parity with old frontend
|
|
||||||
- Additional CS2-specific features
|
|
||||||
- Performance optimizations
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Testing Checklist
|
|
||||||
|
|
||||||
### Completed Features
|
|
||||||
|
|
||||||
- [x] Player tracking modal opens and closes
|
|
||||||
- [x] Player tracking modal validates auth code input
|
|
||||||
- [x] Track/untrack API calls work
|
|
||||||
- [x] Tracking status updates after track/untrack
|
|
||||||
- [x] Share code input validates format
|
|
||||||
- [x] Share code parsing submits to API
|
|
||||||
- [x] Parse status feedback displays correctly
|
|
||||||
- [x] Redirect to match page after successful parse
|
|
||||||
- [x] VAC/ban badges display on player profile
|
|
||||||
- [x] VAC/ban dates show when available
|
|
||||||
|
|
||||||
### TODO Testing
|
|
||||||
|
|
||||||
- [ ] VAC status displays on scoreboard
|
|
||||||
- [ ] Weapons tab loads and displays data
|
|
||||||
- [ ] Hitgroup visualization renders correctly
|
|
||||||
- [ ] Recently visited players tracked correctly
|
|
||||||
- [ ] Recently visited players display on home page
|
|
||||||
- [ ] All Phase 2 and 3 features
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Known Issues
|
|
||||||
|
|
||||||
### Current
|
|
||||||
|
|
||||||
- None
|
|
||||||
|
|
||||||
### Potential
|
|
||||||
|
|
||||||
- Translation API rate limiting (once implemented)
|
|
||||||
- Sitemap generation performance with large datasets
|
|
||||||
- Weapons tab may need pagination for long matches
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Notes
|
|
||||||
|
|
||||||
### Architecture Decisions
|
|
||||||
|
|
||||||
- Using SvelteKit server routes for API proxying (no CORS issues)
|
|
||||||
- Transformers pattern for legacy API format conversion
|
|
||||||
- Component-based approach for reusability
|
|
||||||
- TypeScript + Zod for type safety
|
|
||||||
|
|
||||||
### API Endpoints Used
|
|
||||||
|
|
||||||
- ✅ `POST /player/:id/track`
|
|
||||||
- ✅ `DELETE /player/:id/track`
|
|
||||||
- ✅ `GET /match/parse/:sharecode`
|
|
||||||
- ⏭️ `GET /match/:id/weapons` (available but not used yet)
|
|
||||||
- ⏭️ `GET /player/:id/meta` (available but not optimized yet)
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Contributors
|
|
||||||
|
|
||||||
- Initial Analysis: Claude (Anthropic AI)
|
|
||||||
- Implementation: In Progress
|
|
||||||
- Testing: Pending
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**For questions or updates, refer to the main project README.md**
|
|
||||||
@@ -1,335 +0,0 @@
|
|||||||
# Local Development Setup
|
|
||||||
|
|
||||||
This guide will help you set up the CS2.WTF frontend for local development.
|
|
||||||
|
|
||||||
## Prerequisites
|
|
||||||
|
|
||||||
- **Node.js**: v18.x or v20.x (check with `node --version`)
|
|
||||||
- **npm**: v9.x or higher (comes with Node.js)
|
|
||||||
- **Backend API**: Either local csgowtfd service OR access to production API
|
|
||||||
|
|
||||||
## Quick Start
|
|
||||||
|
|
||||||
### 1. Install Dependencies
|
|
||||||
|
|
||||||
```bash
|
|
||||||
npm install
|
|
||||||
```
|
|
||||||
|
|
||||||
### 2. Environment Configuration
|
|
||||||
|
|
||||||
The `.env` file already exists in the project. You can use it as-is or modify it:
|
|
||||||
|
|
||||||
**Option A: Use Production API** (Recommended for frontend development)
|
|
||||||
|
|
||||||
```env
|
|
||||||
# Use the live production API - no local backend needed
|
|
||||||
VITE_API_BASE_URL=https://api.csgow.tf
|
|
||||||
VITE_API_TIMEOUT=10000
|
|
||||||
VITE_DEBUG_MODE=true
|
|
||||||
VITE_ENABLE_ANALYTICS=false
|
|
||||||
```
|
|
||||||
|
|
||||||
**Option B: Use Local Backend** (For full-stack development)
|
|
||||||
|
|
||||||
```env
|
|
||||||
# Use local backend (requires csgowtfd running on port 8000)
|
|
||||||
VITE_API_BASE_URL=http://localhost:8000
|
|
||||||
VITE_API_TIMEOUT=10000
|
|
||||||
VITE_DEBUG_MODE=true
|
|
||||||
VITE_ENABLE_ANALYTICS=false
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3. Start the Development Server
|
|
||||||
|
|
||||||
```bash
|
|
||||||
npm run dev
|
|
||||||
```
|
|
||||||
|
|
||||||
The frontend will be available at `http://localhost:5173`
|
|
||||||
|
|
||||||
You should see output like:
|
|
||||||
|
|
||||||
```
|
|
||||||
VITE v5.x.x ready in xxx ms
|
|
||||||
|
|
||||||
➜ Local: http://localhost:5173/
|
|
||||||
➜ Network: use --host to expose
|
|
||||||
```
|
|
||||||
|
|
||||||
### 4. (Optional) Start Local Backend
|
|
||||||
|
|
||||||
Only needed if using `VITE_API_BASE_URL=http://localhost:8000`:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# In the csgowtfd repository
|
|
||||||
cd ../csgowtfd
|
|
||||||
go run cmd/csgowtfd/main.go
|
|
||||||
```
|
|
||||||
|
|
||||||
Or use Docker:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
docker-compose up csgowtfd
|
|
||||||
```
|
|
||||||
|
|
||||||
## How SvelteKit API Routes Work
|
|
||||||
|
|
||||||
All API requests go through **SvelteKit server routes** which proxy to the backend. This works consistently in all environments.
|
|
||||||
|
|
||||||
### Request Flow (All Environments)
|
|
||||||
|
|
||||||
```
|
|
||||||
1. Browser makes request to: http://localhost:5173/api/matches
|
|
||||||
2. SvelteKit routes to: src/routes/api/[...path]/+server.ts
|
|
||||||
3. Server handler reads VITE_API_BASE_URL environment variable
|
|
||||||
4. Server fetches from backend: ${VITE_API_BASE_URL}/matches
|
|
||||||
5. Backend responds
|
|
||||||
6. Server handler forwards response to browser
|
|
||||||
```
|
|
||||||
|
|
||||||
### Benefits
|
|
||||||
|
|
||||||
- ✅ **No CORS errors** - All requests are server-side
|
|
||||||
- ✅ **Works in all environments** - Dev, preview, and production
|
|
||||||
- ✅ **Single code path** - Same behavior everywhere
|
|
||||||
- ✅ **Easy backend switching** - Change one environment variable
|
|
||||||
- ✅ **Future-proof** - Can add caching, rate limiting, auth later
|
|
||||||
- ✅ **Backend URL not exposed** - Hidden from client
|
|
||||||
|
|
||||||
### Switching Between Backends
|
|
||||||
|
|
||||||
Simply update `.env` and restart the dev server:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Use production API
|
|
||||||
echo "VITE_API_BASE_URL=https://api.csgow.tf" > .env
|
|
||||||
npm run dev
|
|
||||||
|
|
||||||
# Use local backend
|
|
||||||
echo "VITE_API_BASE_URL=http://localhost:8000" > .env
|
|
||||||
npm run dev
|
|
||||||
```
|
|
||||||
|
|
||||||
### Development vs Production
|
|
||||||
|
|
||||||
| Mode | Request Flow | Backend URL From |
|
|
||||||
| -------------------------------- | ---------------------------------------------- | ------------------------------ |
|
|
||||||
| **Development** (`npm run dev`) | Browser → `/api/*` → SvelteKit Route → Backend | `.env` → `VITE_API_BASE_URL` |
|
|
||||||
| **Production** (`npm run build`) | Browser → `/api/*` → SvelteKit Route → Backend | Build-time `VITE_API_BASE_URL` |
|
|
||||||
|
|
||||||
**Note**: The flow is identical in both modes - this is the key advantage over the old Vite proxy approach.
|
|
||||||
|
|
||||||
## Troubleshooting
|
|
||||||
|
|
||||||
### No Data Showing / Network Errors
|
|
||||||
|
|
||||||
**Problem**: Frontend loads but shows no matches, players show "Failed to load" errors.
|
|
||||||
|
|
||||||
**Solutions**:
|
|
||||||
|
|
||||||
1. **Check what backend you're using**:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Look at your .env file
|
|
||||||
cat .env | grep VITE_API_BASE_URL
|
|
||||||
```
|
|
||||||
|
|
||||||
2. **If using production API** (`https://api.csgow.tf`):
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Test if production API is accessible
|
|
||||||
curl https://api.csgow.tf/matches?limit=1
|
|
||||||
```
|
|
||||||
|
|
||||||
Should return JSON data. If not, production API may be down.
|
|
||||||
|
|
||||||
3. **If using local backend** (`http://localhost:8000`):
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Test if local backend is running
|
|
||||||
curl http://localhost:8000/matches?limit=1
|
|
||||||
```
|
|
||||||
|
|
||||||
If you get "Connection refused", start the backend service.
|
|
||||||
|
|
||||||
4. **Check browser console**:
|
|
||||||
- Open DevTools → Console tab
|
|
||||||
- Look for `[API Route]` error messages from the server route handler
|
|
||||||
- Network tab should show requests to `/api/*` (not external URLs)
|
|
||||||
- Check if requests return 503 (backend unreachable) or 500 (server error)
|
|
||||||
|
|
||||||
5. **Check server logs**:
|
|
||||||
- Look at the terminal running `npm run dev`
|
|
||||||
- Server route errors will appear with `[API Route] Error fetching...`
|
|
||||||
- This will show you the exact backend URL being requested
|
|
||||||
|
|
||||||
6. **Restart dev server**:
|
|
||||||
```bash
|
|
||||||
# Stop dev server (Ctrl+C)
|
|
||||||
npm run dev
|
|
||||||
```
|
|
||||||
|
|
||||||
### CORS Errors (Should Never Happen)
|
|
||||||
|
|
||||||
CORS errors should be impossible with SvelteKit server routes since all requests are server-side.
|
|
||||||
|
|
||||||
**If you somehow see CORS errors:**
|
|
||||||
|
|
||||||
- This means the API client is bypassing the `/api` routes
|
|
||||||
- Check that `src/lib/api/client.ts` has `API_BASE_URL = '/api'`
|
|
||||||
- Verify `src/routes/api/[...path]/+server.ts` exists
|
|
||||||
- Clear cache and restart:
|
|
||||||
```bash
|
|
||||||
rm -rf .svelte-kit
|
|
||||||
npm run dev
|
|
||||||
```
|
|
||||||
|
|
||||||
### Port Already in Use
|
|
||||||
|
|
||||||
If port 5173 is already in use:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Vite will automatically try the next available port
|
|
||||||
npm run dev
|
|
||||||
|
|
||||||
# Or specify a custom port
|
|
||||||
npm run dev -- --port 3000
|
|
||||||
```
|
|
||||||
|
|
||||||
### Backend Connection Issues
|
|
||||||
|
|
||||||
If the backend is on a different host/port, update `.env`:
|
|
||||||
|
|
||||||
```env
|
|
||||||
# Custom backend location
|
|
||||||
VITE_API_BASE_URL=http://192.168.1.100:8080
|
|
||||||
```
|
|
||||||
|
|
||||||
Then restart the dev server.
|
|
||||||
|
|
||||||
## Development Workflow
|
|
||||||
|
|
||||||
### 1. Make Changes
|
|
||||||
|
|
||||||
Edit files in `src/`. The dev server has hot module replacement (HMR):
|
|
||||||
|
|
||||||
- Component changes reload instantly
|
|
||||||
- Route changes reload the page
|
|
||||||
- Store changes reload affected components
|
|
||||||
|
|
||||||
### 2. Type Checking
|
|
||||||
|
|
||||||
Run TypeScript type checking:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
npm run check # Check once
|
|
||||||
npm run check:watch # Watch mode
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3. Linting
|
|
||||||
|
|
||||||
```bash
|
|
||||||
npm run lint # Check for issues
|
|
||||||
npm run lint:fix # Auto-fix issues
|
|
||||||
npm run format # Run Prettier
|
|
||||||
```
|
|
||||||
|
|
||||||
### 4. Testing
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Unit tests
|
|
||||||
npm run test # Run once
|
|
||||||
npm run test:watch # Watch mode
|
|
||||||
npm run test:coverage # Generate coverage report
|
|
||||||
|
|
||||||
# E2E tests
|
|
||||||
npm run test:e2e # Headless
|
|
||||||
npm run test:e2e:ui # Playwright UI
|
|
||||||
```
|
|
||||||
|
|
||||||
## API Endpoints
|
|
||||||
|
|
||||||
The backend provides these endpoints (see `docs/API.md` for full details):
|
|
||||||
|
|
||||||
- `GET /matches` - List all matches
|
|
||||||
- `GET /match/:id` - Get match details
|
|
||||||
- `GET /match/:id/rounds` - Get round economy data
|
|
||||||
- `GET /match/:id/weapons` - Get weapon statistics
|
|
||||||
- `GET /match/:id/chat` - Get chat messages
|
|
||||||
- `GET /player/:id` - Get player profile
|
|
||||||
|
|
||||||
### How Requests Work
|
|
||||||
|
|
||||||
**All Environments** (dev, preview, production):
|
|
||||||
|
|
||||||
```
|
|
||||||
Frontend code: api.matches.getMatches()
|
|
||||||
↓
|
|
||||||
API Client: GET /api/matches
|
|
||||||
↓
|
|
||||||
SvelteKit Route: src/routes/api/[...path]/+server.ts
|
|
||||||
↓
|
|
||||||
Server Handler: GET ${VITE_API_BASE_URL}/matches
|
|
||||||
↓
|
|
||||||
Response: ← Data returned to frontend
|
|
||||||
```
|
|
||||||
|
|
||||||
The request flow is identical in all environments. The only difference is which backend URL `VITE_API_BASE_URL` points to:
|
|
||||||
|
|
||||||
- Development: Usually `https://api.csgow.tf` (production API)
|
|
||||||
- Local full-stack: `http://localhost:8000` (local backend)
|
|
||||||
- Production: `https://api.csgow.tf` (or custom backend URL)
|
|
||||||
|
|
||||||
## Mock Data (Alternative: No Backend)
|
|
||||||
|
|
||||||
If you want to develop without any backend (local or production), enable MSW mocking:
|
|
||||||
|
|
||||||
1. Update `.env`:
|
|
||||||
|
|
||||||
```env
|
|
||||||
VITE_ENABLE_MSW_MOCKING=true
|
|
||||||
```
|
|
||||||
|
|
||||||
2. Restart dev server
|
|
||||||
|
|
||||||
The app will use mock data from `src/mocks/handlers/`.
|
|
||||||
|
|
||||||
**Note**: Mock data is limited and may not reflect all features. **Production API is recommended** for most development work.
|
|
||||||
|
|
||||||
## Building for Production
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Build
|
|
||||||
npm run build
|
|
||||||
|
|
||||||
# Preview production build locally
|
|
||||||
npm run preview
|
|
||||||
```
|
|
||||||
|
|
||||||
The preview server runs on `http://localhost:4173` and uses the production API configuration.
|
|
||||||
|
|
||||||
## Environment Variables Reference
|
|
||||||
|
|
||||||
| Variable | Default | Description |
|
|
||||||
| -------------------------- | ----------------------- | ---------------------------- |
|
|
||||||
| `VITE_API_BASE_URL` | `http://localhost:8000` | Backend API base URL |
|
|
||||||
| `VITE_API_TIMEOUT` | `10000` | Request timeout (ms) |
|
|
||||||
| `VITE_ENABLE_LIVE_MATCHES` | `false` | Enable live match polling |
|
|
||||||
| `VITE_ENABLE_ANALYTICS` | `false` | Enable analytics tracking |
|
|
||||||
| `VITE_DEBUG_MODE` | `false` | Enable debug logging |
|
|
||||||
| `VITE_ENABLE_MSW_MOCKING` | `false` | Use mock data instead of API |
|
|
||||||
|
|
||||||
## Getting Help
|
|
||||||
|
|
||||||
- **Frontend Issues**: Check browser console for errors
|
|
||||||
- **API Issues**: Check backend logs and proxy output in terminal
|
|
||||||
- **Type Errors**: Run `npm run check` for detailed messages
|
|
||||||
- **Build Issues**: Delete `.svelte-kit/` and `node_modules/`, then `npm install`
|
|
||||||
|
|
||||||
## Next Steps
|
|
||||||
|
|
||||||
- Read `TODO.md` for current development status
|
|
||||||
- Check `docs/DESIGN.md` for design system documentation
|
|
||||||
- Review `docs/API.md` for complete API reference
|
|
||||||
- See `README.md` for project overview
|
|
||||||
@@ -1,460 +0,0 @@
|
|||||||
# Matches API Endpoint Documentation
|
|
||||||
|
|
||||||
This document provides detailed information about the matches API endpoints used by CS2.WTF to retrieve match data from the backend CSGOWTFD service.
|
|
||||||
|
|
||||||
## Overview
|
|
||||||
|
|
||||||
The matches API provides access to Counter-Strike 2 match data including match listings, detailed match statistics, and related match information such as weapons, rounds, and chat data.
|
|
||||||
|
|
||||||
## Base URL
|
|
||||||
|
|
||||||
All endpoints are relative to the API base URL: `https://api.csgow.tf`
|
|
||||||
|
|
||||||
During development, requests are proxied through `/api` to avoid CORS issues.
|
|
||||||
|
|
||||||
## Authentication
|
|
||||||
|
|
||||||
No authentication is required for read operations. All match data is publicly accessible.
|
|
||||||
|
|
||||||
## Rate Limiting
|
|
||||||
|
|
||||||
The API does not currently enforce rate limiting, but clients should implement reasonable request throttling to avoid overwhelming the service.
|
|
||||||
|
|
||||||
## Endpoints
|
|
||||||
|
|
||||||
### 1. Get Matches List
|
|
||||||
|
|
||||||
Retrieves a paginated list of matches.
|
|
||||||
|
|
||||||
**Endpoint**: `GET /matches`
|
|
||||||
**Alternative**: `GET /matches/next/:time`
|
|
||||||
|
|
||||||
**Parameters**:
|
|
||||||
|
|
||||||
- `time` (path, optional): Unix timestamp for pagination (use with `/matches/next/:time`)
|
|
||||||
- Query parameters:
|
|
||||||
- `limit` (optional): Number of matches to return (default: 50, max: 100)
|
|
||||||
- `map` (optional): Filter by map name (e.g., `de_inferno`)
|
|
||||||
- `player_id` (optional): Filter by player Steam ID
|
|
||||||
|
|
||||||
**Response** (200 OK):
|
|
||||||
|
|
||||||
**IMPORTANT**: This endpoint returns a **plain array**, not an object with properties.
|
|
||||||
|
|
||||||
```json
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"match_id": "3589487716842078322",
|
|
||||||
"map": "de_inferno",
|
|
||||||
"date": 1730487900,
|
|
||||||
"score": [13, 10],
|
|
||||||
"duration": 2456,
|
|
||||||
"match_result": 1,
|
|
||||||
"max_rounds": 24,
|
|
||||||
"parsed": true,
|
|
||||||
"vac": false,
|
|
||||||
"game_ban": false
|
|
||||||
}
|
|
||||||
]
|
|
||||||
```
|
|
||||||
|
|
||||||
**Field Descriptions**:
|
|
||||||
|
|
||||||
- `match_id`: Unique match identifier (uint64 as string)
|
|
||||||
- `map`: Map name (can be empty string if not parsed)
|
|
||||||
- `date`: Unix timestamp (seconds since epoch)
|
|
||||||
- `score`: Array with two elements `[team_a_score, team_b_score]`
|
|
||||||
- `duration`: Match duration in seconds
|
|
||||||
- `match_result`: 0 = tie, 1 = team_a win, 2 = team_b win
|
|
||||||
- `max_rounds`: Maximum rounds (24 for MR12, 30 for MR15)
|
|
||||||
- `parsed`: Whether the demo has been parsed
|
|
||||||
- `vac`: Whether any player has a VAC ban
|
|
||||||
- `game_ban`: Whether any player has a game ban
|
|
||||||
|
|
||||||
**Pagination**:
|
|
||||||
|
|
||||||
- The API returns a plain array of matches, sorted by date (newest first)
|
|
||||||
- To get the next page, use the `date` field from the **last match** in the array
|
|
||||||
- Request `/matches/next/{timestamp}` where `{timestamp}` is the Unix timestamp
|
|
||||||
- Continue until the response returns fewer matches than your `limit` parameter
|
|
||||||
- Example: If you request `limit=20` and get back 15 matches, you've reached the end
|
|
||||||
|
|
||||||
### 2. Get Match Details
|
|
||||||
|
|
||||||
Retrieves detailed information about a specific match including player statistics.
|
|
||||||
|
|
||||||
**Endpoint**: `GET /match/{match_id}`
|
|
||||||
|
|
||||||
**Parameters**:
|
|
||||||
|
|
||||||
- `match_id` (path): The unique match identifier (uint64 as string)
|
|
||||||
|
|
||||||
**Response** (200 OK):
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"match_id": "3589487716842078322",
|
|
||||||
"share_code": "CSGO-XXXXX-XXXXX-XXXXX-XXXXX-XXXXX",
|
|
||||||
"map": "de_inferno",
|
|
||||||
"date": "2024-11-01T18:45:00Z",
|
|
||||||
"score_team_a": 13,
|
|
||||||
"score_team_b": 10,
|
|
||||||
"duration": 2456,
|
|
||||||
"match_result": 1,
|
|
||||||
"max_rounds": 24,
|
|
||||||
"demo_parsed": true,
|
|
||||||
"vac_present": false,
|
|
||||||
"gameban_present": false,
|
|
||||||
"tick_rate": 64.0, // Optional: not always provided by API
|
|
||||||
"players": [
|
|
||||||
{
|
|
||||||
"id": "765611980123456",
|
|
||||||
"name": "Player1",
|
|
||||||
"avatar": "https://steamcdn-a.akamaihd.net/steamcommunity/public/images/avatars/fe/fef49e7fa7e1997310d705b2a6158ff8dc1cdfeb_full.jpg",
|
|
||||||
"team_id": 2,
|
|
||||||
"kills": 24,
|
|
||||||
"deaths": 18,
|
|
||||||
"assists": 6,
|
|
||||||
"headshot": 12,
|
|
||||||
"mvp": 3,
|
|
||||||
"score": 56,
|
|
||||||
"kast": 78, // Optional: not always provided by API
|
|
||||||
"rank_old": 18500,
|
|
||||||
"rank_new": 18650,
|
|
||||||
"dmg_enemy": 2450,
|
|
||||||
"dmg_team": 120,
|
|
||||||
"flash_assists": 4,
|
|
||||||
"flash_duration_enemy": 15.6,
|
|
||||||
"flash_total_enemy": 8,
|
|
||||||
"ud_he": 450,
|
|
||||||
"ud_flames": 230,
|
|
||||||
"ud_flash": 5,
|
|
||||||
"ud_smoke": 3,
|
|
||||||
"avg_ping": 25.5,
|
|
||||||
"color": "yellow"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 3. Get Match Weapons
|
|
||||||
|
|
||||||
Retrieves weapon statistics for all players in a match.
|
|
||||||
|
|
||||||
**Endpoint**: `GET /match/{match_id}/weapons`
|
|
||||||
|
|
||||||
**Parameters**:
|
|
||||||
|
|
||||||
- `match_id` (path): The unique match identifier
|
|
||||||
|
|
||||||
**Response** (200 OK):
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"match_id": 3589487716842078322,
|
|
||||||
"weapons": [
|
|
||||||
{
|
|
||||||
"player_id": 765611980123456,
|
|
||||||
"weapon_stats": [
|
|
||||||
{
|
|
||||||
"eq_type": 17,
|
|
||||||
"weapon_name": "AK-47",
|
|
||||||
"kills": 12,
|
|
||||||
"damage": 1450,
|
|
||||||
"hits": 48,
|
|
||||||
"hit_groups": {
|
|
||||||
"head": 8,
|
|
||||||
"chest": 25,
|
|
||||||
"stomach": 8,
|
|
||||||
"left_arm": 3,
|
|
||||||
"right_arm": 2,
|
|
||||||
"left_leg": 1,
|
|
||||||
"right_leg": 1
|
|
||||||
},
|
|
||||||
"headshot_pct": 16.7
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 4. Get Match Rounds
|
|
||||||
|
|
||||||
Retrieves round-by-round statistics for a match.
|
|
||||||
|
|
||||||
**Endpoint**: `GET /match/{match_id}/rounds`
|
|
||||||
|
|
||||||
**Parameters**:
|
|
||||||
|
|
||||||
- `match_id` (path): The unique match identifier
|
|
||||||
|
|
||||||
**Response** (200 OK):
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"match_id": 3589487716842078322,
|
|
||||||
"rounds": [
|
|
||||||
{
|
|
||||||
"round": 1,
|
|
||||||
"winner": 2,
|
|
||||||
"win_reason": "elimination",
|
|
||||||
"players": [
|
|
||||||
{
|
|
||||||
"round": 1,
|
|
||||||
"player_id": 765611980123456,
|
|
||||||
"bank": 800,
|
|
||||||
"equipment": 650,
|
|
||||||
"spent": 650,
|
|
||||||
"kills_in_round": 2,
|
|
||||||
"damage_in_round": 120
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 5. Get Match Chat
|
|
||||||
|
|
||||||
Retrieves chat messages from a match.
|
|
||||||
|
|
||||||
**Endpoint**: `GET /match/{match_id}/chat`
|
|
||||||
|
|
||||||
**Parameters**:
|
|
||||||
|
|
||||||
- `match_id` (path): The unique match identifier
|
|
||||||
|
|
||||||
**Response** (200 OK):
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"match_id": 3589487716842078322,
|
|
||||||
"messages": [
|
|
||||||
{
|
|
||||||
"player_id": 765611980123456,
|
|
||||||
"player_name": "Player1",
|
|
||||||
"message": "nice shot!",
|
|
||||||
"tick": 15840,
|
|
||||||
"round": 8,
|
|
||||||
"all_chat": true,
|
|
||||||
"timestamp": "2024-11-01T19:12:34Z"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### 6. Parse Match from Share Code
|
|
||||||
|
|
||||||
Initiates parsing of a match from a CS:GO/CS2 share code.
|
|
||||||
|
|
||||||
**Endpoint**: `GET /match/parse/{sharecode}`
|
|
||||||
|
|
||||||
**Parameters**:
|
|
||||||
|
|
||||||
- `sharecode` (path): The CS:GO/CS2 match share code
|
|
||||||
|
|
||||||
**Response** (200 OK):
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"match_id": "3589487716842078322",
|
|
||||||
"status": "parsing",
|
|
||||||
"message": "Demo download and parsing initiated",
|
|
||||||
"estimated_time": 120
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Data Models
|
|
||||||
|
|
||||||
### Match
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
interface Match {
|
|
||||||
match_id: string; // Unique match identifier (uint64 as string)
|
|
||||||
share_code?: string; // CS:GO/CS2 share code (optional)
|
|
||||||
map: string; // Map name (e.g., "de_inferno")
|
|
||||||
date: string; // Match date and time (ISO 8601)
|
|
||||||
score_team_a: number; // Final score for team A
|
|
||||||
score_team_b: number; // Final score for team B
|
|
||||||
duration: number; // Match duration in seconds
|
|
||||||
match_result: number; // Match result: 0 = tie, 1 = team_a win, 2 = team_b win
|
|
||||||
max_rounds: number; // Maximum rounds (24 for MR12, 30 for MR15)
|
|
||||||
demo_parsed: boolean; // Whether the demo has been successfully parsed
|
|
||||||
vac_present: boolean; // Whether any player has a VAC ban
|
|
||||||
gameban_present: boolean; // Whether any player has a game ban
|
|
||||||
tick_rate?: number; // Server tick rate (64 or 128) - optional, not always provided by API
|
|
||||||
players?: MatchPlayer[]; // Array of player statistics (optional)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### MatchPlayer
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
interface MatchPlayer {
|
|
||||||
id: string; // Player Steam ID (uint64 as string)
|
|
||||||
name: string; // Player display name
|
|
||||||
avatar: string; // Steam avatar URL
|
|
||||||
team_id: number; // Team ID: 2 = T side, 3 = CT side
|
|
||||||
kills: number; // Kills
|
|
||||||
deaths: number; // Deaths
|
|
||||||
assists: number; // Assists
|
|
||||||
headshot: number; // Headshot kills
|
|
||||||
mvp: number; // MVP stars earned
|
|
||||||
score: number; // In-game score
|
|
||||||
kast?: number; // KAST percentage (0-100) - optional, not always provided by API
|
|
||||||
rank_old?: number; // Premier rating before match (0-30000)
|
|
||||||
rank_new?: number; // Premier rating after match (0-30000)
|
|
||||||
dmg_enemy?: number; // Damage to enemies
|
|
||||||
dmg_team?: number; // Damage to teammates
|
|
||||||
flash_assists?: number; // Flash assist count
|
|
||||||
flash_duration_enemy?: number; // Total enemy blind time
|
|
||||||
flash_total_enemy?: number; // Enemies flashed count
|
|
||||||
ud_he?: number; // HE grenade damage
|
|
||||||
ud_flames?: number; // Molotov/Incendiary damage
|
|
||||||
ud_flash?: number; // Flash grenades used
|
|
||||||
ud_smoke?: number; // Smoke grenades used
|
|
||||||
avg_ping?: number; // Average ping
|
|
||||||
color?: string; // Player color
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### MatchListItem
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
interface MatchListItem {
|
|
||||||
match_id: string; // Unique match identifier (uint64 as string)
|
|
||||||
map: string; // Map name
|
|
||||||
date: string; // Match date and time (ISO 8601)
|
|
||||||
score_team_a: number; // Final score for team A
|
|
||||||
score_team_b: number; // Final score for team B
|
|
||||||
duration: number; // Match duration in seconds
|
|
||||||
demo_parsed: boolean; // Whether the demo has been successfully parsed
|
|
||||||
player_count?: number; // Number of players in the match - optional, not provided by API
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Error Handling
|
|
||||||
|
|
||||||
All API errors follow a consistent format:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"error": "Error message",
|
|
||||||
"code": 404,
|
|
||||||
"details": {
|
|
||||||
"match_id": "3589487716842078322"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Common HTTP Status Codes
|
|
||||||
|
|
||||||
- `200 OK`: Request successful
|
|
||||||
- `400 Bad Request`: Invalid parameters
|
|
||||||
- `404 Not Found`: Resource not found
|
|
||||||
- `500 Internal Server Error`: Server error
|
|
||||||
|
|
||||||
## Implementation Notes
|
|
||||||
|
|
||||||
### Pagination
|
|
||||||
|
|
||||||
The matches API implements cursor-based pagination using timestamps:
|
|
||||||
|
|
||||||
1. Initial request to `/matches` returns a plain array of matches (sorted newest first)
|
|
||||||
2. Extract the `date` field from the **last match** in the array
|
|
||||||
3. Request `/matches/next/{timestamp}` to get older matches
|
|
||||||
4. Continue until the response returns fewer matches than your `limit` parameter
|
|
||||||
5. The API does **not** provide `has_more` or `next_page_time` fields - you must calculate these yourself
|
|
||||||
|
|
||||||
### Data Transformation
|
|
||||||
|
|
||||||
The frontend application transforms legacy API responses to a modern schema-validated format:
|
|
||||||
|
|
||||||
- Unix timestamps are converted to ISO strings
|
|
||||||
- Avatar hashes are converted to full URLs (if provided)
|
|
||||||
- Team IDs are normalized (1/2 → 2/3 if needed)
|
|
||||||
- Score arrays `[team_a, team_b]` are split into separate fields
|
|
||||||
- Field names are mapped: `parsed` → `demo_parsed`, `vac` → `vac_present`, `game_ban` → `gameban_present`
|
|
||||||
- Missing fields are provided with defaults (e.g., `tick_rate: 64`)
|
|
||||||
|
|
||||||
### Steam ID Handling
|
|
||||||
|
|
||||||
All Steam IDs and Match IDs are handled as strings to preserve uint64 precision. Never convert these to numbers as it causes precision loss.
|
|
||||||
|
|
||||||
## Examples
|
|
||||||
|
|
||||||
### Fetching Matches with Pagination
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
// Initial request - API returns a plain array
|
|
||||||
const matches = await fetch('/api/matches?limit=20').then((r) => r.json());
|
|
||||||
|
|
||||||
// matches is an array: [{ match_id, map, date, ... }, ...]
|
|
||||||
console.log(`Loaded ${matches.length} matches`);
|
|
||||||
|
|
||||||
// Get the timestamp of the last match for pagination
|
|
||||||
if (matches.length > 0) {
|
|
||||||
const lastMatch = matches[matches.length - 1];
|
|
||||||
const lastTimestamp = lastMatch.date; // Unix timestamp
|
|
||||||
|
|
||||||
// Fetch next page using the timestamp
|
|
||||||
const moreMatches = await fetch(`/api/matches/next/${lastTimestamp}?limit=20`).then((r) =>
|
|
||||||
r.json()
|
|
||||||
);
|
|
||||||
|
|
||||||
console.log(`Loaded ${moreMatches.length} more matches`);
|
|
||||||
|
|
||||||
// Check if we've reached the end
|
|
||||||
if (moreMatches.length < 20) {
|
|
||||||
console.log('Reached the end of matches');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Complete Pagination Loop
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
async function loadAllMatches(limit = 50) {
|
|
||||||
let allMatches = [];
|
|
||||||
let hasMore = true;
|
|
||||||
let lastTimestamp = null;
|
|
||||||
|
|
||||||
while (hasMore) {
|
|
||||||
// Build URL based on whether we have a timestamp
|
|
||||||
const url = lastTimestamp
|
|
||||||
? `/api/matches/next/${lastTimestamp}?limit=${limit}`
|
|
||||||
: `/api/matches?limit=${limit}`;
|
|
||||||
|
|
||||||
// Fetch matches
|
|
||||||
const matches = await fetch(url).then((r) => r.json());
|
|
||||||
|
|
||||||
// Add to collection
|
|
||||||
allMatches.push(...matches);
|
|
||||||
|
|
||||||
// Check if there are more
|
|
||||||
if (matches.length < limit) {
|
|
||||||
hasMore = false;
|
|
||||||
} else {
|
|
||||||
// Get timestamp of last match for next iteration
|
|
||||||
lastTimestamp = matches[matches.length - 1].date;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return allMatches;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Filtering Matches by Map
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const response = await fetch('/api/matches?map=de_inferno&limit=20');
|
|
||||||
const data = await response.json();
|
|
||||||
```
|
|
||||||
|
|
||||||
### Filtering Matches by Player
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const response = await fetch('/api/matches?player_id=765611980123456&limit=20');
|
|
||||||
const data = await response.json();
|
|
||||||
```
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
import js from '@eslint/js';
|
|
||||||
import ts from 'typescript-eslint';
|
|
||||||
import svelte from 'eslint-plugin-svelte';
|
|
||||||
import prettier from 'eslint-config-prettier';
|
|
||||||
import globals from 'globals';
|
|
||||||
|
|
||||||
/** @type {import('eslint').Linter.FlatConfig[]} */
|
|
||||||
export default [
|
|
||||||
js.configs.recommended,
|
|
||||||
...ts.configs.recommended,
|
|
||||||
...svelte.configs['flat/recommended'],
|
|
||||||
prettier,
|
|
||||||
...svelte.configs['flat/prettier'],
|
|
||||||
{
|
|
||||||
languageOptions: {
|
|
||||||
globals: {
|
|
||||||
...globals.browser,
|
|
||||||
...globals.node
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
files: ['**/*.svelte'],
|
|
||||||
languageOptions: {
|
|
||||||
parserOptions: {
|
|
||||||
parser: ts.parser
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
ignores: [
|
|
||||||
'build/',
|
|
||||||
'.svelte-kit/',
|
|
||||||
'dist/',
|
|
||||||
'node_modules/',
|
|
||||||
'**/*.cjs',
|
|
||||||
'*.config.js',
|
|
||||||
'*.config.ts'
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
rules: {
|
|
||||||
'@typescript-eslint/no-unused-vars': [
|
|
||||||
'error',
|
|
||||||
{
|
|
||||||
argsIgnorePattern: '^_',
|
|
||||||
varsIgnorePattern: '^_'
|
|
||||||
}
|
|
||||||
],
|
|
||||||
'@typescript-eslint/no-explicit-any': 'error',
|
|
||||||
'svelte/no-at-html-tags': 'warn'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
];
|
|
||||||
62
index.html
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta content="IE=edge" http-equiv="X-UA-Compatible">
|
||||||
|
<meta content="width=device-width,initial-scale=1.0" name="viewport">
|
||||||
|
|
||||||
|
<meta content="Track your CSGO matches and see your match details."
|
||||||
|
name="description">
|
||||||
|
<meta content="index, follow, archive"
|
||||||
|
name="robots">
|
||||||
|
<meta content="Track your CSGO matches and see your match details."
|
||||||
|
property="st:section">
|
||||||
|
<meta content="csgoWTF - Open source CSGO data platform"
|
||||||
|
name="twitter:title">
|
||||||
|
<meta content="Track your CSGO matches and see your match details."
|
||||||
|
name="twitter:description">
|
||||||
|
<meta content="summary_large_image"
|
||||||
|
name="twitter:card">
|
||||||
|
<meta content="https://csgow.tf/"
|
||||||
|
property="og:url">
|
||||||
|
<meta content="csgoWTF - Open source CSGO data platform"
|
||||||
|
property="og:title">
|
||||||
|
<meta content="Track your CSGO matches and see your match details."
|
||||||
|
property="og:description">
|
||||||
|
<meta content="website"
|
||||||
|
property="og:type">
|
||||||
|
<meta content="en_US"
|
||||||
|
property="og:locale">
|
||||||
|
<meta content="csgoWTF - Open source CSGO data platform"
|
||||||
|
property="og:site_name">
|
||||||
|
<meta content="https://csgow.tf/images/logo.png"
|
||||||
|
name="twitter:image">
|
||||||
|
<meta content="https://csgow.tf/images/logo.png"
|
||||||
|
property="og:image">
|
||||||
|
<meta content="1024"
|
||||||
|
property="og:image:width">
|
||||||
|
<meta content="526"
|
||||||
|
property="og:image:height">
|
||||||
|
<meta content="https://csgow.tf/images/logo.png"
|
||||||
|
property="og:image:secure_url">
|
||||||
|
|
||||||
|
<link href="/images/apple-touch-icon.png" rel="apple-touch-icon" sizes="180x180">
|
||||||
|
<link href="/images/favicon-32x32.png" rel="icon" sizes="32x32" type="image/png">
|
||||||
|
<link href="/images/favicon-16x16.png" rel="icon" sizes="16x16" type="image/png">
|
||||||
|
|
||||||
|
<link href="/site.webmanifest" rel="manifest">
|
||||||
|
|
||||||
|
<link rel="preconnect" href="https://steamcdn-a.akamaihd.net" crossorigin>
|
||||||
|
<link rel="dns-prefetch" href="https://steamcdn-a.akamaihd.net">
|
||||||
|
<link rel="preconnect" href="https://api.csgow.tf" crossorigin>
|
||||||
|
<link rel="dns-prefetch" href="https://api.csgow.tf">
|
||||||
|
<link rel="preconnect" href="https://piwik.harting.hosting" crossorigin>
|
||||||
|
<link rel="dns-prefetch" href="https://piwik.harting.hosting">
|
||||||
|
|
||||||
|
<title>csgoWTF</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="app"></div>
|
||||||
|
<script type="module" src="/src/main.ts"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
9106
package-lock.json
generated
112
package.json
@@ -1,79 +1,49 @@
|
|||||||
{
|
{
|
||||||
"name": "cs2wtf",
|
"name": "csgowtf",
|
||||||
"version": "2.0.0",
|
"version": "1.0.7",
|
||||||
"description": "Statistics for CS2 matchmaking matches",
|
|
||||||
"private": true,
|
|
||||||
"type": "module",
|
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite dev",
|
"dev": "vite",
|
||||||
"build": "vite build",
|
"host": "vite --host",
|
||||||
"preview": "vite preview",
|
"build": "vue-tsc --noEmit && vite build",
|
||||||
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
"preview": "vite preview --port 5050",
|
||||||
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
|
"typecheck": "vue-tsc --noEmit",
|
||||||
"lint": "prettier --check . && eslint .",
|
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore"
|
||||||
"lint:fix": "prettier --write . && eslint --fix .",
|
|
||||||
"format": "prettier --write .",
|
|
||||||
"test": "vitest run",
|
|
||||||
"test:watch": "vitest",
|
|
||||||
"test:coverage": "vitest run --coverage",
|
|
||||||
"test:e2e": "playwright test",
|
|
||||||
"test:e2e:ui": "playwright test --ui",
|
|
||||||
"test:e2e:debug": "playwright test --debug",
|
|
||||||
"prepare": "husky"
|
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@sveltejs/kit": "^2.0.0",
|
"@popperjs/core": "^2.11.4",
|
||||||
"axios": "^1.6.0",
|
"axios": "^0.26.1",
|
||||||
"chart.js": "^4.5.1",
|
"bootstrap": "^5.1.3",
|
||||||
"svelte": "^5.0.0",
|
"bootstrap-icons": "^1.8.1",
|
||||||
"zod": "^3.22.0"
|
"csgo-sharecode": "^3.0.1",
|
||||||
|
"echarts": "^5.3.1",
|
||||||
|
"fork-awesome": "^1.2.0",
|
||||||
|
"http-status-codes": "^2.2.0",
|
||||||
|
"iso-639-1": "^2.1.13",
|
||||||
|
"jquery": "^3.6.0",
|
||||||
|
"luxon": "^2.3.1",
|
||||||
|
"pinia": "^2.0.12",
|
||||||
|
"vue": "^3.2.31",
|
||||||
|
"vue-matomo": "^4.1.0",
|
||||||
|
"vue-router": "^4.0.14",
|
||||||
|
"vue3-cookies": "^1.0.6",
|
||||||
|
"vuex": "^4.0.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@playwright/test": "^1.40.0",
|
"@rushstack/eslint-patch": "^1.1.1",
|
||||||
"@sveltejs/adapter-auto": "^3.0.0",
|
"@types/echarts": "^4.9.13",
|
||||||
"@sveltejs/adapter-node": "^5.0.0",
|
"@types/luxon": "^2.3.1",
|
||||||
"@sveltejs/vite-plugin-svelte": "^4.0.0",
|
"@types/node": "^16.11.26",
|
||||||
"@testing-library/jest-dom": "^6.0.0",
|
"@vitejs/plugin-vue": "^2.2.4",
|
||||||
"@testing-library/svelte": "^5.0.0",
|
"@vue/eslint-config-prettier": "^7.0.0",
|
||||||
"@types/node": "^20.10.0",
|
"@vue/eslint-config-typescript": "^10.0.0",
|
||||||
"@typescript-eslint/eslint-plugin": "^7.0.0",
|
"@vue/tsconfig": "^0.1.3",
|
||||||
"@typescript-eslint/parser": "^7.0.0",
|
"eslint": "^8.11.0",
|
||||||
"@vitest/coverage-v8": "^1.0.0",
|
"eslint-plugin-vue": "^8.5.0",
|
||||||
"autoprefixer": "^10.4.0",
|
"prettier": "^2.6.0",
|
||||||
"daisyui": "^4.0.0",
|
"sass": "^1.49.9",
|
||||||
"eslint": "^8.57.0",
|
"typescript": "~4.6.2",
|
||||||
"eslint-config-prettier": "^9.1.0",
|
"vite": "^2.8.6",
|
||||||
"eslint-plugin-svelte": "^2.35.0",
|
"vue-tsc": "^0.33.2"
|
||||||
"globals": "^15.0.0",
|
|
||||||
"husky": "^9.0.0",
|
|
||||||
"jsdom": "^24.0.0",
|
|
||||||
"lint-staged": "^15.0.0",
|
|
||||||
"lucide-svelte": "^0.400.0",
|
|
||||||
"msw": "^2.0.0",
|
|
||||||
"postcss": "^8.4.0",
|
|
||||||
"prettier": "^3.2.0",
|
|
||||||
"prettier-plugin-svelte": "^3.1.0",
|
|
||||||
"prettier-plugin-tailwindcss": "^0.5.0",
|
|
||||||
"stylelint": "^16.0.0",
|
|
||||||
"stylelint-config-standard": "^36.0.0",
|
|
||||||
"svelte-check": "^4.0.0",
|
|
||||||
"tailwindcss": "^3.4.0",
|
|
||||||
"tslib": "^2.6.0",
|
|
||||||
"typescript": "^5.3.0",
|
|
||||||
"typescript-eslint": "^8.0.0",
|
|
||||||
"vite": "^5.0.0",
|
|
||||||
"vitest": "^1.0.0"
|
|
||||||
},
|
},
|
||||||
"lint-staged": {
|
"packageManager": "yarn@3.2.0"
|
||||||
"*.{js,ts,svelte}": [
|
|
||||||
"prettier --write",
|
|
||||||
"eslint --fix"
|
|
||||||
],
|
|
||||||
"*.{json,css,md}": [
|
|
||||||
"prettier --write"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=18.0.0"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,36 +0,0 @@
|
|||||||
import type { PlaywrightTestConfig } from '@playwright/test';
|
|
||||||
|
|
||||||
const config: PlaywrightTestConfig = {
|
|
||||||
webServer: {
|
|
||||||
command: 'npm run build && npm run preview',
|
|
||||||
port: 4173,
|
|
||||||
reuseExistingServer: !process.env.CI
|
|
||||||
},
|
|
||||||
testDir: 'tests/e2e',
|
|
||||||
testMatch: /(.+\.)?(test|spec)\.[jt]s/,
|
|
||||||
use: {
|
|
||||||
baseURL: 'http://localhost:4173',
|
|
||||||
screenshot: 'only-on-failure',
|
|
||||||
trace: 'retain-on-failure'
|
|
||||||
},
|
|
||||||
projects: [
|
|
||||||
{
|
|
||||||
name: 'chromium',
|
|
||||||
use: { browserName: 'chromium' }
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'firefox',
|
|
||||||
use: { browserName: 'firefox' }
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'webkit',
|
|
||||||
use: { browserName: 'webkit' }
|
|
||||||
}
|
|
||||||
],
|
|
||||||
reporter: process.env.CI ? 'github' : 'html',
|
|
||||||
forbidOnly: !!process.env.CI,
|
|
||||||
retries: process.env.CI ? 2 : 0,
|
|
||||||
workers: process.env.CI ? 1 : undefined
|
|
||||||
};
|
|
||||||
|
|
||||||
export default config;
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
module.exports = {
|
|
||||||
plugins: {
|
|
||||||
tailwindcss: {},
|
|
||||||
autoprefixer: {}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
BIN
public/fonts/OpenSans-Italic-VariableFont_wdth,wght.ttf
Normal file
BIN
public/fonts/OpenSans-Italic-VariableFont_wdth,wght.woff2
Normal file
BIN
public/fonts/OpenSans-VariableFont_wdth,wght.ttf
Normal file
BIN
public/fonts/OpenSans-VariableFont_wdth,wght.woff2
Normal file
BIN
public/fonts/Orbitron-VariableFont_wght.ttf
Normal file
BIN
public/fonts/Orbitron-VariableFont_wght.woff2
Normal file
|
Before Width: | Height: | Size: 7.0 KiB After Width: | Height: | Size: 7.0 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 6.2 KiB After Width: | Height: | Size: 6.2 KiB |
|
Before Width: | Height: | Size: 504 B After Width: | Height: | Size: 504 B |
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.0 KiB |
|
Before Width: | Height: | Size: 1.5 MiB After Width: | Height: | Size: 1.5 MiB |
|
Before Width: | Height: | Size: 7.9 KiB After Width: | Height: | Size: 7.9 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 3.8 KiB |
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 6.3 KiB After Width: | Height: | Size: 6.3 KiB |
|
Before Width: | Height: | Size: 5.9 KiB After Width: | Height: | Size: 5.9 KiB |
|
Before Width: | Height: | Size: 6.2 KiB After Width: | Height: | Size: 6.2 KiB |
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 3.7 KiB |
|
Before Width: | Height: | Size: 6.1 KiB After Width: | Height: | Size: 6.1 KiB |
|
Before Width: | Height: | Size: 9.5 KiB After Width: | Height: | Size: 9.5 KiB |
|
Before Width: | Height: | Size: 8.0 KiB After Width: | Height: | Size: 8.0 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 52 KiB |
|
Before Width: | Height: | Size: 70 KiB After Width: | Height: | Size: 70 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 23 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 214 KiB After Width: | Height: | Size: 214 KiB |
|
Before Width: | Height: | Size: 210 KiB After Width: | Height: | Size: 210 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 212 KiB After Width: | Height: | Size: 212 KiB |
|
Before Width: | Height: | Size: 217 KiB After Width: | Height: | Size: 217 KiB |
|
Before Width: | Height: | Size: 214 KiB After Width: | Height: | Size: 214 KiB |
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 5.5 KiB After Width: | Height: | Size: 5.5 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 209 KiB After Width: | Height: | Size: 209 KiB |
|
Before Width: | Height: | Size: 212 KiB After Width: | Height: | Size: 212 KiB |
|
Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 43 KiB |
|
Before Width: | Height: | Size: 211 KiB After Width: | Height: | Size: 211 KiB |
|
Before Width: | Height: | Size: 214 KiB After Width: | Height: | Size: 214 KiB |
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 211 KiB After Width: | Height: | Size: 211 KiB |
|
Before Width: | Height: | Size: 208 KiB After Width: | Height: | Size: 208 KiB |
|
Before Width: | Height: | Size: 212 KiB After Width: | Height: | Size: 212 KiB |
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 213 KiB After Width: | Height: | Size: 213 KiB |
|
Before Width: | Height: | Size: 278 KiB After Width: | Height: | Size: 278 KiB |
|
Before Width: | Height: | Size: 61 KiB After Width: | Height: | Size: 61 KiB |
|
Before Width: | Height: | Size: 213 KiB After Width: | Height: | Size: 213 KiB |
|
Before Width: | Height: | Size: 212 KiB After Width: | Height: | Size: 212 KiB |
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 231 KiB After Width: | Height: | Size: 231 KiB |
|
Before Width: | Height: | Size: 212 KiB After Width: | Height: | Size: 212 KiB |
|
Before Width: | Height: | Size: 212 KiB After Width: | Height: | Size: 212 KiB |
|
Before Width: | Height: | Size: 214 KiB After Width: | Height: | Size: 214 KiB |
|
Before Width: | Height: | Size: 210 KiB After Width: | Height: | Size: 210 KiB |
|
Before Width: | Height: | Size: 211 KiB After Width: | Height: | Size: 211 KiB |
|
Before Width: | Height: | Size: 212 KiB After Width: | Height: | Size: 212 KiB |
|
Before Width: | Height: | Size: 87 KiB After Width: | Height: | Size: 87 KiB |