Initial commit

This commit is contained in:
MaxJa4
2024-01-14 22:06:53 +01:00
commit fe92068ad3
30 changed files with 1067 additions and 0 deletions

8
.idea/.gitignore generated vendored Normal file
View File

@@ -0,0 +1,8 @@
# Default ignored files
/shelf/
/workspace.xml
# Editor-based HTTP Client requests
/httpRequests/
# Datasource local storage ignored files
/dataSources/
/dataSources.local.xml

11
.idea/InfrantrySkillCalculator.iml generated Normal file
View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="Go" enabled="true" />
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="bootstrap" level="application" />
<orderEntry type="library" name="bootstrap-icons" level="application" />
</component>
</module>

View File

@@ -0,0 +1,16 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Project Default" />
<inspection_tool class="HtmlUnknownAttribute" enabled="true" level="WARNING" enabled_by_default="true">
<option name="myValues">
<value>
<list size="2">
<item index="0" class="java.lang.String" itemvalue="hx-get" />
<item index="1" class="java.lang.String" itemvalue="hx-target" />
</list>
</value>
</option>
<option name="myCustomValuesEnabled" value="true" />
</inspection_tool>
</profile>
</component>

6
.idea/jsLibraryMappings.xml generated Normal file
View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="JavaScriptLibraryMappings">
<file url="PROJECT" libraries="{bootstrap, bootstrap-icons}" />
</component>
</project>

8
.idea/modules.xml generated Normal file
View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/InfrantrySkillCalculator.iml" filepath="$PROJECT_DIR$/.idea/InfrantrySkillCalculator.iml" />
</modules>
</component>
</project>

6
.idea/vcs.xml generated Normal file
View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

View File

@@ -0,0 +1,149 @@
package controllers
import (
"InfrantrySkillCalculator/models"
"InfrantrySkillCalculator/utils"
"fmt"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
"log"
"net/http"
"strconv"
)
type AddClanInput struct {
Name string `json:"name" binding:"required"`
Tag string `json:"tag" binding:"required"`
KeepUpdated bool `json:"keep_updated" gorm:"default:false"`
}
type UpdateClanInput struct {
Name string `json:"name" binding:"required"`
Tag string `json:"tag" binding:"required"`
KeepUpdated bool `json:"keep_updated" gorm:"default:false"`
}
// GetAllClans GET /clan
func GetAllClans(c *gin.Context) {
var clans []models.Clan
models.DB.Find(&clans)
c.JSON(http.StatusOK, clans)
}
// AddClan POST /clan
func AddClan(c *gin.Context) {
var input AddClanInput
if err := c.BindJSON(&input); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
var clan models.Clan
if err := FindClanByName(&clan, input.Name).Error; err == nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Clan with this name already exists!"})
return
} else if err := FindClanByTag(&clan, input.Tag).Error; err == nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Clan with this tag already exists!"})
return
}
clan = models.Clan{Name: input.Name, Tag: input.Tag, KeepUpdated: input.KeepUpdated}
models.DB.Create(&clan)
//UpdateClanTimestamp()
c.JSON(http.StatusOK, clan)
_, err := fmt.Fprintf(utils.GinWriter, "Added clan '"+clan.Name+"' with tag '"+clan.Tag+"'\n")
if err != nil {
log.Fatal(err)
}
}
// GetClanByID GET /clan/:id
func GetClanByID(c *gin.Context) {
var clan models.Clan
if err := FindClanByID(&clan, c).Error; err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Record not found!"})
return
}
c.JSON(http.StatusOK, clan)
}
// UpdateClanByID PATCH /clan/:id
func UpdateClanByID(c *gin.Context) {
var clan models.Clan
if err := FindClanByID(&clan, c).Error; err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Record not found!"})
return
}
var input UpdateClanInput
if err := c.ShouldBindJSON(&input); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
msg := "Updating clan '" + clan.Name + "'#" + strconv.FormatUint(uint64(clan.ID), 10)
if clan.Name != input.Name {
msg += " (new: '" + input.Name + "')"
}
msg += " with tag '" + clan.Tag + "'"
if clan.Tag != input.Tag {
msg += " (new: '" + input.Tag + "')"
}
msg += " with updating '" + strconv.FormatBool(clan.KeepUpdated) + "'"
if clan.KeepUpdated != input.KeepUpdated {
msg += " (new: '" + strconv.FormatBool(input.KeepUpdated) + "')"
}
_, err := fmt.Fprintf(utils.GinWriter, msg+"\n")
if err != nil {
log.Fatal(err)
}
models.DB.Model(&clan).Updates(map[string]interface{}{
"Name": input.Name,
"Tag": input.Tag,
"KeepUpdated": input.KeepUpdated,
})
//UpdateClanTimestamp()
c.JSON(http.StatusOK, clan)
}
// DeleteClanByID DELETE /clan/:id
func DeleteClanByID(c *gin.Context) {
var clan models.Clan
if err := FindClanByID(&clan, c).Error; err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Record not found!"})
return
}
models.DB.Delete(&clan)
//UpdateClanTimestamp()
c.JSON(http.StatusOK, true)
_, err := fmt.Fprintf(utils.GinWriter, "Deleted clan '"+clan.Name+"' with tag '"+clan.Tag+"'\n")
if err != nil {
log.Fatal(err)
}
}
func FindClanByName(out interface{}, name string) *gorm.DB {
return models.DB.Where("name = ?", name).First(out)
}
func FindClanByTag(out interface{}, tag string) *gorm.DB {
return models.DB.Where("tag = ?", tag).First(out)
}
func FindClanByID(out interface{}, c *gin.Context) *gorm.DB {
return models.DB.Where("id = ?", c.Param("id")).First(out)
}

