diff --git a/.env b/.env new file mode 100644 index 0000000..e29f14f --- /dev/null +++ b/.env @@ -0,0 +1,6 @@ +DB_Host: 'db' +DB_Port: 3306 +DB_Name: 'MatheApp' +DB_Charset: 'utf8mb4' +DB_User: 'MatheApp' +DB_Password: 'password' diff --git a/Dockerfile.math_wizard b/Dockerfile.math_wizard index 845815e..dc3859f 100644 --- a/Dockerfile.math_wizard +++ b/Dockerfile.math_wizard @@ -1,5 +1,5 @@ # Verwende ein Basisimage mit PHP -FROM php:8.4-cli +FROM php:8.2-cli # Setze das Arbeitsverzeichnis WORKDIR /app diff --git a/README.md b/README.md deleted file mode 100644 index 82efbc1..0000000 --- a/README.md +++ /dev/null @@ -1,33 +0,0 @@ -# The Math Wizard - -The Math Wizard is a browser game that helps children automate the 1x1. It acts as a dungeon crawler game in which children can fight monsters by solving multiplication problems. - -## Requirements - -To run the game, you need Docker and Docker-Compose. Make sure that these are installed and configured on your system. - -## Installation - -1. clone the repository with `git clone https://github.com/PatrykHegenberg/TheMathWizard.git`. -2. change to the project directory with `cd TheMathWizard`. -3. run `docker-compose up --build` to install and start the game. - -Once the installation is complete, you can access the game via your web browser under `localhost:8080` or the computer name. - -## Usage - -To create a new account, go to `localhost:8080/signup` in the browser. After signing up, you can start the game and begin fighting monsters and solving multiplication tasks. Your progress will be saved persistently on the hard disk of the host computer. - -## License - -This project is licensed under the MIT license. For more information, see the LICENSE file. - -## Acknowledgments - -This project uses the following resources: -- [ROT.js](https://github.com/ondras/rot.js/) (BSD-Lizenz) -- [Micro Rogue tileset](https://kenney.nl/assets/micro-roguelike) von Kenney.nl (CC0 1.0 Universelle Lizenz) -- [NES.css](https://nostalgic-css.github.io/NES.css/) (MIT Lizenz) -- [sfxr.me](https://www.sfxr.me/) (Öffentliches Domain) -- [Pixel Coin Bild](https://opengameart.org/content/pixel-coins) (CC-BY 3.0 Lizenz) -- [Roguelike-Browser-Boilerplate](https://github.com/chr15m/roguelike-browser-boilerplate) diff --git a/images/01coin.gif b/assets/images/01coin.gif similarity index 100% rename from images/01coin.gif rename to assets/images/01coin.gif diff --git a/images/16x16DungeonTileset.png b/assets/images/16x16DungeonTileset.png similarity index 100% rename from images/16x16DungeonTileset.png rename to assets/images/16x16DungeonTileset.png diff --git a/images/HeroBanner.png b/assets/images/HeroBanner.png similarity index 100% rename from images/HeroBanner.png rename to assets/images/HeroBanner.png diff --git a/images/icon.png b/assets/images/icon.png similarity index 100% rename from images/icon.png rename to assets/images/icon.png diff --git a/images/monster_imp.png b/assets/images/monster_imp.png similarity index 100% rename from images/monster_imp.png rename to assets/images/monster_imp.png diff --git a/images/monster_necromancer.png b/assets/images/monster_necromancer.png similarity index 100% rename from images/monster_necromancer.png rename to assets/images/monster_necromancer.png diff --git a/images/npc_knight_green.png b/assets/images/npc_knight_green.png similarity index 100% rename from images/npc_knight_green.png rename to assets/images/npc_knight_green.png diff --git a/images/weapon_silver_topaz.png b/assets/images/weapon_silver_topaz.png similarity index 100% rename from images/weapon_silver_topaz.png rename to assets/images/weapon_silver_topaz.png diff --git a/images/weapon_sword_red.png b/assets/images/weapon_sword_red.png similarity index 100% rename from images/weapon_sword_red.png rename to assets/images/weapon_sword_red.png diff --git a/images/weapon_sword_ruby.png b/assets/images/weapon_sword_ruby.png similarity index 100% rename from images/weapon_sword_ruby.png rename to assets/images/weapon_sword_ruby.png diff --git a/scripts/addition.js b/assets/scripts/addition.js similarity index 100% rename from scripts/addition.js rename to assets/scripts/addition.js diff --git a/scripts/game.js b/assets/scripts/game.js similarity index 100% rename from scripts/game.js rename to assets/scripts/game.js diff --git a/scripts/mathe.js b/assets/scripts/mathe.js similarity index 100% rename from scripts/mathe.js rename to assets/scripts/mathe.js diff --git a/styles/game.css b/assets/styles/game.css similarity index 100% rename from styles/game.css rename to assets/styles/game.css diff --git a/styles/mathe.css b/assets/styles/mathe.css similarity index 100% rename from styles/mathe.css rename to assets/styles/mathe.css diff --git a/styles/style.css b/assets/styles/style.css similarity index 94% rename from styles/style.css rename to assets/styles/style.css index fbbc2a4..6fbaae5 100644 --- a/styles/style.css +++ b/assets/styles/style.css @@ -20,6 +20,7 @@ h1 { font-size: 2em; margin: 0.67em 0; } + figcaption, figure, main { @@ -50,7 +51,8 @@ img { max-width: 100%; vertical-align: middle; } -.site-header img{ + +.site-header img { height: 60px; } @@ -84,6 +86,7 @@ h6, color: #2e1e26; font-weight: 600; } + h1, .h1 { font-size: 38px; @@ -119,6 +122,7 @@ h2, margin-top: 48px; margin-bottom: 16px; } + h3, .h3 { margin-top: 36px; @@ -141,9 +145,11 @@ p { .container { max-width: 1128px; } + .container-sm { max-width: 848px; } + .container .container-sm { max-width: 800px; padding-left: 0; @@ -167,6 +173,7 @@ p { padding: 12px 0; z-index: 2; } + .site-header-inner { position: relative; display: flex; @@ -179,8 +186,8 @@ p { text-align: center; padding-top: 48px; padding-bottom: 88px; - background-image: url("./../../images/HeroBanner.png"); - background-repeat: no-repeat; + background-image: url("/static/images/HeroBanner.png"); + background-repeat: no-repeat; background-position: center; background-size: cover; } @@ -192,15 +199,18 @@ footer img { .hero-copy { position: relative; } + .hero-paragraph { margin-bottom: 32px; } + .hero-cta { max-width: 400px; margin-left: auto; margin-right: auto; margin-bottom: 80px; } + .lights-toggle { color: rgba(107, 122, 144, 0.64); } @@ -211,10 +221,12 @@ footer img { padding-top: 88px; padding-bottom: 120px; } + .hero-inner { display: flex; justify-content: space-between; } + .hero-copy { padding-top: 40px; padding-right: 48px; @@ -222,33 +234,42 @@ footer img { max-width: 512px; z-index: 1; } + .hero-title { margin-bottom: 16px; } + .hero-paragraph { margin-bottom: 32px; } + .hero-cta { display: flex; align-items: center; margin: 0; } + .hero-cta .button { min-width: 170px; } + .hero-cta .button:first-child { margin-right: 32px; } + .header-illustration { display: block; } + .hero-media { z-index: 0; } + .hero-media img, .hero-media svg { max-width: none; } + .header-illustration-image { display: block; position: absolute; @@ -258,6 +279,7 @@ footer img { height: 324px; } } + .features-wrap { max-width: 540px; margin: 0 auto; @@ -267,21 +289,26 @@ footer img { text-align: center; margin-bottom: 48px; } + .feature:last-of-type { margin-bottom: 0; } + .feature-icon { display: inline-flex; margin-bottom: 16px; } + .feature-icon img, .feature-icon svg { max-width: none; } + .feature-title { position: relative; margin-bottom: 26px; } + .feature-title::after { content: ''; width: 32px; @@ -291,20 +318,25 @@ footer img { left: calc(50% - 16px); background: #e9edf3; } - .feature-title::after { + +.feature-title::after { background: #3f2a34; } + @media (min-width: 641px) { .features { position: relative; } + .features .section-inner { padding-bottom: 100px; } + .features .section-paragraph { padding-left: 72px; padding-right: 72px; } + .features::before { content: ''; width: 100%; @@ -312,40 +344,47 @@ footer img { position: absolute; left: 0; top: 168px; - background: linear-gradient( - to bottom, - #3f2a34, - #3f2a34 - ); + background: linear-gradient(to bottom, + #3f2a34, + #3f2a34); } - .features::before { + + .features::before { display: none; } + .feature { text-align: left; } + .feature-inner { display: flex; } + .feature-icon { display: block; margin-top: 8px; margin-right: 32px; margin-bottom: 0; } + .feature-title::after { left: 0; } } + .cta { position: relative; } + .cta .section-inner { padding-bottom: 64px; } + .cta .section-paragraph { margin-bottom: 32px; } + .cta::before { content: ''; position: absolute; @@ -354,32 +393,39 @@ footer img { height: 263px; width: 1440px; } + .cta-cta { max-width: 400px; margin-left: auto; margin-right: auto; } + @media (max-width: 639px) { .cta-cta .button { display: flex; } } + @media (min-width: 641px) { .cta .section-inner { padding-bottom: 128px; } + .cta .section-paragraph { margin-bottom: 40px; padding-left: 72px; padding-right: 72px; } + .cta::before { bottom: 0; } } + .is-boxed { background: #e9edf3; } + .body-wrap { background: #fff; overflow: hidden; @@ -387,14 +433,17 @@ footer img { flex-direction: column; min-height: 100vh; } + .boxed-container { max-width: 1440px; margin: 0 auto; box-shadow: 0 16px 48px rgba(255, 255, 255, 0.5); } + main { flex: 1 0 auto; } + .section-inner { position: relative; padding-top: 48px; @@ -432,6 +481,7 @@ main { margin: 0 auto; height: 500px; } + .container2 { display: flex; justify-content: center; @@ -440,6 +490,7 @@ main { margin: 0 auto; height: 750px; } + .container3 { display: flex; justify-content: center; @@ -450,6 +501,7 @@ main { background-color: #2e1e26; padding: 20px; } + .container4 { display: flex; justify-content: center; @@ -460,6 +512,7 @@ main { background-color: #2e1e26; padding: 20px; } + .footer-copyright { flex: none; width: 80%; @@ -467,6 +520,7 @@ main { justify-content: center; align-items: center; } + .register { height: 1200px; display: flex; @@ -490,6 +544,7 @@ main { .nes-field { max-width: 400px; } + .login { height: 1200px; display: flex; @@ -510,6 +565,7 @@ main { border-radius: 5px; box-shadow: #3f2a34; } + .learnContainer { display: flex; justify-content: center; @@ -523,6 +579,7 @@ main { border-radius: 5px; box-shadow: 0 0 4px #433e4c } + .profile { height: 1200px; display: flex; @@ -532,58 +589,68 @@ main { border-radius: 5px; box-shadow: #3f2a34; } + .container .login { display: flex; justify-content: center; align-items: center; text-align: center; } + .asset-dark { visibility: hidden; opacity: 0; } + .is-loaded .asset-dark { visibility: visible; opacity: 1; } + .asset-dark { display: none; } - .asset-dark { +.asset-dark { display: block; } - a { + +a { color: #8595ae; } - h1, - h2, - h3, - h4, - h5, - h6, - .h1, - .h2, - .h3, - .h4, - .h5, - .h6 { + +h1, +h2, +h3, +h4, +h5, +h6, +.h1, +.h2, +.h3, +.h4, +.h5, +.h6 { color: #fff !important; } + .is-boxed { background: #3f2a34; } - .body-wrap { + +.body-wrap { background: #2e1e26; } - .boxed-container { + +.boxed-container { box-shadow: 0 16px 48px #433e4c; } - .has-top-divider { +.has-top-divider { position: relative; } - .has-top-divider::before { + +.has-top-divider::before { content: ''; position: absolute; top: 0; @@ -593,10 +660,12 @@ main { height: 1px; background: #3f2a34; } - .has-bottom-divider { + +.has-bottom-divider { position: relative; } - .has-bottom-divider::after { + +.has-bottom-divider::after { content: ''; position: absolute; bottom: 0; @@ -606,6 +675,7 @@ main { height: 1px; background: #3f2a34; } + body, a, h1, @@ -625,4 +695,4 @@ h6, .boxed-container { transition: box-shadow 0.15s ease; -} +} \ No newline at end of file diff --git a/db.go b/db.go new file mode 100644 index 0000000..a614f60 --- /dev/null +++ b/db.go @@ -0,0 +1,148 @@ +package main + +import ( + "crypto/sha256" + "database/sql" + "encoding/hex" + "fmt" + "os" + + _ "github.com/go-sql-driver/mysql" + "github.com/joho/godotenv" + "github.com/labstack/echo/v4" +) + +var db *sql.DB + +func init() { + err := godotenv.Load() + if err != nil { + fmt.Println("Error loading .env file") + os.Exit(1) + } + + dbHost := os.Getenv("DB_Host") + dbPort := os.Getenv("DB_Port") + dbName := os.Getenv("DB_Name") + dbCharset := os.Getenv("DB_Charset") + dbUser := os.Getenv("DB_User") + dbPassword := os.Getenv("DB_Password") + + // Connect to the database + db, err = sql.Open("mysql", fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=%s", dbUser, dbPassword, dbHost, dbPort, dbName, dbCharset)) + // db, err = sql.Open("mysql", fmt.Sprintf("%s:%s@tcp(db:3306)/%s?charset=utf8mb4", dbUser, dbPassword, dbName)) + + if err != nil { + fmt.Println("Error connecting to the database") + os.Exit(1) + } +} + +// User struct +type User struct { + ID int `json:"id"` + Username string `json:"username"` + Vorname string `json:"vorname"` + Nachname string `json:"nachname"` + Email string `json:"email"` + Password string `json:"password"` + LessonCount int `json:"lesson_count"` + Level int `json:"level"` + XP int `json:"xp"` + Coins int `json:"coins"` + IsAdmin sql.NullBool `json:"is_admin"` +} + +// CRUD operations + +func getUsers(c echo.Context) error { + rows, err := db.Query("SELECT * FROM user") + if err != nil { + return err + } + defer rows.Close() + + users := []User{} + for rows.Next() { + user := User{} + err := rows.Scan(&user.ID, &user.Username, &user.Vorname, &user.Nachname, &user.Email, &user.Password, &user.LessonCount, &user.Level, &user.XP, &user.Coins, &user.IsAdmin) + if err != nil { + return err + } + users = append(users, user) + } + + return c.JSON(200, users) +} + +func getUser(c echo.Context) error { + id := c.Param("id") + + row := db.QueryRow("SELECT * FROM user WHERE id = ?", id) + + user := User{} + err := row.Scan(&user.ID, &user.Username, &user.Vorname, &user.Nachname, &user.Email, &user.Password, &user.LessonCount, &user.Level, &user.XP, &user.Coins, &user.IsAdmin) + if err != nil { + return err + } + + return c.JSON(200, user) +} + +func createUser(c echo.Context) error { + user := new(User) + if err := c.Bind(user); err != nil { + return err + } + + // Hash das Passwort + user.Password = hashPassword(user.Password) + + result, err := db.Exec("INSERT INTO user (username, vorname, nachname, email, password, lesson_count, level, xp, coins, isAdmin) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", + user.Username, user.Vorname, user.Nachname, user.Email, user.Password, user.LessonCount, user.Level, user.XP, user.Coins, user.IsAdmin) + if err != nil { + return err + } + + lastInsertID, err := result.LastInsertId() + if err != nil { + return err + } + + user.ID = int(lastInsertID) + + return c.JSON(201, user) +} + +func updateUser(c echo.Context) error { + id := c.Param("id") + + user := new(User) + if err := c.Bind(user); err != nil { + return err + } + + _, err := db.Exec("UPDATE user SET username = ?, vorname = ?, nachname = ?, email = ?, password = ?, lesson_count = ?, level = ?, xp = ?, coins = ?, isAdmin = ? WHERE id = ?", + user.Username, user.Vorname, user.Nachname, user.Email, user.Password, user.LessonCount, user.Level, user.XP, user.Coins, user.IsAdmin, id) + if err != nil { + return err + } + + return c.NoContent(204) +} + +func deleteUser(c echo.Context) error { + id := c.Param("id") + + _, err := db.Exec("DELETE FROM user WHERE id = ?", id) + if err != nil { + return err + } + + return c.NoContent(204) +} + +func hashPassword(password string) string { + hash := sha256.Sum256([]byte(password)) + return hex.EncodeToString(hash[:]) +} diff --git a/docker-compose.yml b/docker-compose.yml index 4642444..b22948e 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -4,6 +4,7 @@ services: web: #image: math_wizard build: + context: /home/pata/dev/WebProg/TheMathWizard dockerfile: Dockerfile.math_wizard ports: - "8080:8080" @@ -14,6 +15,7 @@ services: db: build: + context: /home/pata/dev/WebProg/TheMathWizard dockerfile: Dockerfile.db environment: MYSQL_ROOT_PASSWORD: password diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..0212516 --- /dev/null +++ b/go.mod @@ -0,0 +1,24 @@ +module the_math_wizard + +go 1.21.5 + +require ( + github.com/chasefleming/elem-go v0.17.0 + github.com/joho/godotenv v1.5.1 + github.com/labstack/echo/v4 v4.11.4 +) + +require ( + github.com/go-sql-driver/mysql v1.7.1 + github.com/golang-jwt/jwt v3.2.2+incompatible // indirect + github.com/labstack/gommon v0.4.2 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/valyala/bytebufferpool v1.0.0 // indirect + github.com/valyala/fasttemplate v1.2.2 // indirect + golang.org/x/crypto v0.17.0 // indirect + golang.org/x/net v0.19.0 // indirect + golang.org/x/sys v0.15.0 // indirect + golang.org/x/text v0.14.0 // indirect + golang.org/x/time v0.5.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..213e730 --- /dev/null +++ b/go.sum @@ -0,0 +1,41 @@ +github.com/chasefleming/elem-go v0.17.0 h1:9hqg6OSacy2YgHLpY/soZqXLOOMfhmKG5qukrSgjxlI= +github.com/chasefleming/elem-go v0.17.0/go.mod h1:hz73qILBIKnTgOujnSMtEj20/epI+f6vg71RUilJAA4= +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/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI= +github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= +github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= +github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= +github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= +github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= +github.com/labstack/echo/v4 v4.11.4 h1:vDZmA+qNeh1pd/cCkEicDMrjtrnMGQ1QFI9gWN1zGq8= +github.com/labstack/echo/v4 v4.11.4/go.mod h1:noh7EvLwqDsmh/X/HWKPUl1AjzJrhyptRyEbQJfxen8= +github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0= +github.com/labstack/gommon v0.4.2/go.mod h1:QlUFxVM+SNXhDL/Z7YhocGIBYOiwB0mXm1+1bAPHPyU= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +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/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= +github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo= +github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= +golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= +golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= +golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= +golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= +golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= +golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/renovate.json b/renovate.json deleted file mode 100644 index 7190a60..0000000 --- a/renovate.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "$schema": "https://docs.renovatebot.com/renovate-schema.json" -} diff --git a/server.go b/server.go new file mode 100644 index 0000000..3ad5e8e --- /dev/null +++ b/server.go @@ -0,0 +1,78 @@ +package main + +import ( + // "fmt" + "net/http" + // "os" + // "os/exec" + // "runtime" + // "strconv" + + // "github.com/chasefleming/elem-go" + // "github.com/chasefleming/elem-go/attrs" + // "github.com/chasefleming/elem-go/htmx" + "the_math_wizard/views" + + "github.com/labstack/echo/v4" + "github.com/labstack/echo/v4/middleware" +) + +func main() { + e := echo.New() + e.Static("/static", "assets") + + // Middleware + e.Use(middleware.Logger()) + e.Use(middleware.Recover()) + + // Routes + e.GET("/", RenderIndexRoute) + e.GET("/learn", RenderLearnRoute) + e.GET("/mathe", RenderMatheRoute) + e.GET("/game", RenderGameRoute) + e.GET("/login", RenderLoginRoute) + e.GET("/register", RenderRegisterRoute) + e.GET("/profile", RenderProfileRoute) + e.GET("/logout", LogoutRoute) + e.GET("/delete", DeleteRoute) + e.GET("/updateData", RenderUpdateRoute) + e.GET("/deleteUser", RenderDeleteRoute) + + // Start the server + e.Logger.Fatal(e.Start(":3000")) + +} + +func RenderIndexRoute(c echo.Context) error { + return c.HTML(http.StatusOK, views.RenderIndex()) +} +func RenderLearnRoute(c echo.Context) error { + return nil +} +func RenderMatheRoute(c echo.Context) error { + return nil +} +func RenderGameRoute(c echo.Context) error { + return nil +} +func RenderLoginRoute(c echo.Context) error { + return nil +} +func RenderRegisterRoute(c echo.Context) error { + return nil +} +func RenderProfileRoute(c echo.Context) error { + return nil +} +func LogoutRoute(c echo.Context) error { + return nil +} +func DeleteRoute(c echo.Context) error { + return nil +} +func RenderUpdateRoute(c echo.Context) error { + return nil +} +func RenderDeleteRoute(c echo.Context) error { + return nil +} diff --git a/views/footer.templ.go b/views/footer.templ.go new file mode 100644 index 0000000..7e5a9c5 --- /dev/null +++ b/views/footer.templ.go @@ -0,0 +1,8 @@ +package views + +import "github.com/chasefleming/elem-go" + +func RenderFooter() elem.Node { + footerContent := elem.Footer(nil) + return footerContent +} diff --git a/views/head.templ.go b/views/head.templ.go new file mode 100644 index 0000000..c634d17 --- /dev/null +++ b/views/head.templ.go @@ -0,0 +1,21 @@ +package views + +import ( + "github.com/chasefleming/elem-go" + "github.com/chasefleming/elem-go/attrs" +) + +func RenderHead() elem.Node { + headContent := elem.Head(nil, + elem.Meta(attrs.Props{attrs.Charset: "UTF-8", attrs.Name: "viewport", attrs.Content: "width=device-width, initial-scale=1.0"}), + elem.Meta(attrs.Props{attrs.HTTPequiv: "X-UA-Compatible", attrs.Content: "IE=edge"}), + elem.Script(attrs.Props{attrs.Src: "https://unpkg.com/htmx.org"}), + elem.Title(nil, elem.Text("The Math Wizard")), + elem.Link(attrs.Props{attrs.Href: "https://cdn.jsdelivr.net/npm/nes.css@2.3.0/css/nes.min.css", attrs.Rel: "stylesheet"}), + elem.Link(attrs.Props{attrs.Rel: "stylesheet", attrs.Href: "https://cdn.jsdelivr.net/npm/bulma@0.9.4/css/bulma.min.css"}), + elem.Link(attrs.Props{attrs.Rel: "stylesheet", attrs.Href: "/static/styles/style.css"}), + elem.Script(attrs.Props{attrs.Src: "https://unpkg.com/scrollreveal@4.0.0/dist/scrollreveal.min.js"}), + elem.Style(nil, elem.Text("@import url('https://fonts.googleapis.com/css2?family=Press+Start+2P');")), + ) + return headContent +} diff --git a/views/index.templ.go b/views/index.templ.go new file mode 100644 index 0000000..7ac46c0 --- /dev/null +++ b/views/index.templ.go @@ -0,0 +1,24 @@ +package views + +import ( + "github.com/chasefleming/elem-go" + "github.com/chasefleming/elem-go/attrs" +) + +func RenderIndex() string { + headContent := RenderHead() + navContent := RenderNav() + mainContent := RenderMain() + footerContent := RenderFooter() + bodyContent := elem.Body(attrs.Props{attrs.Class: "is-boxed"}, + elem.Div(attrs.Props{attrs.Class: "body-wrap boxed-container"}, + navContent, + mainContent, + footerContent, + ), + ) + + htmlContent := elem.Html(nil, headContent, bodyContent) + + return htmlContent.Render() +} diff --git a/views/main.templ.go b/views/main.templ.go new file mode 100644 index 0000000..87e30f6 --- /dev/null +++ b/views/main.templ.go @@ -0,0 +1,94 @@ +package views + +import ( + "github.com/chasefleming/elem-go" + "github.com/chasefleming/elem-go/attrs" +) + +func RenderMain() elem.Node { + mainContent := elem.Main(nil, + elem.Section(attrs.Props{attrs.Class: "hero"}, + elem.Div(attrs.Props{attrs.Class: "container"}, + elem.Div(attrs.Props{attrs.Class: "hero-inner"}, + elem.Div(attrs.Props{attrs.Class: "hero-copy"}, + elem.H1(attrs.Props{attrs.Class: "hero-title"}, elem.Text("Automatisiere spielerisch das 1x1")), + elem.P(attrs.Props{attrs.Class: "hero-paragraph"}, elem.Text("Zeige was du kannst und kämpfe dich durch den Dungeon.")), + elem.Div(attrs.Props{attrs.Class: "hero-cta"}, + elem.A(attrs.Props{attrs.Class: "nes-btn is-primary", attrs.Href: "/login"}, elem.Text("Starte Jetzt!")), + ), + ), + ), + ), + ), + elem.Section(attrs.Props{attrs.Class: "features section"}, + elem.Div(attrs.Props{attrs.Class: "container"}, + elem.Div(attrs.Props{attrs.Class: "features-inner section-inner has-bottom-divider"}, + elem.Div(attrs.Props{attrs.Class: "features-header text-center"}, + elem.Div(attrs.Props{attrs.Class: "container-sm"}, + elem.H2(attrs.Props{attrs.Class: "section-title"}, elem.Text("The Math Wizard")), + elem.P(attrs.Props{attrs.Class: "section-paragraph"}, elem.Text("Ohne es zu bemerken lernst du das 1x1.")), + ), + ), + + elem.Div(attrs.Props{attrs.Class: "features-wrap"}, + elem.Div(attrs.Props{attrs.Class: "feature is-revealing"}, + elem.Div(attrs.Props{attrs.Class: "feature-inner"}, + elem.Div(attrs.Props{attrs.Class: "feature-icon"}, + elem.Img(attrs.Props{attrs.Src: "/static/images/weapon_sword_ruby.png", attrs.Alt: "Feature 01"})), + elem.Div(attrs.Props{attrs.Class: "feature-content"}, + elem.H3(attrs.Props{attrs.Class: "feature-title"}, elem.Text("Immer neue Level")), + elem.P(attrs.Props{attrs.Class: "text-sm mb-0"}, elem.Text("Spannende, zufallsgeneriete Level im Dungeon-Crawler-Stil.")), + ), + ), + ), + elem.Div(attrs.Props{attrs.Class: "feature is-revealing"}, + elem.Div(attrs.Props{attrs.Class: "feature-inner"}, + elem.Div(attrs.Props{attrs.Class: "feature-icon"}, + elem.Img(attrs.Props{attrs.Src: "/static/images/monster_imp.png", attrs.Alt: "Feature 02"})), + elem.Div(attrs.Props{attrs.Class: "feature-content"}, + elem.H3(attrs.Props{attrs.Class: "feature-title"}, elem.Text("Angepasste Schwierigkeit")), + elem.P(attrs.Props{attrs.Class: "text-sm mb-0"}, elem.Text("Die Aufgaben passen sich deinen aktuellen Fähigkeiten an.")), + ), + ), + ), + elem.Div(attrs.Props{attrs.Class: "feature is-revealing"}, + elem.Div(attrs.Props{attrs.Class: "feature-inner"}, + elem.Div(attrs.Props{attrs.Class: "feature-icon"}, + elem.Img(attrs.Props{attrs.Src: "/static/images/npc_knight_green.png", attrs.Alt: "Feature 03"})), + elem.Div(attrs.Props{attrs.Class: "feature-content"}, + elem.H3(attrs.Props{attrs.Class: "feature-title"}, elem.Text("Fokus auf das was zählt")), + elem.P(attrs.Props{attrs.Class: "text-sm mb-0"}, elem.Text("Fokus auf das Automatisieren der Multiplikation im Zahlenraum von 1 bis 10.")), + ), + ), + ), + elem.Div(attrs.Props{attrs.Class: "feature is-revealing"}, + elem.Div(attrs.Props{attrs.Class: "feature-inner"}, + elem.Div(attrs.Props{attrs.Class: "feature-icon"}, + elem.Img(attrs.Props{attrs.Src: "/static/images/monster_necromancer.png", attrs.Alt: "Feature 04"})), + elem.Div(attrs.Props{attrs.Class: "feature-content"}, + elem.H3(attrs.Props{attrs.Class: "feature-title"}, elem.Text("Lernfortschritt immer im Blick")), + elem.P(attrs.Props{attrs.Class: "text-sm mb-0"}, elem.Text("Lernziel und Fortschrittskontrolle, um deinen Fortschritt zu verfolgen.")), + ), + ), + ), + ), + ), + ), + ), + elem.Section(attrs.Props{attrs.Class: "cta section"}, + elem.Div(attrs.Props{attrs.Class: "container-sm"}, + elem.Div(attrs.Props{attrs.Class: "cta-inner section-inner"}, + elem.Div(attrs.Props{attrs.Class: "cta-header text-center"}, + elem.H2(attrs.Props{attrs.Class: "section-title"}, elem.Text("Bereit los zu legen?")), + elem.P(attrs.Props{attrs.Class: "section-paragraph"}, elem.Text("Fange noch heute an zu lernen.")), + elem.Div(attrs.Props{attrs.Class: "cta-cta"}, + elem.A(attrs.Props{attrs.Class: "nes-btn is-primary", attrs.Href: "/login"}, + elem.Text("Los geht's")), + ), + ), + ), + ), + ), + ) + return mainContent +} diff --git a/views/nav.templ.go b/views/nav.templ.go new file mode 100644 index 0000000..7a7d0de --- /dev/null +++ b/views/nav.templ.go @@ -0,0 +1,19 @@ +package views + +import ( + "github.com/chasefleming/elem-go" + "github.com/chasefleming/elem-go/attrs" +) + +func RenderNav() elem.Node { + navContent := elem.Header(attrs.Props{attrs.Class: "site-header"}, + elem.Div(attrs.Props{attrs.Class: "container"}, + elem.Nav(nil, + elem.Div(attrs.Props{attrs.Class: "site-header-inner"}, + elem.A(attrs.Props{attrs.Class: "", attrs.Href: "/"}), + ), + ), + ), + ) + return navContent +} diff --git a/views/profile.templ.go b/views/profile.templ.go new file mode 100644 index 0000000..d7a581c --- /dev/null +++ b/views/profile.templ.go @@ -0,0 +1,46 @@ +package views + +import ( + "fmt" + + "github.com/chasefleming/elem-go" + "github.com/chasefleming/elem-go/attrs" +) + +type Stats struct { + Username string +} + +func RenderProfile() elem.Node { + stats := Stats{ + Username: "TestUser", + } + page := elem.Div(attrs.Props{attrs.Class: "profile"}, + elem.Div(attrs.Props{attrs.Style: "padding: 20px; border-radius: 5px; box-shadow: 0 0 4px #433e4c; margin-top: 20px; margin-bottom: 20px; background-color: #2e1e26;"}, + elem.Div(attrs.Props{attrs.Class: "container3"}, + elem.Section(attrs.Props{attrs.Class: "message-list"}, + elem.Section(attrs.Props{attrs.Class: "message -left"}, + elem.I(attrs.Props{attrs.Class: "nes-bcrikko"}), + elem.Div(attrs.Props{attrs.Class: "nes-balloon from-left"}, + elem.P(nil, elem.Text(fmt.Sprintf("Hallo %s zurück. Wie kann ich dir helfen?", stats.Username))), + ), + ), + ), + elem.Div(attrs.Props{attrs.Class: "container3"}, + elem.Div(attrs.Props{attrs.Class: "text-center"}, + elem.H3(nil, elem.Text("Was möchtest du tun?"))), + ), + elem.Div(nil, + elem.A(attrs.Props{attrs.Class: "nes-btn", attrs.Href: "/learn"}, elem.Text("Lernen")), + elem.A(attrs.Props{attrs.Class: "nes-btn", attrs.Href: "/play"}, elem.Text("Spielen")), + ), + ), + elem.Div(attrs.Props{attrs.Class: "container3"}, + elem.Span(attrs.Props{attrs.Class: "title"}, elem.Text("Dein Fortschritt")), + elem.Span(nil, elem.Text("Du bist aktuell Leven: ")), + ), + ), + ) + + return page +}