| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240 |
- package views
- import "github.com/pocketbase/pocketbase/core"
- // JamList renders the list of all jams.
- templ JamList(jams []*core.Record, locationNames map[string]string, songCounts map[string]SongCount) {
- @Layout("Jams") {
- <div class="level">
- <div class="level-left">
- <h1 class="title">Jams</h1>
- </div>
- <div class="level-right">
- <a class="button is-primary" href="/jams/new">+ New Jam</a>
- </div>
- </div>
- if len(jams) == 0 {
- <p class="has-text-grey">No jams logged yet.</p>
- } else {
- <table class="table is-fullwidth is-striped is-hoverable">
- <thead>
- <tr><th>Date</th><th>Location</th><th>Songs</th></tr>
- </thead>
- <tbody>
- for _, j := range jams {
- <tr>
- <td><a href={ templ.SafeURL("/jams/" + j.Id) }>{ dateOnly(j.GetString("date")) }</a></td>
- <td>{ locationNames[j.GetString("location")] }</td>
- <td>{ songCountDisplay(songCounts[j.Id]) }</td>
- </tr>
- }
- </tbody>
- </table>
- }
- }
- }
- // JamNew renders the new-jam creation form.
- templ JamNew(locations []*core.Record) {
- @Layout("New Jam") {
- <h1 class="title">New Jam</h1>
- <form method="POST" action="/jams" class="box" style="max-width:480px">
- <div class="field">
- <label class="label">Date</label>
- <div class="control">
- <input class="input" type="date" name="date" required/>
- </div>
- </div>
- <div class="field">
- <label class="label">Location</label>
- <div class="control">
- <div class="select is-fullwidth">
- <select name="location" required>
- <option value="">— select —</option>
- for _, loc := range locations {
- <option value={ loc.Id }>{ loc.GetString("name") }</option>
- }
- </select>
- </div>
- </div>
- </div>
- <div class="field">
- <label class="label">Notes</label>
- <div class="control">
- <textarea class="textarea" name="notes" rows="2"></textarea>
- </div>
- </div>
- <div class="field mt-4">
- <div class="control">
- <button class="button is-primary" type="submit">Create Jam</button>
- <a class="button ml-2" href="/jams">Cancel</a>
- </div>
- </div>
- </form>
- }
- }
- // SetlistRow is the view model for a single row in the setlist table.
- type SetlistRow struct {
- ID string
- Artist string
- Title string
- Played bool
- }
- // JamDetail renders a single jam and its setlist.
- templ JamDetail(jam *core.Record, location *core.Record, rows []SetlistRow) {
- @Layout("Jam — " + dateOnly(jam.GetString("date"))) {
- <div class="level">
- <div class="level-left">
- <h1 class="title">{ dateOnly(jam.GetString("date")) } — <a href={ templ.SafeURL("/locations/" + location.Id) }>{ location.GetString("name") }</a></h1>
- </div>
- </div>
- <div class="columns">
- <div class="column is-two-thirds">
- <h2 class="subtitle">Setlist</h2>
- <table class="table is-fullwidth" id="setlist">
- <thead>
- <tr><th>Artist</th><th>Title</th><th>Played</th><th></th></tr>
- </thead>
- <tbody id="setlist-rows">
- for _, row := range rows {
- @SetlistRowFrag(jam.Id, row)
- }
- </tbody>
- </table>
- <div class="mt-4" id="song-add-form">
- @SongAddForm(jam.Id)
- </div>
- </div>
- <div class="column">
- if jam.GetString("notes") != "" {
- <p><strong>Notes:</strong> { jam.GetString("notes") }</p>
- }
- </div>
- </div>
- }
- }
- // SetlistRowFrag renders a single setlist row (used for HTMX swap).
- templ SetlistRowFrag(jamID string, row SetlistRow) {
- <tr id={ "row-" + row.ID }>
- <td>{ row.Artist }</td>
- <td>{ row.Title }</td>
- <td>
- <input
- type="checkbox"
- if row.Played {
- checked
- }
- hx-patch={ "/setlist/" + row.ID + "/played" }
- hx-target={ "#row-" + row.ID }
- hx-swap="outerHTML"
- />
- </td>
- <td>
- <button
- class="button is-small is-ghost"
- hx-get={ "/setlist/" + row.ID + "/edit" }
- hx-target={ "#row-" + row.ID }
- hx-swap="outerHTML"
- >✎</button>
- <button
- class="delete"
- hx-delete={ "/setlist/" + row.ID }
- hx-target={ "#row-" + row.ID }
- hx-swap="outerHTML"
- hx-confirm="Remove this song from the setlist?"
- ></button>
- </td>
- </tr>
- }
- // SetlistEditFrag renders a setlist row as an inline edit form.
- templ SetlistEditFrag(row SetlistRow) {
- <tr id={ "row-" + row.ID }>
- <td><input class="input is-small" type="text" name="artist" value={ row.Artist } required/></td>
- <td><input class="input is-small" type="text" name="title" value={ row.Title } required/></td>
- <td>
- <input
- type="checkbox"
- name="played"
- if row.Played {
- checked
- }
- />
- </td>
- <td>
- <button
- class="button is-small is-primary mr-1"
- hx-patch={ "/setlist/" + row.ID }
- hx-include="closest tr"
- hx-target={ "#row-" + row.ID }
- hx-swap="outerHTML"
- >Save</button>
- <button
- class="button is-small"
- hx-get={ "/setlist/" + row.ID + "/view" }
- hx-target={ "#row-" + row.ID }
- hx-swap="outerHTML"
- >Cancel</button>
- </td>
- </tr>
- }
- // SongAddForm renders the add-song form for a jam. Artist has a typeahead
- // datalist (suggestions only; free-form text is always accepted).
- templ SongAddForm(jamID string) {
- <form
- hx-post={ "/jams/" + jamID + "/songs" }
- hx-target="#setlist-rows"
- hx-swap="beforeend"
- hx-on--after-request="if(event.detail.requestConfig.verb==='post') { this.reset(); const a=this.querySelector('[name=artist]'); setTimeout(()=>{a.focus();a.scrollIntoView({behavior:'smooth',block:'nearest'});},0); }"
- >
- <div class="field has-addons">
- <div class="control is-expanded">
- <input
- class="input"
- type="text"
- name="artist"
- placeholder="Artist"
- list="song-artist-list"
- hx-get="/songs/search"
- hx-trigger="input changed delay:300ms, change from:[name=title]"
- hx-target="#song-artist-list"
- hx-swap="innerHTML"
- hx-include="[name=title]"
- autocomplete="off"
- required
- />
- <datalist id="song-artist-list"></datalist>
- </div>
- <div class="control is-expanded">
- <input
- class="input"
- type="text"
- name="title"
- placeholder="Title"
- list="song-title-list"
- hx-get="/songs/search"
- hx-vals='{"field":"title"}'
- hx-trigger="input changed delay:300ms, change from:[name=artist]"
- hx-target="#song-title-list"
- hx-swap="innerHTML"
- hx-include="[name=artist]"
- autocomplete="off"
- required
- />
- <datalist id="song-title-list"></datalist>
- </div>
- <div class="control">
- <label class="checkbox" style="padding: 0.5em 0.75em">
- <input type="checkbox" name="played"/> Played
- </label>
- </div>
- <div class="control">
- <button class="button is-link" type="submit">Add</button>
- </div>
- </div>
- </form>
- }
|