Compare commits

..

No commits in common. "9fd841229b539d452a1d016579bd0cdf203a087d" and "708722865d7599c4ee487aeeda3eb5a4315dcdee" have entirely different histories.

16 changed files with 91 additions and 381 deletions

12
go.mod
View file

@ -1,13 +1,5 @@
module tms module tms
go 1.23 go 1.22.0
toolchain go1.23.6 require github.com/mattn/go-sqlite3 v1.14.24 // indirect
require golang.org/x/crypto v0.33.0
require (
github.com/gorilla/securecookie v1.1.2 // indirect
github.com/gorilla/sessions v1.4.0 // indirect
github.com/mattn/go-sqlite3 v1.14.24 // indirect
)

6
go.sum
View file

@ -1,8 +1,2 @@
github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA=
github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo=
github.com/gorilla/sessions v1.4.0 h1:kpIYOp/oi6MG/p5PgxApU8srsSw9tuFbt46Lt7auzqQ=
github.com/gorilla/sessions v1.4.0/go.mod h1:FLWm50oby91+hl7p/wRxDth9bWSuk0qVL2emc7lT5ik=
github.com/mattn/go-sqlite3 v1.14.24 h1:tpSp2G2KyMnnQu99ngJ47EIkWVmliIizyZBfPrBWDRM= github.com/mattn/go-sqlite3 v1.14.24 h1:tpSp2G2KyMnnQu99ngJ47EIkWVmliIizyZBfPrBWDRM=
github.com/mattn/go-sqlite3 v1.14.24/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/mattn/go-sqlite3 v1.14.24/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus=
golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=

260
main.go
View file

