jamtrack/migrations/1700000000_init.go
Frédéric G. MARAND 6f3321371d bug(ux): fix various front end bugs.
- Fixed song add form (3 HTMX bugs: reset on typeahead, wrong query param, artist-only find-or-create)
- Added Played checkbox to add form; inline edit (✎/Save/Cancel) for setlist rows
- View fixes: date format, location name instead of ID in jam list, notes on location detail, song count column, location name links to detail
- Title typeahead + cross-filtering (artist↔title filter each other)
- Fixed datalist accumulation bug: HTMX 2.0 inherits hx-swap from parent; added explicit hx-swap="innerHTML" on both inputs
- Setlist sort by id (ULID = insertion order); created rejected by PocketBase
- Location delete guard: OnRecordDelete hook blocks if jams exist; FK enforcement is app-level not SQLite-level

Loose ends to pick up:
- Focus-after-add not user-confirmed yet (setTimeout approach)
- jams.participants and setlist.position still in DB schema but unused in UI (optional migration to clean up)
- Logout flow and dashboard ranking not browser-tested
2026-06-15 14:12:57 +02:00

109 lines
3.5 KiB
Go

// Package migrations contains PocketBase schema migrations for jamtrack.
package migrations
import (
"github.com/pocketbase/pocketbase/core"
)
func init() {
core.AppMigrations.Register(func(app core.App) error {
// locations — domain table for jam venues
locations := core.NewBaseCollection("locations")
locations.Fields.Add(&core.TextField{Name: "name", Required: true})
locations.Fields.Add(&core.TextField{Name: "notes"})
locations.AddIndex("idx_locations_name", true, "name", "")
if err := app.Save(locations); err != nil {
return err
}
// songs — catalog of all songs ever seen on a board
songs := core.NewBaseCollection("songs")
songs.Fields.Add(&core.TextField{Name: "title", Required: true})
songs.Fields.Add(&core.TextField{Name: "artist", Required: true})
songs.Fields.Add(&core.TextField{Name: "notes"})
songs.AddIndex("idx_songs_artist_title", true, "artist,title", "")
if err := app.Save(songs); err != nil {
return err
}
// jams — one record per jam session
jams := core.NewBaseCollection("jams")
jams.Fields.Add(&core.DateField{Name: "date", Required: true})
jams.Fields.Add(&core.RelationField{
Name: "location",
Required: true,
CollectionId: locations.Id,
MaxSelect: 1,
})
jams.Fields.Add(&core.TextField{Name: "participants"})
jams.Fields.Add(&core.FileField{
Name: "board_photo",
MaxSelect: 1,
MimeTypes: []string{"image/jpeg", "image/png", "image/gif", "image/webp"},
})
jams.Fields.Add(&core.TextField{Name: "notes"})
if err := app.Save(jams); err != nil {
return err
}
// setlist — junction: one row per song proposed at a jam
setlist := core.NewBaseCollection("setlist")
setlist.Fields.Add(&core.RelationField{
Name: "jam",
Required: true,
CollectionId: jams.Id,
MaxSelect: 1,
CascadeDelete: true,
})
setlist.Fields.Add(&core.RelationField{
Name: "song",
Required: true,
CollectionId: songs.Id,
MaxSelect: 1,
})
setlist.Fields.Add(&core.BoolField{Name: "played"})
setlist.Fields.Add(&core.NumberField{Name: "position"})
setlist.AddIndex("idx_setlist_jam_song", true, "jam,song", "")
if err := app.Save(setlist); err != nil {
return err
}
// configure the built-in users auth collection:
// - username identity (not email)
// - no self-registration (CreateRule = nil means API create is forbidden)
users, err := app.FindCollectionByNameOrId("users")
if err != nil {
return err
}
users.Fields.Add(&core.TextField{Name: "username", Required: true})
users.AddIndex("idx_users_username", true, "username", "")
users.PasswordAuth.Enabled = true
users.PasswordAuth.IdentityFields = []string{"username"}
users.CreateRule = nil
return app.Save(users)
}, func(app core.App) error {
for _, name := range []string{"setlist", "jams", "songs", "locations"} {
col, err := app.FindCollectionByNameOrId(name)
if err != nil {
return err
}
if err := app.Delete(col); err != nil {
return err
}
}
// revert users to email identity + allow self-registration
users, err := app.FindCollectionByNameOrId("users")
if err != nil {
return err
}
usernameField := users.Fields.GetByName("username")
if usernameField != nil {
users.Fields.RemoveById(usernameField.GetId())
}
users.RemoveIndex("idx_users_username")
users.PasswordAuth.IdentityFields = []string{"email"}
users.CreateRule = func() *string { s := ""; return &s }()
return app.Save(users)
})
}