Initial commit
This commit is contained in:
commit
ca04cc51f3
|
@ -0,0 +1,28 @@
|
|||
# Created by https://www.toptal.com/developers/gitignore/api/go
|
||||
# Edit at https://www.toptal.com/developers/gitignore?templates=go
|
||||
|
||||
### Go ###
|
||||
# If you prefer the allow list template instead of the deny list, see community template:
|
||||
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
|
||||
#
|
||||
# Binaries for programs and plugins
|
||||
*.exe
|
||||
*.exe~
|
||||
*.dll
|
||||
*.so
|
||||
*.dylib
|
||||
|
||||
# Test binary, built with `go test -c`
|
||||
*.test
|
||||
|
||||
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||
*.out
|
||||
|
||||
# Dependency directories (remove the comment below to include it)
|
||||
# vendor/
|
||||
|
||||
# Go workspace file
|
||||
go.work
|
||||
|
||||
# End of https://www.toptal.com/developers/gitignore/api/go
|
||||
nn
|
|
@ -0,0 +1,36 @@
|
|||
# run templ generation in watch mode to detect all .templ files and
|
||||
# re-create _templ.txt files on change, then send reload event to browser.
|
||||
# Default url: http://localhost:7331
|
||||
live/templ:
|
||||
templ generate --watch --proxy="http://localhost:8080" --open-browser=false -v
|
||||
|
||||
# run air to detect any go file changes to re-build and re-run the server.
|
||||
live/server:
|
||||
go run github.com/cosmtrek/air@v1.51.0 \
|
||||
--build.cmd "go build -o tmp/bin/main" --build.bin "tmp/bin/main" --build.delay "100" \
|
||||
--build.exclude_dir "node_modules" \
|
||||
--build.include_ext "go" \
|
||||
--build.stop_on_error "false" \
|
||||
--misc.clean_on_exit true
|
||||
|
||||
# run tailwindcss to generate the styles.css bundle in watch mode.
|
||||
live/tailwind:
|
||||
npx tailwindcss -i ./style/input.css -o ./assets/styles.css --minify --watch
|
||||
|
||||
# run esbuild to generate the index.js bundle in watch mode.
|
||||
live/esbuild:
|
||||
npx esbuild script/index.ts --bundle --outdir=assets/ --watch=forever
|
||||
|
||||
# watch for any js or css change in the assets/ folder, then reload the browser via templ proxy.
|
||||
live/sync_assets:
|
||||
go run github.com/cosmtrek/air@v1.51.0 \
|
||||
--build.cmd "templ generate --notify-proxy" \
|
||||
--build.bin "true" \
|
||||
--build.delay "100" \
|
||||
--build.exclude_dir "" \
|
||||
--build.include_dir "assets" \
|
||||
--build.include_ext "js,css"
|
||||
|
||||
# start all 5 watch processes in parallel.
|
||||
live:
|
||||
make -j5 live/templ live/server live/tailwind live/esbuild live/sync_assets
|
Binary file not shown.
After Width: | Height: | Size: 15 KiB |
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 545 KiB |
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,14 @@
|
|||
module gitea.henriburau.de/haw-lan/cod4watcher
|
||||
|
||||
go 1.22.2
|
||||
|
||||
require github.com/a-h/templ v0.2.707
|
||||
|
||||
require github.com/mergestat/timediff v0.0.3 // indirect
|
||||
|
||||
require (
|
||||
github.com/go-chi/chi v1.5.5
|
||||
github.com/go-chi/chi/v5 v5.0.12 // indirect
|
||||
github.com/gorcon/rcon v1.3.5 // indirect
|
||||
github.com/joho/godotenv v1.5.1 // indirect
|
||||
)
|
|
@ -0,0 +1,12 @@
|
|||
github.com/a-h/templ v0.2.707 h1:T1Gkd2ugbRglZ9rYw/VBchWOSZVKmetDbBkm4YubM7U=
|
||||
github.com/a-h/templ v0.2.707/go.mod h1:5cqsugkq9IerRNucNsI4DEamdHPsoGMQy99DzydLhM8=
|
||||
github.com/go-chi/chi v1.5.5 h1:vOB/HbEMt9QqBqErz07QehcOKHaWFtuj87tTDVz2qXE=
|
||||
github.com/go-chi/chi v1.5.5/go.mod h1:C9JqLr3tIYjDOZpzn+BCuxY8z8vmca43EeMgyZt7irw=
|
||||
github.com/go-chi/chi/v5 v5.0.12 h1:9euLV5sTrTNTRUU9POmDUvfxyj6LAABLUcEWO+JJb4s=
|
||||
github.com/go-chi/chi/v5 v5.0.12/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
|
||||
github.com/gorcon/rcon v1.3.5 h1:YE/Vrw6R99uEP08wp0EjdPAP3Jwz/ys3J8qxI1nYoeU=
|
||||
github.com/gorcon/rcon v1.3.5/go.mod h1:zR1qfKZttF8vAgH1NsP6CdpachOvLDq8jE64NboTpIM=
|
||||
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
||||
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||
github.com/mergestat/timediff v0.0.3 h1:ucCNh4/ZrTPjFZ081PccNbhx9spymCJkFxSzgVuPU+Y=
|
||||
github.com/mergestat/timediff v0.0.3/go.mod h1:yvMUaRu2oetc+9IbPLYBJviz6sA7xz8OXMDfhBl7YSI=
|
|
@ -0,0 +1,51 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"gitea.henriburau.de/haw-lan/cod4watcher/models"
|
||||
"gitea.henriburau.de/haw-lan/cod4watcher/routes"
|
||||
"github.com/go-chi/chi"
|
||||
"github.com/joho/godotenv"
|
||||
)
|
||||
|
||||
func main() {
|
||||
if err := godotenv.Load(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
cod4server, err := models.NewCOD4ServerStatus("80.57.28.137", "28960", 1*time.Second)
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
|
||||
status, err := cod4server.GetServerStatus()
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
|
||||
log.Printf("%+v", status)
|
||||
|
||||
router := chi.NewMux()
|
||||
|
||||
server := &routes.Server{}
|
||||
|
||||
router.Handle("/*", public())
|
||||
router.Get("/health", routes.Make(server.HandleHealth))
|
||||
router.Get("/captures/{captureID}", routes.Make(server.HandleCapture))
|
||||
router.Get("/", routes.Make(server.HandleHome))
|
||||
|
||||
listenAddr := os.Getenv("LISTEN_ADDR")
|
||||
slog.Info("HTTP server started", "listenAddr", listenAddr)
|
||||
http.ListenAndServe(listenAddr, router)
|
||||
}
|
||||
|
||||
func public() http.Handler {
|
||||
fmt.Println("building static files for development")
|
||||
return http.StripPrefix("/assets/", http.FileServerFS(os.DirFS("assets")))
|
||||
}
|
|
@ -0,0 +1,89 @@
|
|||
package models
|
||||
|
||||
import (
|
||||
"cmp"
|
||||
"slices"
|
||||
"time"
|
||||
|
||||
"github.com/mergestat/timediff"
|
||||
)
|
||||
|
||||
type MapScoreList []MapScore
|
||||
|
||||
type Capture struct {
|
||||
Id uint
|
||||
Host string
|
||||
Port string
|
||||
Name string
|
||||
Active bool
|
||||
Start time.Time
|
||||
MapScores MapScoreList
|
||||
}
|
||||
|
||||
type MapScore struct {
|
||||
Id uint
|
||||
StartTime time.Time
|
||||
Map string
|
||||
ScoreList []Score
|
||||
}
|
||||
|
||||
type Score struct {
|
||||
Id uint
|
||||
Name string
|
||||
Score int
|
||||
Ping int
|
||||
}
|
||||
|
||||
type ResultTable struct {
|
||||
Header []ResultTableHeader
|
||||
Rows []ResultTableRow
|
||||
}
|
||||
|
||||
type ResultTableHeader struct {
|
||||
Title string
|
||||
Subtitle string
|
||||
}
|
||||
|
||||
type ResultTableRow struct {
|
||||
Name string
|
||||
Total int
|
||||
Individual []int
|
||||
}
|
||||
|
||||
func (msl MapScoreList) BuildTable() *ResultTable {
|
||||
rt := &ResultTable{
|
||||
Header: []ResultTableHeader{{Title: "Name"}, {Title: "Total"}},
|
||||
}
|
||||
|
||||
userMapRows := make(map[string]*ResultTableRow)
|
||||
|
||||
for mapIndex, mapScore := range msl {
|
||||
rt.Header = append(rt.Header, ResultTableHeader{
|
||||
Title: mapScore.Map,
|
||||
Subtitle: timediff.TimeDiff(mapScore.StartTime),
|
||||
})
|
||||
for _, score := range mapScore.ScoreList {
|
||||
if score.Ping > 0 {
|
||||
if _, ok := userMapRows[score.Name]; !ok {
|
||||
rt.Rows = append(rt.Rows, ResultTableRow{
|
||||
Name: score.Name,
|
||||
Total: 0,
|
||||
Individual: make([]int, len(msl)),
|
||||
})
|
||||
|
||||
userMapRows[score.Name] = &rt.Rows[len(rt.Rows)-1]
|
||||
}
|
||||
|
||||
row := userMapRows[score.Name]
|
||||
row.Total = row.Total + score.Score
|
||||
row.Individual[mapIndex] = score.Score
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
slices.SortFunc(rt.Rows, func(a, b ResultTableRow) int {
|
||||
return cmp.Compare(b.Total, a.Total)
|
||||
})
|
||||
|
||||
return rt
|
||||
}
|
|
@ -0,0 +1,167 @@
|
|||
package models
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
var timeLayout = "Mon Jun 2 15:04:05 2006"
|
||||
|
||||
type CoD4Server struct {
|
||||
server string
|
||||
port string
|
||||
protocol string
|
||||
timeout time.Duration
|
||||
}
|
||||
|
||||
type CoD4ServerStatus struct {
|
||||
raw string
|
||||
serverData map[string]string
|
||||
MapStartTime time.Time
|
||||
MapName string
|
||||
Score []Score
|
||||
meta map[string]interface{}
|
||||
}
|
||||
|
||||
func NewCOD4ServerStatus(server, port string, timeout time.Duration) (*CoD4Server, error) {
|
||||
if server == "" || port == "" {
|
||||
return nil, errors.New("server or port is empty")
|
||||
}
|
||||
|
||||
return &CoD4Server{
|
||||
server: server,
|
||||
port: port,
|
||||
protocol: "udp",
|
||||
timeout: timeout,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *CoD4Server) GetServerStatus() (*CoD4ServerStatus, error) {
|
||||
data, err := c.receiveData()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return parseServerData(data)
|
||||
}
|
||||
|
||||
func (c *CoD4Server) receiveData() (string, error) {
|
||||
address := fmt.Sprintf("%s:%s", c.server, c.port)
|
||||
conn, err := net.DialTimeout(c.protocol, address, c.timeout)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("could not connect to server: %s", err)
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
conn.SetDeadline(time.Now().Add(c.timeout))
|
||||
_, err = conn.Write([]byte("\xFF\xFF\xFF\xFFgetstatus\x00"))
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to send data to server: %s", err)
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
response := make([]byte, 8192)
|
||||
reader := bufio.NewReader(conn)
|
||||
for {
|
||||
n, err := reader.Read(response)
|
||||
if err != nil {
|
||||
break
|
||||
}
|
||||
buf.Write(response[:n])
|
||||
}
|
||||
|
||||
data := buf.String()
|
||||
if len(strings.TrimSpace(data)) == 0 {
|
||||
return "", fmt.Errorf("no data received from server: %s", err)
|
||||
}
|
||||
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func parseServerData(data string) (*CoD4ServerStatus, error) {
|
||||
lines := strings.Split(data, "\n")
|
||||
|
||||
if len(lines) < 2 {
|
||||
return nil, fmt.Errorf("insufficient data received")
|
||||
}
|
||||
|
||||
c := &CoD4ServerStatus{
|
||||
raw: data,
|
||||
serverData: make(map[string]string),
|
||||
meta: make(map[string]interface{}),
|
||||
}
|
||||
|
||||
tempPlayers := lines[2:]
|
||||
tempData := strings.Split(lines[1], "\\")
|
||||
|
||||
for i := 1; i < len(tempData)-1; i += 2 {
|
||||
c.serverData[tempData[i]] = tempData[i+1]
|
||||
}
|
||||
|
||||
c.serverData["sv_hostname"] = colorCode(c.serverData["sv_hostname"])
|
||||
c.serverData["_Maps"] = strings.Join(strings.Split(c.serverData["_Maps"], "-"), ",")
|
||||
c.MapName = c.serverData["mapname"]
|
||||
|
||||
startTime, err := time.Parse(timeLayout, c.serverData["g_mapStartTime"])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c.MapStartTime = startTime
|
||||
|
||||
for _, playerLine := range tempPlayers {
|
||||
if len(strings.TrimSpace(playerLine)) > 1 {
|
||||
temp := strings.Fields(playerLine)
|
||||
if len(temp) >= 3 {
|
||||
playerName := strings.Trim(playerLine, `"`)
|
||||
scoreValue, err := strconv.Atoi(temp[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pingValue, err := strconv.Atoi(temp[1])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c.Score = append(c.Score, Score{
|
||||
Name: playerName[strings.Index(playerName, "\"")+1:],
|
||||
Score: scoreValue,
|
||||
Ping: pingValue,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func colorCode(str string) string {
|
||||
str += "^"
|
||||
|
||||
colorMap := map[string]string{
|
||||
"0": "#000000",
|
||||
"1": "#F65A5A",
|
||||
"2": "#00F100",
|
||||
"3": "#EFEE04",
|
||||
"4": "#0F04E8",
|
||||
"5": "#04E8E7",
|
||||
"6": "#F75AF6",
|
||||
"7": "#FFFFFF",
|
||||
"8": "#7E7E7E",
|
||||
"9": "#6E3C3C",
|
||||
}
|
||||
|
||||
re := regexp.MustCompile(`\^(\d)(.*?)\^`)
|
||||
return re.ReplaceAllStringFunc(str, func(m string) string {
|
||||
matches := re.FindStringSubmatch(m)
|
||||
return fmt.Sprintf(`<span style="color:%s;">%s</span>^`, colorMap[matches[1]], matches[2])
|
||||
})
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
package models
|
||||
|
||||
type Persistence interface {
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
package routes
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"gitea.henriburau.de/haw-lan/cod4watcher/views/capture"
|
||||
"github.com/go-chi/chi"
|
||||
)
|
||||
|
||||
func (s *Server) HandleCapture(w http.ResponseWriter, r *http.Request) error {
|
||||
captureString := chi.URLParam(r, "captureID")
|
||||
captureID, err := strconv.Atoi(captureString)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
foundCapture, err := s.cs.GetCaptureById(captureID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return Render(w, r, capture.Capture(foundCapture))
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
package routes
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
func (s *Server) HandleHealth(w http.ResponseWriter, r *http.Request) error {
|
||||
fmt.Fprint(w, "Up and running!")
|
||||
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
package routes
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"gitea.henriburau.de/haw-lan/cod4watcher/views/home"
|
||||
)
|
||||
|
||||
func (s *Server) HandleHome(w http.ResponseWriter, r *http.Request) error {
|
||||
captureList, err := s.cs.GetActiveCapures()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return Render(w, r, home.Index(captureList))
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
package routes
|
||||
|
||||
import (
|
||||
"log/slog"
|
||||
"net/http"
|
||||
|
||||
"gitea.henriburau.de/haw-lan/cod4watcher/services"
|
||||
"github.com/a-h/templ"
|
||||
)
|
||||
|
||||
type HTTPHandler func(w http.ResponseWriter, r *http.Request) error
|
||||
|
||||
func Make(h HTTPHandler) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
if err := h(w, r); err != nil {
|
||||
slog.Error("HTTP handler error", "err", err, "path", r.URL.Path)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type Server struct {
|
||||
cs *services.CaptureService
|
||||
}
|
||||
|
||||
func Render(w http.ResponseWriter, r *http.Request, c templ.Component) error {
|
||||
return c.Render(r.Context(), w)
|
||||
}
|
|
@ -0,0 +1,145 @@
|
|||
# Created by https://www.toptal.com/developers/gitignore/api/node
|
||||
# Edit at https://www.toptal.com/developers/gitignore?templates=node
|
||||
|
||||
### Node ###
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
.pnpm-debug.log*
|
||||
|
||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
*.lcov
|
||||
|
||||
# nyc test coverage
|
||||
.nyc_output
|
||||
|
||||
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||
.grunt
|
||||
|
||||
# Bower dependency directory (https://bower.io/)
|
||||
bower_components
|
||||
|
||||
# node-waf configuration
|
||||
.lock-wscript
|
||||
|
||||
# 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
|
||||
|
||||
# Optional stylelint cache
|
||||
.stylelintcache
|
||||
|
||||
# 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 variable files
|
||||
.env
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
.env.local
|
||||
|
||||
# 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
|
||||
|
||||
# vuepress v2.x temp and cache directory
|
||||
.temp
|
||||
|
||||
# Docusaurus cache and generated files
|
||||
.docusaurus
|
||||
|
||||
# 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/
|
||||
|
||||
# Optional stylelint cache
|
||||
|
||||
# SvelteKit build / generate output
|
||||
.svelte-kit
|
||||
|
||||
# End of https://www.toptal.com/developers/gitignore/api/node
|
||||
n
|
|
@ -0,0 +1 @@
|
|||
import 'htmx.org'
|
|
@ -0,0 +1,21 @@
|
|||
{
|
||||
"name": "cod4observer",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "cod4observer",
|
||||
"version": "1.0.0",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"htmx.org": "^1.9.12"
|
||||
}
|
||||
},
|
||||
"node_modules/htmx.org": {
|
||||
"version": "1.9.12",
|
||||
"resolved": "https://registry.npmjs.org/htmx.org/-/htmx.org-1.9.12.tgz",
|
||||
"integrity": "sha512-VZAohXyF7xPGS52IM8d1T1283y+X4D+Owf3qY1NZ9RuBypyu9l8cGsxUMAG5fEAb/DhT7rDoJ9Hpu5/HxFD3cw=="
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
"name": "cod4observer",
|
||||
"version": "1.0.0",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"description": "",
|
||||
"dependencies": {
|
||||
"htmx.org": "^1.9.12"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
package services
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"gitea.henriburau.de/haw-lan/cod4watcher/models"
|
||||
)
|
||||
|
||||
type CaptureService struct {
|
||||
}
|
||||
|
||||
var captures = []models.Capture{
|
||||
{Id: 1, Host: "80.57.28.137", Port: "28960", Active: true, Name: "Gungame HAW-LAN 11", Start: time.Now().Add(-1 * time.Hour)},
|
||||
{Id: 1, Host: "80.57.28.137", Port: "28960", Active: true, Name: "Gungame HAW-LAN 12", Start: time.Now().Add(-5 * time.Minute)},
|
||||
}
|
||||
|
||||
func (cs *CaptureService) GetActiveCapures() ([]models.Capture, error) {
|
||||
|
||||
return captures, nil
|
||||
}
|
||||
|
||||
func (cs *CaptureService) GetCaptureById(id int) (*models.Capture, error) {
|
||||
capture := captures[0]
|
||||
server, err := models.NewCOD4ServerStatus(capture.Host, capture.Port, time.Second)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
status, err := server.GetServerStatus()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
capture.MapScores = []models.MapScore{
|
||||
{
|
||||
Id: 0,
|
||||
StartTime: status.MapStartTime,
|
||||
Map: status.MapName,
|
||||
ScoreList: status.Score,
|
||||
},
|
||||
}
|
||||
|
||||
return &capture, nil
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
|
@ -0,0 +1,9 @@
|
|||
/** @type {import('tailwindcss').Config} */
|
||||
module.exports = {
|
||||
content: ["./**/*.html", "./**/*.templ", "./**/*.go"],
|
||||
theme: {
|
||||
extend: {},
|
||||
},
|
||||
plugins: [],
|
||||
}
|
||||
|
Binary file not shown.
|
@ -0,0 +1 @@
|
|||
exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1
|
|
@ -0,0 +1,19 @@
|
|||
package capture
|
||||
|
||||
import "gitea.henriburau.de/haw-lan/cod4watcher/models"
|
||||
import "gitea.henriburau.de/haw-lan/cod4watcher/views/layouts"
|
||||
import "gitea.henriburau.de/haw-lan/cod4watcher/views/components"
|
||||
|
||||
templ Capture(capture *models.Capture) {
|
||||
@layouts.Base() {
|
||||
<div class="flex flex-col">
|
||||
<div class="block font-bold text-lg">
|
||||
{ capture.Name }
|
||||
</div>
|
||||
<div class="block">
|
||||
{ capture.Host }:{ capture.Port }
|
||||
</div>
|
||||
@components.CaptureTable(*capture.MapScores.BuildTable())
|
||||
</div>
|
||||
}
|
||||
}
|
|
@ -0,0 +1,101 @@
|
|||
// Code generated by templ - DO NOT EDIT.
|
||||
|
||||
// templ: version: v0.2.707
|
||||
package capture
|
||||
|
||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||
|
||||
import "github.com/a-h/templ"
|
||||
import "context"
|
||||
import "io"
|
||||
import "bytes"
|
||||
|
||||
import "gitea.henriburau.de/haw-lan/cod4watcher/models"
|
||||
import "gitea.henriburau.de/haw-lan/cod4watcher/views/layouts"
|
||||
import "gitea.henriburau.de/haw-lan/cod4watcher/views/components"
|
||||
|
||||
func Capture(capture *models.Capture) templ.Component {
|
||||
return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
templ_7745c5c3_Buffer = templ.GetBuffer()
|
||||
defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var1 == nil {
|
||||
templ_7745c5c3_Var1 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
templ_7745c5c3_Var2 := templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
templ_7745c5c3_Buffer = templ.GetBuffer()
|
||||
defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
}
|
||||
templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 1)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var3 string
|
||||
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(capture.Name)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/capture/capture.templ`, Line: 11, Col: 18}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 2)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var4 string
|
||||
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(capture.Host)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/capture/capture.templ`, Line: 14, Col: 18}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 3)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var5 string
|
||||
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(capture.Port)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/capture/capture.templ`, Line: 14, Col: 35}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 4)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = components.CaptureTable(*capture.MapScores.BuildTable()).Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 5)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
_, templ_7745c5c3_Err = io.Copy(templ_7745c5c3_W, templ_7745c5c3_Buffer)
|
||||
}
|
||||
return templ_7745c5c3_Err
|
||||
})
|
||||
templ_7745c5c3_Err = layouts.Base().Render(templ.WithChildren(ctx, templ_7745c5c3_Var2), templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
|
||||
}
|
||||
return templ_7745c5c3_Err
|
||||
})
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
<div class=\"flex flex-col\"><div class=\"block font-bold text-lg\">
|
||||
</div><div class=\"block\">
|
||||
:
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,12 @@
|
|||
package components
|
||||
|
||||
import "gitea.henriburau.de/haw-lan/cod4watcher/models"
|
||||
import "github.com/mergestat/timediff"
|
||||
import "fmt"
|
||||
|
||||
templ CaptureCard(capture models.Capture) {
|
||||
<a href={ templ.URL(fmt.Sprintf("/captures/%d", capture.Id)) } class="block max-w-sm p-6 bg-white border border-gray-200 rounded-lg shadow hover:bg-gray-100 dark:bg-gray-800 dark:border-gray-700 dark:hover:bg-gray-700">
|
||||
<h5 class="mb-2 text-2xl font-bold tracking-tight text-gray-900 dark:text-white">{ capture.Name }</h5>
|
||||
<p class="font-normal text-gray-700 dark:text-gray-400">{ timediff.TimeDiff(capture.Start) }</p>
|
||||
</a>
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
// Code generated by templ - DO NOT EDIT.
|
||||
|
||||
// templ: version: v0.2.707
|
||||
package components
|
||||
|
||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||
|
||||
import "github.com/a-h/templ"
|
||||
import "context"
|
||||
import "io"
|
||||
import "bytes"
|
||||
|
||||
import "gitea.henriburau.de/haw-lan/cod4watcher/models"
|
||||
import "github.com/mergestat/timediff"
|
||||
import "fmt"
|
||||
|
||||
func CaptureCard(capture models.Capture) templ.Component {
|
||||
return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
templ_7745c5c3_Buffer = templ.GetBuffer()
|
||||
defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var1 == nil {
|
||||
templ_7745c5c3_Var1 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 1)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var2 templ.SafeURL = templ.URL(fmt.Sprintf("/captures/%d", capture.Id))
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(string(templ_7745c5c3_Var2)))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 2)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var3 string
|
||||
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(capture.Name)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/components/capture_card.templ`, Line: 9, Col: 97}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 3)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var4 string
|
||||
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(timediff.TimeDiff(capture.Start))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/components/capture_card.templ`, Line: 10, Col: 92}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 4)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
|
||||
}
|
||||
return templ_7745c5c3_Err
|
||||
})
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
<a href=\"
|
||||
\" class=\"block max-w-sm p-6 bg-white border border-gray-200 rounded-lg shadow hover:bg-gray-100 dark:bg-gray-800 dark:border-gray-700 dark:hover:bg-gray-700\"><h5 class=\"mb-2 text-2xl font-bold tracking-tight text-gray-900 dark:text-white\">
|
||||
</h5><p class=\"font-normal text-gray-700 dark:text-gray-400\">
|
||||
</p></a>
|
|
@ -0,0 +1,42 @@
|
|||
package components
|
||||
|
||||
import "gitea.henriburau.de/haw-lan/cod4watcher/models"
|
||||
import "strconv"
|
||||
|
||||
templ CaptureTable(table models.ResultTable) {
|
||||
<div class="relative overflow-x-auto">
|
||||
<table class="w-full text-sm text-left rtl:text-right text-gray-500 dark:text-gray-400">
|
||||
<thead class="text-xs text-gray-700 uppercase bg-gray-50 dark:bg-gray-700 dark:text-gray-400">
|
||||
<tr>
|
||||
for _, header := range table.Header {
|
||||
<th scope="col" class="px-6 py-3">
|
||||
<div>
|
||||
{ header.Title }
|
||||
</div>
|
||||
<div class="lowercase font-light">
|
||||
{ header.Subtitle }
|
||||
</div>
|
||||
</th>
|
||||
}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
for _, row := range table.Rows {
|
||||
<tr class="bg-white border-b dark:bg-gray-800 dark:border-gray-700">
|
||||
<th scope="row" class="px-6 py-4 font-medium text-gray-900 whitespace-nowrap dark:text-white">
|
||||
{ row.Name }
|
||||
</th>
|
||||
<td class="px-6 py-4">
|
||||
{ strconv.Itoa(row.Total) }
|
||||
</td>
|
||||
for _, score := range row.Individual {
|
||||
<td class="px-6 py-4">
|
||||
{ strconv.Itoa(score) }
|
||||
</td>
|
||||
}
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
}
|
|
@ -0,0 +1,133 @@
|
|||
// Code generated by templ - DO NOT EDIT.
|
||||
|
||||
// templ: version: v0.2.707
|
||||
package components
|
||||
|
||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||
|
||||
import "github.com/a-h/templ"
|
||||
import "context"
|
||||
import "io"
|
||||
import "bytes"
|
||||
|
||||
import "gitea.henriburau.de/haw-lan/cod4watcher/models"
|
||||
import "strconv"
|
||||
|
||||
func CaptureTable(table models.ResultTable) templ.Component {
|
||||
return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
templ_7745c5c3_Buffer = templ.GetBuffer()
|
||||
defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var1 == nil {
|
||||
templ_7745c5c3_Var1 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 1)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
for _, header := range table.Header {
|
||||
templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 2)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var2 string
|
||||
templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(header.Title)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/components/capture_table.templ`, Line: 14, Col: 22}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 3)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var3 string
|
||||
templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(header.Subtitle)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/components/capture_table.templ`, Line: 17, Col: 25}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 4)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 5)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
for _, row := range table.Rows {
|
||||
templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 6)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var4 string
|
||||
templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(row.Name)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/components/capture_table.templ`, Line: 27, Col: 17}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 7)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var5 string
|
||||
templ_7745c5c3_Var5, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.Itoa(row.Total))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/components/capture_table.templ`, Line: 30, Col: 32}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 8)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
for _, score := range row.Individual {
|
||||
templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 9)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
var templ_7745c5c3_Var6 string
|
||||
templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(strconv.Itoa(score))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ.Error{Err: templ_7745c5c3_Err, FileName: `views/components/capture_table.templ`, Line: 34, Col: 29}
|
||||
}
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6))
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 10)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 11)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 12)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
|
||||
}
|
||||
return templ_7745c5c3_Err
|
||||
})
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
<div class=\"relative overflow-x-auto\"><table class=\"w-full text-sm text-left rtl:text-right text-gray-500 dark:text-gray-400\"><thead class=\"text-xs text-gray-700 uppercase bg-gray-50 dark:bg-gray-700 dark:text-gray-400\"><tr>
|
||||
<th scope=\"col\" class=\"px-6 py-3\"><div>
|
||||
</div><div class=\"lowercase font-light\">
|
||||
</div></th>
|
||||
</tr></thead> <tbody>
|
||||
<tr class=\"bg-white border-b dark:bg-gray-800 dark:border-gray-700\"><th scope=\"row\" class=\"px-6 py-4 font-medium text-gray-900 whitespace-nowrap dark:text-white\">
|
||||
</th><td class=\"px-6 py-4\">
|
||||
</td>
|
||||
<td class=\"px-6 py-4\">
|
||||
</td>
|
||||
</tr>
|
||||
</tbody></table></div>
|
|
@ -0,0 +1,24 @@
|
|||
package components
|
||||
|
||||
templ Nagivation() {
|
||||
<nav class="bg-gray-800">
|
||||
<div class="mx-auto max-w-7xl">
|
||||
<div class="relative flex h-16 items-center justify-between">
|
||||
<div class="flex flex-1 items-center justify-center sm:items-stretch sm:justify-start">
|
||||
<div class="flex flex-shrink-0 items-center">
|
||||
<span class="text-white pr-2 font-bold">Turnier-Tracker</span>
|
||||
<img class="h-8 w-auto" src="/assets/logo.svg" alt="Your Company"/>
|
||||
</div>
|
||||
<div class="hidden sm:ml-6 sm:block">
|
||||
<div class="flex space-x-4">
|
||||
<!-- Current: "bg-gray-900 text-white", Default: "text-gray-300 hover:bg-gray-700 hover:text-white" -->
|
||||
<a href="/" class="bg-gray-900 text-white rounded-md px-3 py-2 text-sm font-medium" aria-current="page">Dashboard</a>
|
||||
<a href="/about" class="text-gray-300 hover:bg-gray-700 hover:text-white rounded-md px-3 py-2 text-sm font-medium">About</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="absolute inset-y-0 right-0 flex items-center pr-2 sm:static sm:inset-auto sm:ml-6 sm:pr-0"></div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
// Code generated by templ - DO NOT EDIT.
|
||||
|
||||
// templ: version: v0.2.707
|
||||
package components
|
||||
|
||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||
|
||||
import "github.com/a-h/templ"
|
||||
import "context"
|
||||
import "io"
|
||||
import "bytes"
|
||||
|
||||
func Nagivation() templ.Component {
|
||||
return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
templ_7745c5c3_Buffer = templ.GetBuffer()
|
||||
defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var1 == nil {
|
||||
templ_7745c5c3_Var1 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 1)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
|
||||
}
|
||||
return templ_7745c5c3_Err
|
||||
})
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
<nav class=\"bg-gray-800\"><div class=\"mx-auto max-w-7xl\"><div class=\"relative flex h-16 items-center justify-between\"><div class=\"flex flex-1 items-center justify-center sm:items-stretch sm:justify-start\"><div class=\"flex flex-shrink-0 items-center\"><span class=\"text-white pr-2 font-bold\">Turnier-Tracker</span> <img class=\"h-8 w-auto\" src=\"/assets/logo.svg\" alt=\"Your Company\"></div><div class=\"hidden sm:ml-6 sm:block\"><div class=\"flex space-x-4\"><!-- Current: \"bg-gray-900 text-white\", Default: \"text-gray-300 hover:bg-gray-700 hover:text-white\" --><a href=\"/\" class=\"bg-gray-900 text-white rounded-md px-3 py-2 text-sm font-medium\" aria-current=\"page\">Dashboard</a> <a href=\"/about\" class=\"text-gray-300 hover:bg-gray-700 hover:text-white rounded-md px-3 py-2 text-sm font-medium\">About</a></div></div></div><div class=\"absolute inset-y-0 right-0 flex items-center pr-2 sm:static sm:inset-auto sm:ml-6 sm:pr-0\"></div></div></div></nav>
|
|
@ -0,0 +1,13 @@
|
|||
package home
|
||||
|
||||
import "gitea.henriburau.de/haw-lan/cod4watcher/views/layouts"
|
||||
import "gitea.henriburau.de/haw-lan/cod4watcher/models"
|
||||
import "gitea.henriburau.de/haw-lan/cod4watcher/views/components"
|
||||
|
||||
templ Index(captures []models.Capture) {
|
||||
@layouts.Base() {
|
||||
for _, capture := range captures {
|
||||
@components.CaptureCard(capture)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
// Code generated by templ - DO NOT EDIT.
|
||||
|
||||
// templ: version: v0.2.707
|
||||
package home
|
||||
|
||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||
|
||||
import "github.com/a-h/templ"
|
||||
import "context"
|
||||
import "io"
|
||||
import "bytes"
|
||||
|
||||
import "gitea.henriburau.de/haw-lan/cod4watcher/views/layouts"
|
||||
import "gitea.henriburau.de/haw-lan/cod4watcher/models"
|
||||
import "gitea.henriburau.de/haw-lan/cod4watcher/views/components"
|
||||
|
||||
func Index(captures []models.Capture) templ.Component {
|
||||
return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
templ_7745c5c3_Buffer = templ.GetBuffer()
|
||||
defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var1 == nil {
|
||||
templ_7745c5c3_Var1 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
templ_7745c5c3_Var2 := templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
templ_7745c5c3_Buffer = templ.GetBuffer()
|
||||
defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
}
|
||||
for _, capture := range captures {
|
||||
templ_7745c5c3_Err = components.CaptureCard(capture).Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
}
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
_, templ_7745c5c3_Err = io.Copy(templ_7745c5c3_W, templ_7745c5c3_Buffer)
|
||||
}
|
||||
return templ_7745c5c3_Err
|
||||
})
|
||||
templ_7745c5c3_Err = layouts.Base().Render(templ.WithChildren(ctx, templ_7745c5c3_Var2), templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
|
||||
}
|
||||
return templ_7745c5c3_Err
|
||||
})
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
package layouts
|
||||
|
||||
import "gitea.henriburau.de/haw-lan/cod4watcher/views/components"
|
||||
|
||||
templ Base() {
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>CoD 4 Turnier-Tracker</title>
|
||||
<link rel="icon" type="image/x-icon" href="/assets/favicon.ico"/>
|
||||
<meta charset="UTF-8"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||
<link rel="stylesheet" href="/assets/styles.css"/>
|
||||
<script src="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/js/all.min.js"></script>
|
||||
</head>
|
||||
<body class="antialiased">
|
||||
@components.Nagivation()
|
||||
<div class="mx-auto max-w-7xl mt-10 flex gap-3">
|
||||
{ children... }
|
||||
</div>
|
||||
<script src="/assets/index.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
// Code generated by templ - DO NOT EDIT.
|
||||
|
||||
// templ: version: v0.2.707
|
||||
package layouts
|
||||
|
||||
//lint:file-ignore SA4006 This context is only used if a nested component is present.
|
||||
|
||||
import "github.com/a-h/templ"
|
||||
import "context"
|
||||
import "io"
|
||||
import "bytes"
|
||||
|
||||
import "gitea.henriburau.de/haw-lan/cod4watcher/views/components"
|
||||
|
||||
func Base() templ.Component {
|
||||
return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) {
|
||||
templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer)
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
templ_7745c5c3_Buffer = templ.GetBuffer()
|
||||
defer templ.ReleaseBuffer(templ_7745c5c3_Buffer)
|
||||
}
|
||||
ctx = templ.InitializeContext(ctx)
|
||||
templ_7745c5c3_Var1 := templ.GetChildren(ctx)
|
||||
if templ_7745c5c3_Var1 == nil {
|
||||
templ_7745c5c3_Var1 = templ.NopComponent
|
||||
}
|
||||
ctx = templ.ClearChildren(ctx)
|
||||
templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 1)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = components.Nagivation().Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 2)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templ_7745c5c3_Var1.Render(ctx, templ_7745c5c3_Buffer)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
templ_7745c5c3_Err = templ.WriteWatchModeString(templ_7745c5c3_Buffer, 3)
|
||||
if templ_7745c5c3_Err != nil {
|
||||
return templ_7745c5c3_Err
|
||||
}
|
||||
if !templ_7745c5c3_IsBuffer {
|
||||
_, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteTo(templ_7745c5c3_W)
|
||||
}
|
||||
return templ_7745c5c3_Err
|
||||
})
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
<!doctype html><html lang=\"en\"><head><title>CoD 4 Turnier-Tracker</title><link rel=\"icon\" type=\"image/x-icon\" href=\"/assets/favicon.ico\"><meta charset=\"UTF-8\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"><link rel=\"stylesheet\" href=\"/assets/styles.css\"><script src=\"https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/js/all.min.js\"></script></head><body class=\"antialiased\">
|
||||
<div class=\"mx-auto max-w-7xl mt-10 flex gap-3\">
|
||||
</div><script src=\"/assets/index.js\"></script></body></html>
|
Loading…
Reference in New Issue