@ -5,13 +5,9 @@ import (
"embed" "embed"
"fmt" "fmt"
"html/template" "html/template"
"log"
"net/http" "net/http"
"time" "time"
"github.com/gorilla/sessions"
"golang.org/x/crypto/bcrypt"
_ "github.com/mattn/go-sqlite3" _ "github.com/mattn/go-sqlite3"
) )
@ -21,9 +17,6 @@ var templatesFS embed.FS
//go:embed schema.sql //go:embed schema.sql
var schemaSQL string var schemaSQL string
// Store for sessions (adjust the key to something secure)
var store = sessions.NewCookieStore([]byte("your-secret-key"))
type Trainer struct { type Trainer struct {
ID int ID int
Name string Name string
@ -69,15 +62,13 @@ func initDB() {
var err error var err error
db, err = sql.Open("sqlite3", "training.db") db, err = sql.Open("sqlite3", "training.db")
if err != nil { if err != nil {
log.Fatalf("Error opening database: %v", err) panic(err)
} }
_, err = db.Exec(schemaSQL) _, err = db.Exec(schemaSQL)
if err != nil { if err != nil {
log.Fatalf("Error initializing database schema: %v", err) panic(err)
} }
log.Println("Database initialized successfully")
} }
var tmpl *template.Template var tmpl *template.Template
@ -86,33 +77,26 @@ func initTemplates() {
var err error var err error
tmpl, err = template.ParseFS(templatesFS, "templates/*.html") tmpl, err = template.ParseFS(templatesFS, "templates/*.html")
if err != nil { if err != nil {
log.Fatalf("Error parsing templates: %v", err) panic(fmt.Sprintf("Error parsing templates: %v", err))
} }
log.Println("Templates loaded successfully")
} }
func homeHandler(w http.ResponseWriter, r *http.Request) { func homeHandler(w http.ResponseWriter, r *http.Request) {
log.Println("Handling home page request")
err := tmpl.ExecuteTemplate(w, "home.html", nil) err := tmpl.ExecuteTemplate(w, "home.html", nil)
if err != nil { if err != nil {
log.Printf("Error in templating page home.html: %v", err)
http.Error(w, fmt.Sprintf("Error in templating page %v", err), http.StatusInternalServerError) http.Error(w, fmt.Sprintf("Error in templating page %v", err), http.StatusInternalServerError)
} }
} }
func trainingAreaListHandler(w http.ResponseWriter, r *http.Request) { func trainingAreaListHandler(w http.ResponseWriter, r *http.Request) {
log.Println("Fetching training areas and trainers")
areas, err := fetchTrainingAreasWithTrainers() areas, err := fetchTrainingAreasWithTrainers()
if err != nil { if err != nil {
log.Printf("Error fetching training areas: %v", err)
http.Error(w, "Database error", http.StatusInternalServerError) http.Error(w, "Database error", http.StatusInternalServerError)
return return
} }
trainers, err := fetchTrainers() trainers, err := fetchTrainers()
if err != nil { if err != nil {
log.Printf("Error fetching trainers: %v", err)
http.Error(w, "Database error", http.StatusInternalServerError) http.Error(w, "Database error", http.StatusInternalServerError)
return return
} }
@ -127,16 +111,12 @@ func trainingAreaListHandler(w http.ResponseWriter, r *http.Request) {
err = tmpl.ExecuteTemplate(w, "training_area_list.html", data) err = tmpl.ExecuteTemplate(w, "training_area_list.html", data)
if err != nil { if err != nil {
log.Printf("Error in templating page training_area_list.html: %v", err)
http.Error(w, fmt.Sprintf("Error in templating page %v", err), http.StatusInternalServerError) http.Error(w, fmt.Sprintf("Error in templating page %v", err), http.StatusInternalServerError)
return return
} }
log.Println("Training areas and trainers displayed successfully")
} }
func fetchTrainingAreasWithTrainers() ([]TrainingArea, error) { func fetchTrainingAreasWithTrainers() ([]TrainingArea, error) {
log.Println("Executing query to fetch training areas with trainers")
rows, err := db.Query(` rows, err := db.Query(`
SELECT ta.id, ta.name, t.id, t.name, t.email SELECT ta.id, ta.name, t.id, t.name, t.email
FROM training_areas ta FROM training_areas ta
@ -144,7 +124,6 @@ func fetchTrainingAreasWithTrainers() ([]TrainingArea, error) {
LEFT JOIN trainers t ON tta.trainer_id = t.id LEFT JOIN trainers t ON tta.trainer_id = t.id
ORDER BY ta.id, t.name`) ORDER BY ta.id, t.name`)
if err != nil { if err != nil {
log.Printf("Database error while fetching training areas: %v", err)
return nil, err return nil, err
} }
defer rows.Close() defer rows.Close()
@ -160,7 +139,6 @@ func fetchTrainingAreasWithTrainers() ([]TrainingArea, error) {
err := rows.Scan(&areaID, &areaName, &trainerID, &trainerName, &trainerEmail) err := rows.Scan(&areaID, &areaName, &trainerID, &trainerName, &trainerEmail)
if err != nil { if err != nil {
log.Printf("Error scanning row for training area: %v", err)
return nil, err return nil, err
} }
@ -184,67 +162,55 @@ func fetchTrainingAreasWithTrainers() ([]TrainingArea, error) {
areas = append(areas, *area) areas = append(areas, *area)
} }
log.Printf("Found %d training areas", len(areas))
return areas, nil return areas, nil
} }
func assignTrainerToAreaHandler(w http.ResponseWriter, r *http.Request) { func assignTrainerToAreaHandler(w http.ResponseWriter, r *http.Request) {
log.Println("Handling trainer assignment to training area")
if r.Method == http.MethodPost { if r.Method == http.MethodPost {
trainerID := r.FormValue("trainer_id") trainerID := r.FormValue("trainer_id")
areaID := r.FormValue("training_area_id") areaID := r.FormValue("training_area_id")
if trainerID == "" || areaID == "" { if trainerID == "" || areaID == "" {
log.Println("Missing trainer or training area ID")
http.Error(w, "Trainer and Training Area are required", http.StatusBadRequest) http.Error(w, "Trainer and Training Area are required", http.StatusBadRequest)
return return
} }
_, err := db.Exec("INSERT OR IGNORE INTO trainer_training_areas (trainer_id, training_area_id) VALUES (?, ?)", trainerID, areaID) _, err := db.Exec("INSERT OR IGNORE INTO trainer_training_areas (trainer_id, training_area_id) VALUES (?, ?)", trainerID, areaID)
if err != nil { if err != nil {
log.Printf("Error assigning trainer to area: %v", err)
http.Error(w, "Database error", http.StatusInternalServerError) http.Error(w, "Database error", http.StatusInternalServerError)
return return
} }
log.Println("Trainer assigned to area successfully")
http.Redirect(w, r, "/training-areas", http.StatusSeeOther) http.Redirect(w, r, "/training-areas", http.StatusSeeOther)
} }
} }
func addTrainingAreaHandler(w http.ResponseWriter, r *http.Request) { func addTrainingAreaHandler(w http.ResponseWriter, r *http.Request) {
log.Println("Handling request to add a new training area")
if r.Method == http.MethodPost { if r.Method == http.MethodPost {
name := r.FormValue("name") name := r.FormValue("name")
if name == "" { if name == "" {
log.Println("Training area name is required")
http.Error(w, "Name is required", http.StatusBadRequest) http.Error(w, "Name is required", http.StatusBadRequest)
return return
} }
_, err := db.Exec("INSERT INTO training_areas (name) VALUES (?)", name) _, err := db.Exec("INSERT INTO training_areas (name) VALUES (?)", name)
if err != nil { if err != nil {
log.Printf("Error adding training area: %v", err)
http.Error(w, "Database error", http.StatusInternalServerError) http.Error(w, "Database error", http.StatusInternalServerError)
return return
} }
log.Println("New training area added successfully")
http.Redirect(w, r, "/training-areas", http.StatusSeeOther) http.Redirect(w, r, "/training-areas", http.StatusSeeOther)
return return
} }
err := tmpl.ExecuteTemplate(w, "add_training_area.html", nil) err := tmpl.ExecuteTemplate(w, "add_training_area.html", nil)
if err != nil { if err != nil {
log.Printf("Error in templating page add_training_area.html: %v", err)
http.Error(w, fmt.Sprintf("Error in templating page %v", err), http.StatusInternalServerError) http.Error(w, fmt.Sprintf("Error in templating page %v", err), http.StatusInternalServerError)
} }
} }
func fetchTrainings() ([]Training, error) { func fetchTrainings() ([]Training, error) {
log.Println("Fetching trainings from the database")
rows, err := db.Query("SELECT id, title, training_type, mode, start, end, status FROM trainings") rows, err := db.Query("SELECT id, title, training_type, mode, start, end, status FROM trainings")
if err != nil { if err != nil {
log.Printf("Error fetching trainings: %v", err)
return nil, err return nil, err
} }
defer rows.Close() defer rows.Close()
@ -255,39 +221,31 @@ func fetchTrainings() ([]Training, error) {
var start, end string var start, end string
err := rows.Scan(&t.ID, &t.Title, &t.TrainingType, &t.Mode, &start, &end, &t.Status) err := rows.Scan(&t.ID, &t.Title, &t.TrainingType, &t.Mode, &start, &end, &t.Status)
if err != nil { if err != nil {
log.Printf("Error scanning row for training: %v", err)
return nil, err return nil, err
} }
t.Start, _ = time.Parse("2006-01-02 15:04:05", start) t.Start, _ = time.Parse("2006-01-02 15:04:05", start)
t.End, _ = time.Parse("2006-01-02 15:04:05", end) t.End, _ = time.Parse("2006-01-02 15:04:05", end)
trainings = append(trainings, t) trainings = append(trainings, t)
} }
log.Printf("Found %d trainings", len(trainings))
return trainings, nil return trainings, nil
} }
func trainingListHandler(w http.ResponseWriter, r *http.Request) { func trainingListHandler(w http.ResponseWriter, r *http.Request) {
log.Println("Handling training list request")
trainings, err := fetchTrainings() trainings, err := fetchTrainings()
if err != nil { if err != nil {
log.Printf("Error fetching trainings: %v", err)
http.Error(w, "Database error", http.StatusInternalServerError) http.Error(w, "Database error", http.StatusInternalServerError)
return return
} }
err = tmpl.ExecuteTemplate(w, "training_list.html", trainings) err = tmpl.ExecuteTemplate(w, "training_list.html", trainings)
if err != nil { if err != nil {
log.Printf("Error in templating page training_list.html: %v", err)
http.Error(w, fmt.Sprintf("Error in templating page %v", err), http.StatusInternalServerError) http.Error(w, fmt.Sprintf("Error in templating page %v", err), http.StatusInternalServerError)
} }
} }
func fetchTrainers() ([]Trainer, error) { func fetchTrainers() ([]Trainer, error) {
log.Println("Fetching trainers from the database")
rows, err := db.Query("SELECT id, name, email FROM trainers") rows, err := db.Query("SELECT id, name, email FROM trainers")
if err != nil { if err != nil {
log.Printf("Error fetching trainers: %v", err)
return nil, err return nil, err
} }
defer rows.Close() defer rows.Close()
@ -297,256 +255,62 @@ func fetchTrainers() ([]Trainer, error) {
var t Trainer var t Trainer
err := rows.Scan(&t.ID, &t.Name, &t.Email) err := rows.Scan(&t.ID, &t.Name, &t.Email)
if err != nil { if err != nil {
log.Printf("Error scanning row for trainer: %v", err)
return nil, err return nil, err
} }
trainers = append(trainers, t) trainers = append(trainers, t)
} }
log.Printf("Found %d trainers", len(trainers))
return trainers, nil return trainers, nil
} }
func trainerListHandler(w http.ResponseWriter, r *http.Request) { func trainerListHandler(w http.ResponseWriter, r *http.Request) {
log.Println("Handling trainer list request")
trainers, err := fetchTrainers() trainers, err := fetchTrainers()
if err != nil { if err != nil {
log.Printf("Error fetching trainers: %v", err)
http.Error(w, "Database error", http.StatusInternalServerError) http.Error(w, "Database error", http.StatusInternalServerError)
return return
} }
err = tmpl.ExecuteTemplate(w, "trainer_list.html", trainers) err = tmpl.ExecuteTemplate(w, "trainer_list.html", trainers)
if err != nil { if err != nil {
log.Printf("Error in templating page trainer_list.html: %v", err)
http.Error(w, fmt.Sprintf("Error in templating page %v", err), http.StatusInternalServerError) http.Error(w, fmt.Sprintf("Error in templating page %v", err), http.StatusInternalServerError)
} }
} }
func addTrainerHandler(w http.ResponseWriter, r *http.Request) { func addTrainerHandler(w http.ResponseWriter, r *http.Request) {
log.Println("Handling request to add a new trainer")
if r.Method == http.MethodPost { if r.Method == http.MethodPost {
name := r.FormValue("name") name := r.FormValue("name")
email := r.FormValue("email") email := r.FormValue("email")
if name == "" || email == "" { if name == "" || email == "" {
log.Println("Trainer name and email are required")
http.Error(w, "Name and Email are required", http.StatusBadRequest) http.Error(w, "Name and Email are required", http.StatusBadRequest)
return return
} }
_, err := db.Exec("INSERT INTO trainers (name, email) VALUES (?, ?)", name, email) _, err := db.Exec("INSERT INTO trainers (name, email) VALUES (?, ?)", name, email)
if err != nil { if err != nil {
log.Printf("Error adding trainer: %v", err)
http.Error(w, "Database error", http.StatusInternalServerError) http.Error(w, "Database error", http.StatusInternalServerError)
return return
} }
log.Println("New trainer added successfully")
http.Redirect(w, r, "/trainers", http.StatusSeeOther) http.Redirect(w, r, "/trainers", http.StatusSeeOther)
return return
} }
err := tmpl.ExecuteTemplate(w, "add_trainer.html", nil) err := tmpl.ExecuteTemplate(w, "add_trainer.html", nil)
if err != nil { if err != nil {
log.Printf("Error in templating page add_trainer.html: %v", err)
http.Error(w, fmt.Sprintf("Error in templating page %v", err), http.StatusInternalServerError) http.Error(w, fmt.Sprintf("Error in templating page %v", err), http.StatusInternalServerError)
} }
}
func hashPassword(password string) (string, error) {
log.Println("Hashing password")
bytes, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
return string(bytes), err
}
func setupAdminHandler(w http.ResponseWriter, r *http.Request) {
// Check if an admin exists
var count int
err := db.QueryRow("SELECT COUNT(*) FROM admins").Scan(&count)
if err != nil {
http.Error(w, "Database error", http.StatusInternalServerError)
return
}
// If an admin exists, redirect to login
if count > 0 {
http.Redirect(w, r, "/login", http.StatusSeeOther)
return
}
// If it's a POST request, create the admin
if r.Method == http.MethodPost {
name := r.FormValue("name")
email := r.FormValue("email")
password := r.FormValue("password")
if email == "" || password == "" || name == "" {
http.Error(w, "Name, email and password are required", http.StatusBadRequest)
return
}
// Hash the password
hashedPassword, err := hashPassword(password)
if err != nil {
http.Error(w, "Error hashing password", http.StatusInternalServerError)
return
}
// Insert the admin into the database
_, err = db.Exec("INSERT INTO admins (name, email, password) VALUES (?, ?, ?)", name, email, hashedPassword)
if err != nil {
http.Error(w, "Error saving admin", http.StatusInternalServerError)
return
}
// Redirect to login after setup
http.Redirect(w, r, "/login", http.StatusSeeOther)
return
}
// Show the admin setup page
err = tmpl.ExecuteTemplate(w, "setup_admin.html", nil)
if err != nil {
http.Error(w, "Template rendering error", http.StatusInternalServerError)
}
}
func redirectIfNoAdmin(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
var count int
err := db.QueryRow("SELECT COUNT(*) FROM admins").Scan(&count)
if err != nil || count == 0 {
http.Redirect(w, r, "/setup-admin", http.StatusSeeOther)
return
}
session, err := store.Get(r, "user-session")
if err != nil || session.Values["username"] == nil {
http.Redirect(w, r, "/login", http.StatusSeeOther)
return
}
next.ServeHTTP(w, r)
})
}
func checkPasswordHash(password, hash string) bool {
err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password))
return err == nil
}
// / loginHandler handles the user login
func loginHandler(w http.ResponseWriter, r *http.Request) {
if r.Method == http.MethodGet {
// Show the login page
log.Println("Rendering login page.")
err := tmpl.ExecuteTemplate(w, "login.html", nil)
if err != nil {
log.Println("Error rendering login template:", err)
http.Error(w, "Template rendering error", http.StatusInternalServerError)
}
return
}
if r.Method == http.MethodPost {
username := r.FormValue("username")
password := r.FormValue("password")
log.Printf("Received login attempt for username: %s", username)
// Query the admin table for the password hash
var storedHash string
err := db.QueryRow("SELECT password FROM admins WHERE email = $1", username).Scan(&storedHash)
if err != nil {
if err == sql.ErrNoRows {
// User doesn't exist
log.Println("Invalid username or password: no matching user found.")
http.Error(w, "Invalid username or password", http.StatusUnauthorized)
} else {
// Database error
log.Println("Error querying database:", err)
http.Error(w, "Database error", http.StatusInternalServerError)
}
return
}
// Check if the provided password matches the stored hash
if !checkPasswordHash(password, storedHash) {
log.Println("Invalid username or password: password mismatch.")
http.Error(w, "Invalid username or password", http.StatusUnauthorized)
return
}
// Password is correct; set up a session
session, err := store.Get(r, "user-session")
if err != nil {
log.Println("Error creating session:", err)
http.Error(w, "Internal server error", http.StatusInternalServerError)
return
}
// Store username in the session
session.Values["username"] = username
err = session.Save(r, w)
if err != nil {
log.Println("Error saving session:", err)
http.Error(w, "Internal server error", http.StatusInternalServerError)
return
}
log.Printf("User '%s' successfully logged in.", username)
// Redirect to the home page (or dashboard)
http.Redirect(w, r, "/", http.StatusSeeOther)
}
}
// logoutHandler logs the user out by destroying their session
func logoutHandler(w http.ResponseWriter, r *http.Request) {
log.Println("Logging out user.")
session, err := store.Get(r, "user-session")
if err != nil {
log.Println("Error fetching session:", err)
http.Error(w, "Internal server error", http.StatusInternalServerError)
return
}
// Clear the session
session.Options.MaxAge = -1
err = session.Save(r, w)
if err != nil {
log.Println("Error saving session:", err)
http.Error(w, "Internal server error", http.StatusInternalServerError)
return
}
log.Println("User successfully logged out.")
// Redirect to the login page or home page
http.Redirect(w, r, "/login", http.StatusSeeOther)
}
func initServerHandlers() {
http.Handle("/", redirectIfNoAdmin(http.HandlerFunc(homeHandler)))
http.Handle("/trainings", redirectIfNoAdmin(http.HandlerFunc(trainingListHandler)))
http.Handle("/trainers", redirectIfNoAdmin(http.HandlerFunc(trainerListHandler)))
http.Handle("/trainers/add", redirectIfNoAdmin(http.HandlerFunc(addTrainerHandler)))
http.Handle("/training-areas", redirectIfNoAdmin(http.HandlerFunc(trainingAreaListHandler)))
http.Handle("/training-areas/add", redirectIfNoAdmin(http.HandlerFunc(addTrainingAreaHandler)))
http.Handle("/training-areas/assign-trainer", redirectIfNoAdmin(http.HandlerFunc(assignTrainerToAreaHandler)))
http.HandleFunc("/setup-admin", setupAdminHandler)
http.HandleFunc("/login", loginHandler)
http.HandleFunc("/logout", logoutHandler)
} }
func main() { func main() {
initDB() initDB()
initTemplates() initTemplates()
initServerHandlers() http.HandleFunc("/", homeHandler)
http.HandleFunc("/trainings", trainingListHandler)
http.HandleFunc("/trainers", trainerListHandler)
http.HandleFunc("/trainers/add", addTrainerHandler)
http.HandleFunc("/training-areas", trainingAreaListHandler)
http.HandleFunc("/training-areas/add", addTrainingAreaHandler)
http.HandleFunc("/training-areas/assign-trainer", assignTrainerToAreaHandler)
log.Println("Starting server on :8080") fmt.Println("Server running on http://localhost:8080")
err := http.ListenAndServe(":8080", nil) http.ListenAndServe(":8080", nil)
if err != nil {
log.Fatalf("Error starting server: %v", err)
}
} }

