Expert assistant for RoundTiming project - Go web app with Templ templates, TailwindCSS, and MySQL. Handles routing, auth, and component patterns.
Expert assistant for the RoundTiming project - a Go web application for tracking game rounds/timing using Templ templates, TailwindCSS, and MySQL.
Helps you develop and maintain the RoundTiming codebase by:
```bash
make live # Start dev server with hot reload (localhost:7331)
make db_start # Start database container
make db_stop # Stop database container
make migration_up # Run database migrations
make migration_down # Rollback last migration
make build/templ # Generate templ files
make build/tailwind # Build CSS
```
**Understand the Structure:**
**Steps:**
1. **Create the template** in `views/page/[domain]/`:
```go
package match
import (
"github.com/rousseau-romain/round-timing/views/components/layout"
"github.com/rousseau-romain/round-timing/views/components/ui"
)
templ MyPage(user *model.User) {
@layout.Layout(user, "Page Title") {
@ui.Container("padded") {
@ui.PageHeader() { My New Page }
<p>Content here</p>
}
}
}
```
2. **Create handler** in `handlers/`:
```go
func (h *Handler) HandlersMyPage(w http.ResponseWriter, r *http.Request) {
user := r.Context().Value("user").(*user.User)
component := match.MyPage(user)
component.Render(r.Context(), w)
}
```
3. **Register route** in appropriate `routes/[domain].go`:
```go
r.Handle("/my-page", middleware.RequireAuth(handler.HandlersMyPage, authService, logger)).Methods("GET")
```
4. **Build templates**:
```bash
make build/templ
```
**Use existing component patterns** from `views/components/ui/`:
**Button Example:**
```go
// Basic usage
@ui.Button("primary", "md") { Submit }
// With HTMX
@ui.ButtonAction("danger", "sm", templ.Attributes{
"hx-delete": "/item/123",
"hx-confirm": "Are you sure?",
}) { Delete }
```
**Table Example:**
```go
@ui.Table("default") {
@ui.TableHead("default") {
<tr>
@ui.Th() { Name }
@ui.Th() { Status }
@ui.ThEmpty()
</tr>
}
@ui.TableBody(templ.Attributes{"hx-target": "closest tr"}) {
for _, item := range items {
<tr>
@ui.TdPrimary() { {item.Name} }
@ui.Td() { {item.Status} }
@ui.TdAction() {
@ui.ButtonAction("outline", "sm", templ.Attributes{
"hx-get": fmt.Sprintf("/item/%d/edit", item.ID),
}) { Edit }
}
</tr>
}
}
}
```
**Form Example:**
```go
<form hx-post="/submit">
@forms.Input("text", "name", "name", "form.name", true)
@forms.Select("country", "country", "form.country", []forms.SelectOption{
{Value: "fr", Label: "France", Selected: true},
{Value: "us", Label: "USA"},
}, true)
@forms.Textarea("bio", "bio", "form.bio", 4, false)
@ui.Button("primary", "md") { Save }
</form>
```
**Follow domain organization** in `model/[domain]/`:
1. **Create model struct** in `model/[domain]/[model].go`:
```go
package match
import "time"
type MyModel struct {
ID int64 `db:"id"`
Name string `db:"name"`
CreatedAt time.Time `db:"created_at"`
}
```
2. **Add database connection** at top of file:
```go
var db = model.DB
```
3. **Create query methods**:
```go
func GetMyModelByID(id int64) (*MyModel, error) {
sb := sqlbuilder.NewSelectBuilder()
sb.Select("*").From("my_models").Where(sb.Equal("id", id))
sql, args := sb.Build()
var m MyModel
err := db.Get(&m, sql, args...)
return &m, err
}
func CreateMyModel(name string) (*MyModel, error) {
ib := sqlbuilder.NewInsertBuilder()
ib.InsertInto("my_models").Cols("name").Values(name)
sql, args := ib.Build()
result, err := db.Exec(sql, args...)
if err != nil {
return nil, err
}
id, _ := result.LastInsertId()
return GetMyModelByID(id)
}
```
**Use middleware from `middleware/auth.go`:**
```go
// Public pages (optional auth)
r.Handle("/", middleware.AllowToBeAuth(handler.HandlersHome, authService, logger))
// Require login
r.Handle("/dashboard", middleware.RequireAuth(handler.HandlersDashboard, authService, logger))
// Require admin role
r.Handle("/admin", middleware.RequireAuthAndAdmin(handler.HandlersAdmin, authService, logger))
// Require match ownership
r.Handle("/match/{matchId}/edit", middleware.RequireAuthAndHisMatch(handler.HandlersEditMatch, authService, logger))
// Block authenticated users (signin/signup)
r.Handle("/signin", middleware.RequireNotAuth(handler.HandlersSignIn, authService, logger))
```
**Access user in handlers:**
```go
func (h *Handler) HandlersMyPage(w http.ResponseWriter, r *http.Request) {
user := r.Context().Value("user").(*user.User)
// User is guaranteed to exist if RequireAuth middleware passed
}
```
**In templates:**
```go
// Use i18n keys (resolved server-side)
@ui.Button("primary", "md") { @templ.Raw(i18n.T(locale, "form.submit")) }
// Or pass translated strings from handler
```
**In handlers:**
```go
locale := lang.GetPreferred(r)
message := i18n.T(locale, "error.not_found")
```
**Follow project conventions:**
**Color variants:**
```go
// Buttons: primary, success, danger, outline, indigo, black
@ui.Button("primary", "md")
// Badges: red, yellow, cyan, green, indigo, gray
@ui.Badge("red")
// Cards: default, bordered
@ui.Card("default")
// Team-colored cards: red, indigo
@ui.CardColor("red")
```
**Common patterns:**
```go
// Swap content
<div hx-get="/data" hx-target="#result" hx-swap="innerHTML">Load</div>
// Form submission
<form hx-post="/save" hx-target="this" hx-swap="outerHTML">
@forms.Input(...)
@ui.Button("primary", "md") { Save }
</form>
// Delete with confirmation
@ui.ButtonAction("danger", "sm", templ.Attributes{
"hx-delete": "/item/123",
"hx-confirm": "Delete this item?",
"hx-target": "closest tr",
"hx-swap": "outerHTML",
}) { Delete }
```
**Branches:**
**Deploy to staging:**
```bash
git checkout staging
git merge master
git push origin staging
```
1. **Forgetting to rebuild templates** - Run `make build/templ` or use `make live`
2. **Wrong middleware chain** - Use correct middleware for auth requirements
3. **Cross-package model references** - Import and alias properly
4. **Missing database connection** - Add `var db = model.DB` in model files
5. **Hardcoded strings** - Use i18n keys for user-facing text
Leave a review
No reviews yet. Be the first to review this skill!
# Download SKILL.md from killerskills.ai/api/skills/roundtiming-gotempl-development-assistant/raw