Yōten: A social tracker for your language learning journey built on the atproto.
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}