View file

@ -42,10 +42,3 @@ CREATE TABLE IF NOT EXISTS training_training_areas (
FOREIGN KEY (training_id) REFERENCES trainings(id), FOREIGN KEY (training_id) REFERENCES trainings(id),
FOREIGN KEY (training_area_id) REFERENCES training_areas(id) FOREIGN KEY (training_area_id) REFERENCES training_areas(id)
); );
CREATE TABLE IF NOT EXISTS admins (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
email TEXT UNIQUE NOT NULL,
password TEXT NOT NULL
);

View file

@ -1,12 +1,19 @@
{{ template "preamble.html" }} <!DOCTYPE html>
<h1>Add Trainer</h1> <html>
<form action="/trainers/add" method="POST"> {{template "head.html" .}} <!-- Include header template -->
<label for="name">Name:</label>
<input type="text" id="name" name="name" required><br><br>
<label for="email">Email:</label> <body>
<input type="email" id="email" name="email" required><br><br> {{template "header.html" .}}
<h1>Add Trainer</h1>
<a href="/">Back to Home</a>
<form action="/trainers/add" method="POST">
<label for="name">Name:</label>
<input type="text" id="name" name="name" required><br><br>
<button type="submit">Add Trainer</button> <label for="email">Email:</label>
</form> <input type="email" id="email" name="email" required><br><br>
{{template "epilogue.html" }}
<button type="submit">Add Trainer</button>
</form>
</body>
</html>

