Yōten: A social tracker for your language learning journey built on the atproto.
at master 128 lines 4.2 kB view raw
1package views 2 3import ( 4 "fmt" 5 "slices" 6 7 "yoten.app/internal/db" 8 "yoten.app/internal/server/views/layouts" 9 "yoten.app/internal/server/views/partials" 10) 11 12templ EditActivityPage(params EditActivityPageParams) { 13 @layouts.Base(layouts.BaseParams{Title: "edit activity"}) { 14 @partials.Header(partials.HeaderProps{User: params.User}) 15 <div class="container mx-auto px-4 py-8 max-w-2xl"> 16 <form 17 class="card mb-8 group" 18 hx-post={ templ.SafeURL(fmt.Sprintf("/activity/edit/%s", params.Activity.Rkey)) } 19 hx-swap="none" 20 hx-disabled-elt="#save-button,#cancel-button" 21 > 22 <div> 23 <h1 class="text-3xl font-bold">Edit Custom Activity</h1> 24 </div> 25 <div class="flex flex-col gap-4"> 26 <div class="flex flex-col gap-2"> 27 <div class="flex flex-col gap-1"> 28 <label for="description" class="font-medium text-sm">Activity Name</label> 29 <div x-data={ templ.JSONString(JsonText{Text: params.Activity.Name}) }> 30 <input 31 id="activity-name" 32 name="activity_name" 33 placeholder="e.g., Anki flashcards, Shadowing practice, etc." 34 class="input w-full" 35 maxLength="64" 36 /> 37 <div class="text-right text-sm text-text-muted mt-1"> 38 <span x-text="text.length"></span> / 64 39 </div> 40 </div> 41 </div> 42 </div> 43 <div class="flex flex-col gap-1"> 44 <label for="description" class="font-medium text-sm">Description (optional)</label> 45 <div x-data="{ text: '' }" x-init="text = $el.querySelector('textarea').value"> 46 <textarea 47 x-model="text" 48 id="description" 49 name="description" 50 placeholder="Describe what this activity involves..." 51 class="input w-full" 52 maxLength="256" 53 > 54 { params.Activity.Description } 55 </textarea> 56 <div class="text-right text-sm text-text-muted mt-1"> 57 <span x-text="text.length"></span> / 256 58 </div> 59 </div> 60 </div> 61 <div class="flex flex-col gap-2"> 62 <div> 63 <p class="font-medium text-sm">Categories</p> 64 <p class="text-sm text-text-muted mt-1"> 65 Select which categories this activity involves (for tracking in stats) 66 </p> 67 </div> 68 for _, c := range params.SortedCategories { 69 {{ 70 contains := slices.ContainsFunc(params.Activity.Categories, func(category db.Category) bool { 71 return category.ID == c.ID 72 }) 73 }} 74 <div key={ c.ID } class="input"> 75 <input 76 type="checkbox" 77 id={ fmt.Sprintf("category-%d", c.ID) } 78 name={ fmt.Sprintf("category_%d", c.ID) } 79 if contains { 80 checked 81 } 82 value={ c.Name } 83 /> 84 <label for={ fmt.Sprintf("category-%d", c.ID) }> 85 { c.Name } 86 </label> 87 </div> 88 } 89 </div> 90 </div> 91 <div class="flex flex-col sm:flex-row gap-4 mt-4"> 92 <button id="save-button" type="submit" class="btn btn-primary"> 93 <i class="w-4 h-4" data-lucide="save"></i> 94 Save 95 <i class="w-4 h-4 animate-spin hidden group-[.htmx-request]:inline" data-lucide="loader-circle"></i> 96 </button> 97 <button 98 id="cancel-button" 99 onclick="window.location.href = '/profile/activities'" 100 type="button" 101 class="btn btn-secondary" 102 > 103 Cancel 104 </button> 105 </div> 106 </form> 107 <div class="flex flex-col sm:flex-row gap-4 items-center bg-bg-light text-red-500 rounded border w-full p-6"> 108 <div class="flex flex-col gap-1"> 109 <h3 class="font-medium">Delete this activity</h3> 110 <p class="text-sm text-text-muted"> 111 This action cannot be undone. This will permanently delete this activity. 112 </p> 113 </div> 114 <button 115 id="delete-button" 116 hx-disabled-elt="#delete-button,#save-button,#cancel-button" 117 hx-delete={ templ.SafeURL(fmt.Sprintf("/activity/%s", params.Activity.Rkey)) } 118 class="btn btn-dangerous w-full md:w-fit whitespace-nowrap group" 119 type="button" 120 > 121 <i class="w-4 h-4" data-lucide="trash-2"></i> 122 Delete Activity 123 <i class="w-4 h-4 animate-spin hidden group-[.htmx-request]:inline" data-lucide="loader-circle"></i> 124 </button> 125 </div> 126 </div> 127 } 128}