View File

@@ -0,0 +1,15 @@
package controllers
import (
"InfrantrySkillCalculator/models"
"github.com/gin-gonic/gin"
"net/http"
)
// GetAllPlayers GET /player
func GetAllPlayers(c *gin.Context) {
var players []models.Player
models.DB.Find(&players)
c.JSON(http.StatusOK, players)
}

39
go.mod Normal file
View File

@@ -0,0 +1,39 @@
module InfrantrySkillCalculator
go 1.21
require (
github.com/gin-gonic/gin v1.9.1
gorm.io/driver/sqlite v1.5.4
gorm.io/gorm v1.25.5
)
require (
github.com/bytedance/sonic v1.9.1 // indirect
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.14.0 // indirect
github.com/goccy/go-json v0.10.2 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/cpuid/v2 v2.2.4 // indirect
github.com/leodido/go-urn v1.2.4 // indirect
github.com/mattn/go-isatty v0.0.19 // indirect
github.com/mattn/go-sqlite3 v1.14.17 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pelletier/go-toml/v2 v2.0.8 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.11 // indirect
golang.org/x/arch v0.3.0 // indirect
golang.org/x/crypto v0.9.0 // indirect
golang.org/x/net v0.10.0 // indirect
golang.org/x/sys v0.8.0 // indirect
golang.org/x/text v0.9.0 // indirect
google.golang.org/protobuf v1.30.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

96
go.sum Normal file
View File

@@ -0,0 +1,96 @@
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s=
github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U=
github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams=
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU=
github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA=
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg=
github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.14.0 h1:vgvQWe3XCz3gIeFDm/HnTIbj6UGmg/+t63MyGU2n5js=
github.com/go-playground/validator/v10 v10.14.0/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk=
github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q=
github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-sqlite3 v1.14.17 h1:mCRHCLDUBXgpKAqIKsaAaAsrAlbkeomtRFKXh2L6YIM=
github.com/mattn/go-sqlite3 v1.14.17/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ=
github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY=
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU=
github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
golang.org/x/arch v0.3.0 h1:02VY4/ZcO/gBOH6PUaoiptASxtXU10jazRCP865E97k=
golang.org/x/arch v0.3.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g=
golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0=
golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gorm.io/driver/sqlite v1.5.4 h1:IqXwXi8M/ZlPzH/947tn5uik3aYQslP9BVveoax0nV0=
gorm.io/driver/sqlite v1.5.4/go.mod h1:qxAuCol+2r6PannQDpOP1FP6ag3mKi4esLnB/jHed+4=
gorm.io/gorm v1.25.5 h1:zR9lOiiYf09VNh5Q1gphfyia1JpiClIWG9hQaxB/mls=
gorm.io/gorm v1.25.5/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=

BIN
isc_data.db Normal file

Binary file not shown.

54
isc_rest.log Normal file
View File

@@ -0,0 +1,54 @@
[GIN] 2024/01/14 - 21:02:24 | 200 | 2.0629ms | 127.0.0.1 | GET "/"
[GIN] 2024/01/14 - 21:02:25 | 200 | 140µs | 127.0.0.1 | GET "/players"
[GIN] 2024/01/14 - 21:02:25 | 200 | 315.5µs | 127.0.0.1 | GET "/clan/1"
Updating clan 'asdf'#1 (new: 'asdf1111') with tag 'fdsa' with updating 'true'
[GIN] 2024/01/14 - 21:02:29 | 200 | 2.4671ms | 127.0.0.1 | PATCH "/clan/1"
[GIN] 2024/01/14 - 21:02:33 | 200 | 156.6µs | 127.0.0.1 | GET "/"
[GIN] 2024/01/14 - 21:02:34 | 200 | 784.3µs | 127.0.0.1 | GET "/players"
[GIN] 2024/01/14 - 21:08:36 | 200 | 517.1µs | 127.0.0.1 | GET "/"
[GIN] 2024/01/14 - 21:08:39 | 200 | 179.4µs | 127.0.0.1 | GET "/players"
[GIN] 2024/01/14 - 21:08:40 | 200 | 272.3µs | 127.0.0.1 | GET "/players"
Deleted clan 'fasdfs' with tag 'dfsadfasdf'
[GIN] 2024/01/14 - 21:08:48 | 200 | 2.7076ms | 127.0.0.1 | DELETE "/clan/2"
[GIN] 2024/01/14 - 21:08:48 | 200 | 128.1µs | 127.0.0.1 | GET "/players"
[GIN] 2024/01/14 - 21:09:07 | 200 | 887.1µs | 127.0.0.1 | GET "/"
[GIN] 2024/01/14 - 21:09:09 | 200 | 0s | 127.0.0.1 | GET "/players"
Deleted clan 'sdfasf' with tag 'safsdf'
[GIN] 2024/01/14 - 21:09:11 | 200 | 2.1614ms | 127.0.0.1 | DELETE "/clan/3"
[GIN] 2024/01/14 - 21:09:11 | 200 | 760µs | 127.0.0.1 | GET "/players"
[GIN] 2024/01/14 - 21:09:55 | 200 | 1.9993ms | 127.0.0.1 | GET "/"
[GIN] 2024/01/14 - 21:09:57 | 200 | 128.1µs | 127.0.0.1 | GET "/players"
Deleted clan 'sfdasfd' with tag 'sdfasdf'
[GIN] 2024/01/14 - 21:10:00 | 200 | 1.9241ms | 127.0.0.1 | DELETE "/clan/4"
[GIN] 2024/01/14 - 21:10:00 | 200 | 102.5µs | 127.0.0.1 | GET "/players"
[GIN] 2024/01/14 - 21:19:05 | 200 | 504µs | 127.0.0.1 | GET "/"
[GIN] 2024/01/14 - 21:19:06 | 200 | 634.5µs | 127.0.0.1 | GET "/"
[GIN] 2024/01/14 - 21:19:38 | 200 | 2.9992ms | 127.0.0.1 | GET "/"
[GIN] 2024/01/14 - 21:19:47 | 200 | 150.7µs | 127.0.0.1 | GET "/players"
[GIN] 2024/01/14 - 21:19:48 | 200 | 854.7µs | 127.0.0.1 | GET "/clan/1"
[GIN] 2024/01/14 - 21:19:49 | 200 | 597µs | 127.0.0.1 | GET "/players"
[GIN] 2024/01/14 - 21:19:51 | 200 | 269.2µs | 127.0.0.1 | GET "/"
[GIN] 2024/01/14 - 21:20:15 | 200 | 1.86ms | 127.0.0.1 | GET "/"
[GIN] 2024/01/14 - 21:20:17 | 200 | 997.5µs | 127.0.0.1 | GET "/players"
[GIN] 2024/01/14 - 21:20:17 | 200 | 573.6µs | 127.0.0.1 | GET "/clan/1"
[GIN] 2024/01/14 - 21:40:22 | 200 | 502.9µs | 127.0.0.1 | GET "/"
[GIN] 2024/01/14 - 21:40:22 | 304 | 600.3µs | 127.0.0.1 | GET "/static/index.css"
[GIN] 2024/01/14 - 21:42:07 | 200 | 503.3µs | 127.0.0.1 | GET "/"
[GIN] 2024/01/14 - 21:42:08 | 200 | 1.0002ms | 127.0.0.1 | GET "/"
[GIN] 2024/01/14 - 21:42:57 | 200 | 1.0243ms | 127.0.0.1 | GET "/"
[GIN] 2024/01/14 - 21:43:01 | 200 | 240.1µs | 127.0.0.1 | GET "/players"
[GIN] 2024/01/14 - 21:43:02 | 200 | 867.7µs | 127.0.0.1 | GET "/clan/1"
Updating clan 'asdf1111'#1 (new: 'asdf11113') with tag 'fdsa' with updating 'true'
[GIN] 2024/01/14 - 21:43:04 | 200 | 2.4373ms | 127.0.0.1 | PATCH "/clan/1"
[GIN] 2024/01/14 - 21:43:06 | 200 | 1.8074ms | 127.0.0.1 | GET "/"
[GIN] 2024/01/14 - 21:46:01 | 200 | 1.0387ms | 127.0.0.1 | GET "/"
[GIN] 2024/01/14 - 21:47:27 | 200 | 1.0153ms | 127.0.0.1 | GET "/"
[GIN] 2024/01/14 - 21:48:16 | 200 | 502.8µs | 127.0.0.1 | GET "/"
[GIN] 2024/01/14 - 21:48:17 | 200 | 2.0001ms | 127.0.0.1 | GET "/"
[GIN] 2024/01/14 - 21:49:29 | 200 | 611.9µs | 127.0.0.1 | GET "/"
[GIN] 2024/01/14 - 21:49:30 | 200 | 316.7µs | 127.0.0.1 | GET "/players"
Deleted clan 'safdsdfafd' with tag 'asdfasdfsdf'
[GIN] 2024/01/14 - 21:49:32 | 200 | 2.1928ms | 127.0.0.1 | DELETE "/clan/6"
[GIN] 2024/01/14 - 21:49:32 | 200 | 75.1µs | 127.0.0.1 | GET "/players"
[GIN] 2024/01/14 - 21:50:57 | 200 | 1.1241ms | 127.0.0.1 | GET "/"
[GIN] 2024/01/14 - 22:05:47 | 200 | 1.1526ms | 127.0.0.1 | GET "/"

73
main.go Normal file
View File

@@ -0,0 +1,73 @@
package main
import (
"InfrantrySkillCalculator/controllers"
"InfrantrySkillCalculator/models"
"InfrantrySkillCalculator/utils"
"github.com/gin-gonic/gin"
"html/template"
"io"
"log"
"os"
)
func main() {
//gin.SetMode(gin.ReleaseMode) // uncomment upon release
router := gin.New()
models.ConnectDatabase()
f, _ := os.OpenFile("isc_rest.log", os.O_RDWR|os.O_APPEND, 0660)
utils.GinWriter = io.MultiWriter(f, os.Stdout)
router.Use(
gin.LoggerWithWriter(utils.GinWriter, "/update"),
gin.Recovery(),
)
router.Static("/static", "./static")
router.GET("/", func(c *gin.Context) {
files := []string{
"./templates/index.html",
"./templates/components/home_clan_bar.html",
"./templates/components/opp_clan_bar.html",
"./templates/components/home_player_bar.html",
"./templates/components/opp_player_bar.html",
"./templates/components/home_player_list.html",
"./templates/components/opp_player_list.html",
"./templates/components/bottom_controls.html",
"./templates/modals/delete_clan.html",
"./templates/modals/add_clan.html",
"./templates/modals/edit_clan.html",
"./templates/components/header.html",
}
tmpl, err := template.ParseFiles(files...)
var clans []models.Clan
models.DB.Find(&clans)
data := map[string]interface{}{
"clans": clans,
}
if err != nil {
log.Fatal(err)
}
err = tmpl.Execute(c.Writer, data)
if err != nil {
log.Fatal(err)
}
})
router.GET("/clans", controllers.GetAllClans)
router.GET("/clan/:id", controllers.GetClanByID)
router.POST("/clan", controllers.AddClan)
router.PATCH("/clan/:id", controllers.UpdateClanByID)
router.DELETE("/clan/:id", controllers.DeleteClanByID)
router.GET("/players", controllers.GetAllPlayers)
log.Println("Running on 8000...")
log.Fatal(router.Run(":8000"))
}

8
models/clan.go Normal file
View File

@@ -0,0 +1,8 @@
package models
type Clan struct {
ID uint `json:"id" gorm:"primary_key"`
Name string `json:"name"`
Tag string `json:"tag"`
KeepUpdated bool `json:"keep_updated"`
}

7
models/player.go Normal file
View File

@@ -0,0 +1,7 @@
package models
type Player struct {
ID uint `json:"id" gorm:"primary_key"`
Name string `json:"name"`
ClanID uint `json:"clan_id"`
}

37
models/setup.go Normal file
View File

@@ -0,0 +1,37 @@
package models
import (
"gorm.io/driver/sqlite"
"gorm.io/gorm"
"gorm.io/gorm/logger"
"log"
)
var DB *gorm.DB
func ConnectDatabase() {
database, err := gorm.Open(sqlite.Open("isc_data.db"), &gorm.Config{
Logger: logger.Default.LogMode(logger.Silent),
SkipDefaultTransaction: true,
PrepareStmt: true,
})
if err != nil {
panic("Failed to connect to database! " + err.Error())
}
err = database.AutoMigrate(&Clan{})
if err != nil {
log.Fatal(err)
}
err = database.AutoMigrate(&Player{})
if err != nil {
log.Fatal(err)
}
//database.AutoMigrate(&PlayerCache{})
//database.AutoMigrate(&User{})
//database.AutoMigrate(&Game{})
//database.AutoMigrate(&MetricSettings{})
DB = database
}

55
static/index.css Normal file
View File

@@ -0,0 +1,55 @@
body, html {
margin: 0;
padding: 0;
font-family: Arial, sans-serif;
background-color: #404040;
height: 100%;
}
.container {
display: flex;
/*height: 80vh;*/
}
.side {
flex: 1;
padding: 10px;
border-right: 1px solid #ccc;
}
.clan-selection {
display: flex;
align-items: center;
margin-bottom: 10px;
}
.player-list {
list-style: none;
height: 60%;
overflow-y: auto;
border: 1px solid #ccc;
padding: 5px;
}
.list-controls {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 10px;
}
.bottom-controls {
display: flex;
justify-content: space-between;
padding: 10px;
}
.settings-btn, .calculate-btn, .single-request-btn {
cursor: pointer;
}
.form-control.overflow-auto {
height: auto; /* Adjust as needed */
max-height: 300px; /* Adjust as needed */
}

View File

@@ -0,0 +1,15 @@
{{ define "bottom_controls" }}
<div class="row justify-content-between border-top pt-4">
<div class="col-md-auto">
<button class="btn btn-lg btn-outline-secondary text-secondary-emphasis w-100"><i class="bi bi-gear-fill me-2"></i>Einstellungen</button>
</div>
<div class="col-md-auto">
<button class="btn btn-lg btn-outline-primary w-100"><i class="bi bi-calculator-fill me-2"></i>Berechnen</button>
</div>
<div class="col-md-auto">
<button class="btn btn-lg btn-outline-secondary text-secondary-emphasis w-100"><i class="bi bi-person me-2"></i>Einzel-Abfrage</button>
</div>
</div>
{{ end }}

View File

@@ -0,0 +1,12 @@
{{ define "header" }}
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Infantry Skill Calculator</title>
<script src="https://unpkg.com/htmx.org@1.9.6"></script>
<!-- Bootstrap 5 CSS -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
<link rel="stylesheet" href="../static/index.css">
{{ end }}

View File

@@ -0,0 +1,41 @@
{{ define "home_clan_bar" }}
<div class="row g-2">
<div class="col-auto">
<label for="home-clan" class="col-form-label col-form-label-lg">Clans:</label>
</div>
<div class="col">
<div class="input-group input-group-lg mb-3">
<select class="form-select form-control border-secondary" id="home-clan" hx-get="/players" hx-target="#home-player-list">
<option disabled selected value>Auswählen...</option>
{{ range .clans }}
<option value="{{ .ID }}">[{{ .Tag }}] {{ .Name }}</option>
{{ end }}
</select>
<button class="btn btn-lg btn-outline-secondary text-danger" type="button" id="home-delete" data-bs-toggle="modal" data-bs-list="#home-clan" data-bs-target="#deleteClanModal" disabled>
<i class="bi bi-trash3"></i>
</button>
<button class="btn btn-lg btn-outline-secondary text-primary" type="button" data-bs-toggle="modal" data-bs-list="#home-clan" data-bs-target="#editClanModal" id="home-edit" disabled>
<i class="bi bi-pencil-fill"></i>
</button>
<button class="btn btn-lg btn-outline-secondary text-success" type="button" data-bs-toggle="modal" data-bs-list="#home-clan" data-bs-target="#addClanModal" id="home-add">
<i class="bi bi-plus-lg"></i>
</button>
</div>
</div>
</div>
<script lang="javascript">
document.addEventListener('DOMContentLoaded', function() {
const dropdownHome = document.getElementById('home-clan');
const deleteButtonHome = document.getElementById('home-delete');
const editButtonHome = document.getElementById('home-edit');
dropdownHome.addEventListener('change', function () {
deleteButtonHome.disabled = !this.value;
editButtonHome.disabled = !this.value;
});
});
</script>
{{ end }}

View File

@@ -0,0 +1,22 @@
{{ define "home_player_bar" }}
<div class="row mt-3 justify-content-between align-items-center">
<div class="col-auto">
<div class="btn-group btn-group-lg" role="group">
<button type="button" class="btn btn-outline-secondary text-secondary-emphasis py-1 px-3"><i class="bi bi-check-square fs-4"></i></button>
<button type="button" class="btn btn-outline-secondary text-secondary-emphasis py-1 px-3"><i class="bi bi-square fs-4"></i></button>
</div>
</div>
<div class="col px-0">
<span class="badge fs-5 w-100 text-secondary-emphasis">0 von 0 ausgewählt</span>
</div>
<div class="col-auto">
<div class="btn-group btn-group-lg" role="group">
<button type="button" class="btn btn-outline-secondary text-danger py-1 px-3"><i class="bi bi-person-dash fs-4"></i></button>
<button type="button" class="btn btn-outline-secondary text-primary py-1 px-3"><i class="bi bi-person-gear fs-4"></i></button>
<button type="button" class="btn btn-outline-secondary text-success py-1 px-3"><i class="bi bi-person-add fs-4"></i></button>
</div>
</div>
</div>
{{ end }}

View File

@@ -0,0 +1,8 @@
{{ define "home_player_list" }}
<label for="home-player-list" class="col-form-label col-form-label-lg pt-0">Spieler:</label>
<select multiple class="form-control form-control-lg overflow-auto border-secondary" id="home-player-list" size="10">
<!-- Player list items go here -->
</select>
{{ end }}

View File

@@ -0,0 +1,41 @@
{{ define "opp_clan_bar" }}
<div class="row g-2">
<div class="col-auto">
<label for="opponent-clan" class="col-form-label col-form-label-lg">Clans:</label>
</div>
<div class="col">
<div class="input-group input-group-lg mb-3">
<select class="form-select form-control border-secondary" id="opponent-clan" hx-get="/players" hx-target="#opponent-player-list">
<option disabled selected value>Auswählen...</option>
{{ range .clans }}
<option value="{{ .ID }}">[{{ .Tag }}] {{ .Name }}</option>
{{ end }}
</select>
<button class="btn btn-lg btn-outline-secondary text-danger" type="button" id="opponent-delete" data-bs-toggle="modal" data-bs-list="#opponent-clan" data-bs-target="#deleteClanModal" disabled>
<i class="bi bi-trash3"></i>
</button>
<button class="btn btn-lg btn-outline-secondary text-primary" type="button" data-bs-toggle="modal" data-bs-list="#opponent-clan" data-bs-target="#editClanModal" id="opponent-edit" disabled>
<i class="bi bi-pencil-fill"></i>
</button>
<button class="btn btn-lg btn-outline-secondary text-success" type="button" data-bs-toggle="modal" data-bs-list="#opponent-clan" data-bs-target="#addClanModal" id="opponent-add">
<i class="bi bi-plus-lg"></i>
</button>
</div>
</div>
</div>
<script lang="javascript">
document.addEventListener('DOMContentLoaded', function() {
const dropdownOpponent = document.getElementById('opponent-clan');
const deleteButtonOpponent = document.getElementById('opponent-delete');
const editButtonOpponent = document.getElementById('opponent-edit');
dropdownOpponent.addEventListener('change', function () {
deleteButtonOpponent.disabled = !this.value;
editButtonOpponent.disabled = !this.value;
});
});
</script>
{{ end }}

View File

@@ -0,0 +1,22 @@
{{ define "opp_player_bar" }}
<div class="row mt-3 justify-content-between align-items-center">
<div class="col-auto">
<div class="btn-group btn-group-lg" role="group">
<button type="button" class="btn btn-outline-secondary text-secondary-emphasis py-1 px-3"><i class="bi bi-check-square fs-4"></i></button>
<button type="button" class="btn btn-outline-secondary text-secondary-emphasis py-1 px-3"><i class="bi bi-square fs-4"></i></button>
</div>
</div>
<div class="col px-0">
<span class="badge fs-5 w-100 text-secondary-emphasis">0 von 0 ausgewählt</span>
</div>
<div class="col-auto">
<div class="btn-group btn-group-lg" role="group">
<button type="button" class="btn btn-outline-secondary text-danger py-1 px-3"><i class="bi bi-person-dash fs-4"></i></button>
<button type="button" class="btn btn-outline-secondary text-primary py-1 px-3"><i class="bi bi-person-gear fs-4"></i></button>
<button type="button" class="btn btn-outline-secondary text-success py-1 px-3"><i class="bi bi-person-add fs-4"></i></button>
</div>
</div>
</div>
{{ end }}

View File

@@ -0,0 +1,8 @@
{{ define "opp_player_list" }}
<label for="opponent-player-list" class="col-form-label col-form-label-lg pt-0">Spieler:</label>
<select multiple class="form-control form-control-lg overflow-auto border-secondary" id="opponent-player-list" size="10">
<!-- Player list items go here -->
</select>
{{ end }}

56
templates/index.html Normal file
View File

@@ -0,0 +1,56 @@
<!DOCTYPE html>
<html lang="de">
<head>
{{ template "header" . }}
</head>
<body data-bs-theme="dark">
<div class="container-xxl bg-dark mt-5 p-4 rounded-3 text-light">
<div class="row">
<!-- Home-Clan Column -->
<div class="col-md-6 d-flex flex-column border-end px-3 pb-4">
<h4 class="text-center mt-2 pb-3 mb-3 border-bottom">Heim-Team</h4>
<!-- Clan Selection -->
{{ template "home_clan_bar" . }}
<!-- Player List -->
{{ template "home_player_list" . }}
<!-- List Controls -->
{{ template "home_player_bar" . }}
</div>
<!-- Opponent-Clan Column -->
<div class="col-md-6 d-flex flex-column px-3 pb-4">
<h4 class="text-center mt-2 pb-3 mb-3 border-bottom">Gegner-Team</h4>
<!-- Clan Selection -->
{{ template "opp_clan_bar" . }}
<!-- Player List -->
{{ template "opp_player_list" . }}
<!-- List Controls -->
{{ template "opp_player_bar" . }}
</div>
</div>
<!-- Bottom Controls -->
{{ template "bottom_controls" . }}
</div>
<!-- Delete Clan Modal -->
{{ template "delete_clan" . }}
<!-- Add Clan Modal -->
{{ template "add_clan" . }}
<!-- Edit Clan Modal -->
{{ template "edit_clan" . }}
<script lang="javascript">
document.addEventListener('DOMContentLoaded', function() {
});
</script>
<!-- Bootstrap 5 JS Bundle with Popper -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

View File

@@ -0,0 +1,80 @@
{{ define "add_clan" }}
<div class="modal fade" id="addClanModal" tabindex="-1">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
<h1 class="modal-title fs-3 text-success fw-bold" id="addClanModalLabel">Clan hinzufügen</h1>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<div class="form-floating mb-3">
<input type="text" class="form-control form-control-lg" id="clanName" placeholder="Clan-Name">
<label for="clanName">Clan-Name</label>
</div>
<div class="form-floating">
<input type="text" class="form-control form-control-lg" id="clanTag" placeholder="Clan-Tag">
<label for="clanTag">Clan-Tag</label>
</div>
<div class="form-check form-check-inline mt-3 fs-5">
<input class="form-check-input" type="checkbox" id="keepUpdated" value="keepUpdated">
<label class="form-check-label" for="keepUpdated">Immer aktuell halten</label>
</div>
</div>
<div class="modal-footer">
<button type="submit" name="submit" class="btn btn-lg btn-primary">Hinzufügen</button>
<button type="button" class="btn btn-lg btn-secondary" data-bs-dismiss="modal">Abbrechen</button>
</div>
</div>
</div>
</div>
<script lang="javascript">
document.addEventListener('DOMContentLoaded', function() {
const addClanModal = document.getElementById('addClanModal');
const addClanModalBS = new bootstrap.Modal('#addClanModal');
if (addClanModal) {
addClanModal.addEventListener('shown.bs.modal', event => {
const submitButton = addClanModal.querySelector('button[name="submit"]');
submitButton.addEventListener('click', function () {
const button = event.relatedTarget;
const clanListId = button.getAttribute('data-bs-list');
const clanList = document.querySelector(clanListId);
const clanName = addClanModal.querySelector('#clanName');
const clanTag = addClanModal.querySelector('#clanTag');
const keepUpdated = addClanModal.querySelector('#keepUpdated');
fetch("/clan", {
method: "POST",
body: JSON.stringify({
name: clanName.value,
tag: clanTag.value,
keep_updated: keepUpdated.checked
}),
headers: {
"Content-type": "application/json; charset=UTF-8"
}
})
.then((response) => {
if (response.ok) {
const opt = document.createElement('option');
opt.innerText = "[" + clanTag.value + "]" + " " + clanName.value;
opt.value = response.json()['ID'];
clanList.appendChild(opt);
clanList.selectedIndex = clanList.children.length - 1;
clanList.dispatchEvent(new Event('change'));
addClanModalBS.hide();
clanName.value = "";
clanTag.value = "";
keepUpdated.checked = false;
} else
throw new Error(response.error)
});
})
});
}
});
</script>
{{ end }}

View File

@@ -0,0 +1,66 @@
{{ define "delete_clan" }}
<div class="modal modal-lg fade" id="deleteClanModal" tabindex="-1">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
<h1 class="modal-title fs-3 text-danger fw-bold" id="deleteClanModalLabel">Clan löschen</h1>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body fs-5">
Möchtest du den Clan "<span id="clan"></span>" wirklich löschen?
<br><br>
Diese Aktion kann nicht rückgängig gemacht werden.
</div>
<div class="modal-footer">
<button type="submit" name="submit" class="btn btn-lg btn-danger">Löschen</button>
<button type="button" class="btn btn-lg btn-secondary" data-bs-dismiss="modal">Abbrechen</button>
</div>
</div>
</div>
</div>
<script lang="javascript">
document.addEventListener('DOMContentLoaded', function() {
const deleteClanModal = document.getElementById('deleteClanModal')
const deleteClanModalBS = new bootstrap.Modal('#deleteClanModal');
if (deleteClanModal) {
deleteClanModal.addEventListener('show.bs.modal', event => {
const button = event.relatedTarget;
const clanListId = button.getAttribute('data-bs-list');
const clanList = document.querySelector(clanListId);
const selectedClan = clanList.options[clanList.selectedIndex].text;
const modalBodyInput = deleteClanModal.querySelector('#clan');
modalBodyInput.innerText = selectedClan;
const submitButton = deleteClanModal.querySelector('button[name="submit"]');
submitButton.addEventListener('click', function () {
const button = event.relatedTarget;
const clanListId = button.getAttribute('data-bs-list');
const clanList = document.querySelector(clanListId);
const clanId = parseInt(clanList.value);
fetch("/clan/" + clanId, {
method: "DELETE",
headers: {
"Content-type": "application/json; charset=UTF-8"
}
})
.then((response) => {
if (response.ok) {
clanList.removeChild(clanList.children[clanList.selectedIndex]);
clanList.selectedIndex = 0;
clanList.dispatchEvent(new Event('change'));
deleteClanModalBS.hide();
} else
throw new Error(response.error)
});
})
});
}
});
</script>
{{ end }}

View File

@@ -0,0 +1,97 @@
{{ define "edit_clan" }}
<div class="modal fade" id="editClanModal" tabindex="-1">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
<h1 class="modal-title fs-3 text-success fw-bold" id="editClanModalLabel">Clan bearbeiten</h1>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<div class="form-floating mb-3">
<input type="text" class="form-control form-control-lg" id="editClanName" placeholder="Clan-Name">
<label for="editClanName">Clan-Name</label>
</div>
<div class="form-floating">
<input type="text" class="form-control form-control-lg" id="editClanTag" placeholder="Clan-Tag">
<label for="editClanTag">Clan-Tag</label>
</div>
<div class="form-check form-check-inline mt-3 fs-5">
<input class="form-check-input" type="checkbox" id="editKeepUpdated" value="keepUpdated">
<label class="form-check-label" for="editKeepUpdated">Immer aktuell halten</label>
</div>
<input type="hidden" id="editClanId" value="">
</div>
<div class="modal-footer">
<button type="submit" name="submit" class="btn btn-lg btn-primary">Speichern</button>
<button type="button" class="btn btn-lg btn-secondary" data-bs-dismiss="modal">Abbrechen</button>
</div>
</div>
</div>
</div>
<script lang="javascript">
document.addEventListener('DOMContentLoaded', function() {
const editClanModal = document.getElementById('editClanModal');
const editClanModalBS = new bootstrap.Modal('#editClanModal');
if (editClanModal) {
editClanModal.addEventListener('show.bs.modal', event => {
const button = event.relatedTarget;
const clanListId = button.getAttribute('data-bs-list');
const clanList = document.querySelector(clanListId);
const selectedClanItem = clanList.options[clanList.selectedIndex];
const clanName = editClanModal.querySelector('#editClanName');
const clanTag = editClanModal.querySelector('#editClanTag');
const keepUpdated = editClanModal.querySelector('#editKeepUpdated');
const clanId = parseInt(clanList.value);
const submitButton = editClanModal.querySelector('button[name="submit"]');
fetch("/clan/" + clanId, {
method: "GET",
headers: {
"Content-type": "application/json; charset=UTF-8"
}
})
.then((response) => {
return response.json();
})
.then((result) => {
keepUpdated.checked = result['keep_updated'];
clanName.value = result['name'];
clanTag.value = result['tag'];
})
.catch((error) => {
throw new Error(error);
});
submitButton.addEventListener('click', function () {
fetch("/clan/" + clanId, {
method: "PATCH",
body: JSON.stringify({
name: clanName.value,
tag: clanTag.value,
keep_updated: keepUpdated.checked
}),
headers: {
"Content-type": "application/json; charset=UTF-8"
}
})
.then((response) => {
if (response.ok) {
selectedClanItem.innerText = "[" + clanTag.value + "]" + " " + clanName.value;
editClanModalBS.hide();
clanName.value = "";
clanTag.value = "";
keepUpdated.checked = false;
} else
throw new Error(response.error)
});
})
});
}
});
</script>
{{ end }}

11
utils/globals.go Normal file
View File

@@ -0,0 +1,11 @@
package utils
import (
"io"
"time"
)
var ClanLastChanged time.Time = time.Now().UTC()
var CacheLastChanged time.Time = time.Now().UTC()
var PlayersLastChanged time.Time = time.Now().UTC()
var GinWriter io.Writer = nil