View file

@ -1,8 +1,18 @@
{{ template "preamble.html" }} <!DOCTYPE html>
<html>
{{template "head.html" .}} <!-- Include header template -->
<body>
{{template "header.html" .}}
<h1>Add New Training Area</h1> <h1>Add New Training Area</h1>
<form action="/training-areas/add" method="post"> <form action="/training-areas/add" method="post">
<label for="name">Training Area Name:</label> <label for="name">Training Area Name:</label>
<input type="text" id="name" name="name" required> <input type="text" id="name" name="name" required>
<button type="submit">Add</button> <button type="submit">Add</button>
</form> </form>
{{template "epilogue.html" }} <br>
<a href="/training-areas">Back to Training Areas</a>
<br><br>
<a href="/">Back to Home</a>
</body>
</html>

View file

@ -12,53 +12,19 @@
header { header {
background-color: #06041f; background-color: #06041f;
color: #fff;
padding: 20px; padding: 20px;
text-align: center; text-align: center;
} }
.header-content { header img {
display: flex; width: 150px;
justify-content: space-between;
align-items: center;
max-width: 1200px;
margin: 0 auto;
}
.logo {
width: 50px;
height: auto; height: auto;
} }
header h1 { header h1 {
flex-grow: 1;
margin: 0;
font-size: 2em;
color: #47d7ac; color: #47d7ac;
} }
.nav-buttons a {
text-decoration: none;
margin: 0 10px;
padding: 10px 20px;
background-color: #47d7ac;
color: #fff;
border-radius: 5px;
font-weight: bold;
transition: background-color 0.3s ease;
display: inline-flex;
align-items: center;
justify-content: center;
}
.nav-buttons a:hover {
background-color: #35a895;
}
.nav-buttons i {
margin-right: 8px;
}
/* home */ /* home */
.tiles ul { .tiles ul {
list-style-type: none; list-style-type: none;
@ -122,7 +88,7 @@
button { button {
background-color: #47d7ac; background-color: #47d7ac;
color: #06041f; color: #fff;
padding: 8px 16px; padding: 8px 16px;
border: none; border: none;
cursor: pointer; cursor: pointer;
@ -132,9 +98,4 @@
button:hover { button:hover {
background-color: #35a895; background-color: #35a895;
} }
.main {
margin-left: 20px;
margin-right: 20px;
}
</style> </style>

View file

@ -1,3 +0,0 @@
</div>
</body>
</html>

View file

@ -1,10 +1,4 @@
<header> <header>
<div class="header-content"> <img src="logo.png" alt="Training Management Logo">
<img src="logo.png" alt="Training Management Logo" class="logo"> <h1>Nagarro Training Solutions</h1>
<h1>Nagarro Training Solutions</h1>
<nav class="nav-buttons">
<a href="/" class="btn"><i class="fas fa-home"></i> Home</a>
<a href="/logout" class="btn"><i class="fas fa-sign-out-alt"></i> Logout</a>
</nav>
</div>
</header> </header>

View file

@ -1,4 +1,9 @@
{{ template "preamble.html" }} <!DOCTYPE html>
<html>
{{template "head.html" .}} <!-- Include header template -->
<body>
{{template "header.html" .}}
<h1>Welcome to the Training Management System</h1> <h1>Welcome to the Training Management System</h1>
<div class="tiles"> <div class="tiles">
<ul> <ul>
@ -24,4 +29,5 @@
</li> </li>
</ul> </ul>
</div> </div>
{{template "epilogue.html" }} </body>
</html>

View file

@ -1,12 +0,0 @@
{{ template "preamble.html" }}
<h2>Please log in to continue</h2>
<form action="/login" method="POST">
<label for="username">Username:</label>
<input type="text" id="username" name="username" required><br><br>
<label for="password">Password:</label>
<input type="password" id="password" name="password" required><br><br>
<button type="submit">Login</button>
</form>
{{template "epilogue.html" }}

View file

@ -1,7 +0,0 @@
<!DOCTYPE html>
<html>
{{template "head.html" .}} <!-- Include header template -->
<body>
{{template "header.html" .}}
<div class="main">

View file

@ -1,14 +0,0 @@
{{ template "preamble.html" }}
<h2>Setup Admin Account</h2>
<form method="post">
<label>Name:</label>
<input type="text", name="name" required><br>
<label>Email:</label>
<input type="email" name="email" required><br>
<label>Password:</label>
<input type="password" name="password" required><br>
<button type="submit">Create Admin</button>
</form>
{{template "epilogue.html" }}

View file

@ -1,6 +1,13 @@
{{ template "preamble.html" }} <!DOCTYPE html>
<html>
{{template "head.html" .}} <!-- Include header template -->
<body>
{{template "header.html" .}}
<h1>Trainers</h1> <h1>Trainers</h1>
<a href="/trainers/add"><button>Add Trainer</button></a> <a href="/">Back to Home</a>
<a href="/trainers/add">Add Trainer</a>
<table border="1"> <table border="1">
<tr> <tr>
<th>Name</th> <th>Name</th>
@ -13,4 +20,5 @@
</tr> </tr>
{{ end }} {{ end }}
</table> </table>
{{template "epilogue.html" }} </body>
</html>

View file

@ -1,6 +1,12 @@
{{ template "preamble.html" }} <!DOCTYPE html>
<html>
{{template "head.html" .}} <!-- Include header template -->
<body>
{{template "header.html" .}}
<h1>Training Areas</h1> <h1>Training Areas</h1>
<a href="/training-areas/add"><button>Add Training Area</button></a> <a href="/training-areas/add">Add Training Area</a>
<table> <table>
<thead> <thead>
@ -28,4 +34,8 @@
{{end}} {{end}}
</tbody> </tbody>
</table> </table>
{{template "epilogue.html" }}
<a href="/">Back to Home</a>
</body>
</html>

View file

@ -1,5 +1,11 @@
{{ template "preamble.html" }} <!DOCTYPE html>
<html>
{{template "head.html" .}} <!-- Include header template -->
<body>
{{template "header.html" .}}
<h1>Trainings</h1> <h1>Trainings</h1>
<a href="/">Back to Home</a>
<table border="1"> <table border="1">
<tr> <tr>
<th>Title</th> <th>Title</th>
@ -20,4 +26,5 @@
</tr> </tr>
{{ end }} {{ end }}
</table> </table>
{{template "epilogue.html" }} </body>
</html>