1package systemviews
2
3import (
4 "fmt"
5 "github.com/bnema/dumber/internal/domain/entity"
6)
7
8templ FavoritesView(data favoritesRenderData) {
9 @Alert("success", data.Notice)
10 @Alert("error", data.Error)
11 @Section("sv-favorites-controls", "Manage favorites") {
12 @FavoritesControls(data)
13 }
14 @Section("sv-favorites-list", "Favorites") {
15 @Meta(favoritesSummary(data))
16 if len(data.Favorites) == 0 {
17 @EmptyState("No favorites")
18 } else {
19 <ul class="sv-list">
20 for _, favorite := range data.Favorites {
21 if favorite != nil {
22 @FavoriteItem(favorite, data.Tags)
23 }
24 }
25 </ul>
26 }
27 }
28 @Section("sv-tags-list", "Tags") {
29 @Meta(tagsSummary(data))
30 @TagCreateForm()
31 if len(data.Tags) == 0 {
32 @EmptyState("No tags")
33 } else {
34 <ul class="sv-list">
35 for _, tag := range data.Tags {
36 if tag != nil {
37 @TagItem(tag)
38 }
39 }
40 </ul>
41 }
42 }
43}
44
45templ FavoritesControls(data favoritesRenderData) {
46 <form class="sv-card-form" data-sv-action="favorite.create">
47 <label><span>URL</span><input name="url" type="url" placeholder="https://example.com" required/></label>
48 <label><span>Title</span><input name="title" type="text" placeholder="Optional title"/></label>
49 @FavoriteCreateTagPicker(data.Tags)
50 <button class="sv-button" type="submit">@ButtonIconLabel(iconPlus, "Add favorite")</button>
51 </form>
52 <div class="sv-filter-panel">
53 <div class="sv-button-row">
54 <span class="sv-meta">Tags</span>
55 <button type="button" class={ favoriteFilterClass(favoriteAllFilterActive(data)) } aria-pressed={ fmt.Sprintf("%t", favoriteAllFilterActive(data)) } data-sv-action="favorite.clearFilters">All</button>
56 <button type="button" class={ favoriteFilterClass(favoriteUntaggedFilterActive(data)) } aria-pressed={ fmt.Sprintf("%t", favoriteUntaggedFilterActive(data)) } data-sv-action="favorite.filterUntagged">Untagged</button>
57 for _, tag := range data.Tags {
58 if tag != nil {
59 <button type="button" class={ favoriteFilterClass(favoriteTagFilterActive(data, tag.ID)) } aria-pressed={ fmt.Sprintf("%t", favoriteTagFilterActive(data, tag.ID)) } data-sv-action="favorite.filterTag" data-tag-id={ fmt.Sprintf("%d", tag.ID) }>
60 <span class="sv-tag-dot" style={ "background:" + safeTagColor(tag.Color) }></span>{ tag.Name }
61 </button>
62 }
63 }
64 </div>
65 </div>
66 <p class="sv-meta sv-key-hints">Keys: <kbd>j</kbd>/<kbd>k</kbd> move, <kbd>Enter</kbd> open, <kbd>e</kbd> edit title, <kbd>d</kbd> delete focused favorite.</p>
67}
68
69templ FavoriteCreateTagPicker(tags []*entity.Tag) {
70 if len(tags) > 0 {
71 <fieldset class="sv-tag-picker">
72 <legend>Tags</legend>
73 <div class="sv-tag-controls">
74 for _, tag := range tags {
75 if tag != nil {
76 <label class="sv-tag-toggle">
77 <input type="checkbox" name="tags" value={ fmt.Sprintf("%d", tag.ID) }/>
78 <span><span class="sv-tag-dot" style={ "background:" + safeTagColor(tag.Color) }></span>{ tag.Name }</span>
79 </label>
80 }
81 }
82 </div>
83 </fieldset>
84 }
85}
86
87templ FavoriteItem(favorite *entity.Favorite, tags []*entity.Tag) {
88 <li class="sv-list-row">
89 <article class="sv-favorite-item" data-sv-favorite-row data-favorite-id={ fmt.Sprintf("%d", favorite.ID) }>
90 <div class="sv-favorite-header">
91 <div>
92 <a class="sv-link sv-favorite-open" href={ templ.SafeURL(sanitizeHref(favorite.URL)) }>{ favoriteItemLabel(favorite) }</a>
93 <p class="sv-history-url">{ favorite.URL }</p>
94 if favoriteMetaText(favorite) != "" {
95 <p class="sv-meta">{ favoriteMetaText(favorite) }</p>
96 }
97 </div>
98 <div class="sv-history-row-actions">
99 <a class="sv-button sv-button-secondary" href={ templ.SafeURL(sanitizeHref(favorite.URL)) }>@ButtonIconLabel(iconExternal, "Open")</a>
100 <button type="button" class="sv-button sv-button-secondary sv-button-danger sv-button-icon-only" data-sv-action="favorite.delete" data-id={ fmt.Sprintf("%d", favorite.ID) } data-sv-confirm="Delete this favorite?" aria-label="Delete this favorite">@IconOnly(iconTrash, "Delete this favorite")</button>
101 </div>
102 </div>
103 @FavoriteEditForm(favorite)
104 @FavoriteTagControls(favorite, tags)
105 </article>
106 </li>
107}
108
109templ FavoriteEditForm(favorite *entity.Favorite) {
110 <form class="sv-inline-form" data-sv-action="favorite.update">
111 <input type="hidden" name="id" value={ fmt.Sprintf("%d", favorite.ID) }/>
112 <label><span>Title</span><input name="title" type="text" value={ favorite.Title }/></label>
113 <label><span>Favicon</span><input name="favicon_url" type="url" value={ favorite.FaviconURL }/></label>
114 @ShortcutSelect(favorite.ShortcutKey)
115 <button class="sv-button sv-button-secondary" type="submit">@ButtonIconLabel(iconSave, "Save")</button>
116 </form>
117}
118
119templ FavoriteTagControls(favorite *entity.Favorite, tags []*entity.Tag) {
120 if len(tags) > 0 {
121 <div class="sv-tag-controls">
122 <span class="sv-meta">Tags</span>
123 for _, tag := range tags {
124 if tag != nil {
125 <button type="button" class={ favoriteTagButtonClass(favorite, tag) } data-sv-action={ favoriteTagButtonAction(favorite, tag) } data-favorite-id={ fmt.Sprintf("%d", favorite.ID) } data-tag-id={ fmt.Sprintf("%d", tag.ID) }>
126 <span class="sv-tag-dot" style={ "background:" + safeTagColor(tag.Color) }></span>{ favoriteTagButtonLabel(favorite, tag) }
127 </button>
128 }
129 }
130 </div>
131 }
132}
133
134templ TagCreateForm() {
135 <form class="sv-inline-form" data-sv-action="tag.create">
136 <label><span>Name</span><input name="name" type="text" required/></label>
137 <label><span>Color</span><input name="color" type="text" placeholder="#808080"/></label>
138 <button class="sv-button sv-button-secondary" type="submit">@ButtonIconLabel(iconTag, "Create tag")</button>
139 </form>
140}
141
142templ TagItem(tag *entity.Tag) {
143 <li class="sv-list-row">
144 <form class="sv-inline-form" data-sv-action="tag.update">
145 <input type="hidden" name="id" value={ fmt.Sprintf("%d", tag.ID) }/>
146 <span class="sv-tag-dot" style={ "background:" + safeTagColor(tag.Color) }></span>
147 <label><span>Name</span><input name="name" type="text" value={ tag.Name } required/></label>
148 <label><span>Color</span><input name="color" type="text" value={ tag.Color }/></label>
149 <button class="sv-button sv-button-secondary" type="submit">@ButtonIconLabel(iconSave, "Save")</button>
150 <button type="button" class="sv-button sv-button-secondary sv-button-danger sv-button-icon-only" data-sv-action="tag.delete" data-id={ fmt.Sprintf("%d", tag.ID) } data-sv-confirm="Delete this tag?" aria-label="Delete this tag">@IconOnly(iconTrash, "Delete this tag")</button>
151 </form>
152 </li>
153}
154
155templ ShortcutSelect(selected *int) {
156 <label><span>Shortcut</span><select name="shortcut_key">
157 <option value="">None</option>
158 for i := 1; i <= 9; i++ {
159 <option value={ fmt.Sprintf("%d", i) } selected?={ shortcutSelected(selected, i) }>{ fmt.Sprintf("%d", i) }</option>
160 }
161 </select></label>
162}