- 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
109 lines
3.5 KiB
Go
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)
|
|
})
|
|
}
|