Skip to main content

max / makenotwork

Surface embed feature, promote data export, restructure dashboard tabs Three medium UX audit items: 1. Add "Edit" and "Embed" links on public item pages for creators (new is_owner field on ItemTemplate) 2. Promote "Your Data" export/import section from collapsed <details> to always-visible in Account tab 3. Restructure dashboard from 11 flat tabs to 4 core (Account, Projects, Payments, Support) + "More" dropdown for Analytics, Creator, SyncKit, Media, SSH Keys, Forums Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Author: Max J. <87768334+MaxJMath@users.noreply.github.com> · 2026-05-03 02:55 UTC
Commit: d86a0c2460235c55973b392fc3adeb875bc032db
Parent: c8945a8
6 files changed, +75 insertions, -75 deletions
@@ -37,15 +37,15 @@ Usability audit grade: B. Complexity C+, Completeness B+, Learnability B, Discov
37 37
38 38 ### High (significant friction reduction)
39 39
40 - - [ ] **[MEDIUM]** Restructure user dashboard tabs — show 4 core tabs (Account, Projects, Payments, Support) by default. Collapse SyncKit, SSH Keys, Forums, Media into "More Tools" overflow section. Current 11 tabs overwhelm new creators
40 + - [x] **[MEDIUM]** Restructure user dashboard tabs — 4 core tabs (Account, Projects, Payments, Support) always visible, Analytics/Creator/SyncKit/Media/SSH Keys/Forums in "More" dropdown
41 41 - [x] **[MEDIUM]** Fix price input to use dollars, not cents — PWYW min, subscription tier, promo code fixed discount, and inline item edit all now accept dollars with auto-conversion to cents
42 42 - [x] **[MEDIUM]** Standardize pricing terminology — item wizard now says "One-Time Purchase" to match project wizard, paywall, landing page, and docs
43 43 - [x] **[MEDIUM]** Add self-service refund UI for creators — new "Sales" tab on item dashboard with per-transaction refund buttons, Stripe refund API integration
44 44
45 45 ### Medium (discoverability and learnability)
46 46
47 - - [ ] **[MEDIUM]** Surface embed feature — currently buried 4 clicks deep (Dashboard > Project > Item > Embed tab). Add "Embed" link on public item pages for creators
48 - - [ ] **[MEDIUM]** Link data export from dashboard — `/dashboard/export` has no entry point from dashboard tabs. Add link in sidebar or account tab
47 + - [x] **[MEDIUM]** Surface embed feature — added "Edit" and "Embed" links on public item pages for creators (via `is_owner` flag)
48 + - [x] **[MEDIUM]** Link data export from dashboard — promoted "Your Data" section from collapsed `<details>` to always-visible in Account tab
49 49 - [x] **[MEDIUM]** Explain creator application — pitch.html already says "Most applications are approved within a few days"
50 50 - [x] **[LOW]** Add one-line descriptions to project feature checkboxes — already implemented via `ProjectFeature::description()` + `type-card-desc` in template
51 51 - [x] **[LOW]** Improve empty states — library purchases has "Discover Content" CTA, project content has "Add First Item" CTA with explanatory text
@@ -276,6 +276,7 @@ pub(crate) async fn render_item_page(
276 276 bundle_items: bundle_item_views,
277 277 containing_bundles: containing_bundle_views,
278 278 sections,
279 + is_owner,
279 280 }
280 281 .into_response())
281 282 }
@@ -275,6 +275,8 @@ pub struct ItemTemplate {
275 275 pub containing_bundles: Vec<Item>,
276 276 /// Tabbed content sections (e.g. Features, Installation, Specs).
277 277 pub sections: Vec<ItemSection>,
278 + /// Whether the current user is the item's creator (for dashboard links).
279 + pub is_owner: bool,
278 280 }
279 281
280 282 /// Blog/article reader view.
@@ -110,16 +110,6 @@
110 110 hx-swap="innerHTML"
111 111 hx-indicator="#tab-spinner"
112 112 onclick="setActiveTab(this)">Account</button>
113 - <button class="tab"
114 - role="tab"
115 - aria-selected="false"
116 - aria-controls="tab-content"
117 - id="tab-payments"
118 - hx-get="/dashboard/tabs/payments"
119 - hx-target="#tab-content"
120 - hx-swap="innerHTML"
121 - hx-indicator="#tab-spinner"
122 - onclick="setActiveTab(this)">Payments</button>
123 113 {% if let Some(su) = session_user %}{% if su.can_create_projects %}
124 114 <button class="tab"
125 115 role="tab"
@@ -131,73 +121,17 @@
131 121 hx-swap="innerHTML"
132 122 hx-indicator="#tab-spinner"
133 123 onclick="setActiveTab(this)">Projects</button>
134 - <button class="tab"
135 - role="tab"
136 - aria-selected="false"
137 - aria-controls="tab-content"
138 - id="tab-analytics"
139 - hx-get="/dashboard/tabs/analytics"
140 - hx-target="#tab-content"
141 - hx-swap="innerHTML"
142 - hx-indicator="#tab-spinner"
143 - onclick="setActiveTab(this)">Analytics</button>
144 - {% endif %}{% endif %}
145 - <button class="tab"
146 - role="tab"
147 - aria-selected="false"
148 - aria-controls="tab-content"
149 - id="tab-creator"
150 - hx-get="/dashboard/tabs/creator"
151 - hx-target="#tab-content"
152 - hx-swap="innerHTML"
153 - hx-indicator="#tab-spinner"
154 - onclick="setActiveTab(this)">Creator</button>
155 - {% if let Some(su) = session_user %}{% if su.can_create_projects && !projects.is_empty() %}
156 - <button class="tab"
157 - role="tab"
158 - aria-selected="false"
159 - aria-controls="tab-content"
160 - id="tab-synckit"
161 - hx-get="/dashboard/tabs/synckit"
162 - hx-target="#tab-content"
163 - hx-swap="innerHTML"
164 - hx-indicator="#tab-spinner"
165 - onclick="setActiveTab(this)">SyncKit</button>
166 - <button class="tab"
167 - role="tab"
168 - aria-selected="false"
169 - aria-controls="tab-content"
170 - id="tab-media"
171 - hx-get="/dashboard/tabs/media"
172 - hx-target="#tab-content"
173 - hx-swap="innerHTML"
174 - hx-indicator="#tab-spinner"
175 - onclick="setActiveTab(this)">Media</button>
176 124 {% endif %}{% endif %}
177 - {% if git_enabled %}
178 125 <button class="tab"
179 126 role="tab"
180 127 aria-selected="false"
181 128 aria-controls="tab-content"
182 - id="tab-ssh-keys"
183 - hx-get="/dashboard/tabs/ssh-keys"
184 - hx-target="#tab-content"
185 - hx-swap="innerHTML"
186 - hx-indicator="#tab-spinner"
187 - onclick="setActiveTab(this)">SSH Keys</button>
188 - {% endif %}
189 - {% if has_mt_memberships %}
190 - <button class="tab"
191 - role="tab"
192 - aria-selected="false"
193 - aria-controls="tab-content"
194 - id="tab-forums"
195 - hx-get="/dashboard/tabs/forums"
129 + id="tab-payments"
130 + hx-get="/dashboard/tabs/payments"
196 131 hx-target="#tab-content"
197 132 hx-swap="innerHTML"
198 133 hx-indicator="#tab-spinner"
199 - onclick="setActiveTab(this)">Forums</button>
200 - {% endif %}
134 + onclick="setActiveTab(this)">Payments</button>
201 135 <button class="tab"
202 136 role="tab"
203 137 aria-selected="false"
@@ -208,9 +142,68 @@
208 142 hx-swap="innerHTML"
209 143 hx-indicator="#tab-spinner"
210 144 onclick="setActiveTab(this)">Support</button>
145 +
146 + <div class="tab-overflow" style="position: relative; display: inline-block;">
147 + <button class="tab" onclick="var m=this.nextElementSibling; m.style.display=m.style.display==='block'?'none':'block';" type="button">More</button>
148 + <div class="tab-overflow-menu" style="display: none; position: absolute; top: 100%; left: 0; z-index: 10; background: var(--background); border: 1px solid var(--border); min-width: 160px; box-shadow: 0 2px 8px rgba(0,0,0,0.1);">
149 + {% if let Some(su) = session_user %}{% if su.can_create_projects %}
150 + <button class="tab" style="display: block; width: 100%; text-align: left; padding: 0.5rem 1rem;"
151 + hx-get="/dashboard/tabs/analytics"
152 + hx-target="#tab-content"
153 + hx-swap="innerHTML"
154 + hx-indicator="#tab-spinner"
155 + onclick="setActiveTab(this); this.closest('.tab-overflow-menu').style.display='none';">Analytics</button>
156 + {% endif %}{% endif %}
157 + <button class="tab" style="display: block; width: 100%; text-align: left; padding: 0.5rem 1rem;"
158 + hx-get="/dashboard/tabs/creator"
159 + hx-target="#tab-content"
160 + hx-swap="innerHTML"
161 + hx-indicator="#tab-spinner"
162 + onclick="setActiveTab(this); this.closest('.tab-overflow-menu').style.display='none';">Creator</button>
163 + {% if let Some(su) = session_user %}{% if su.can_create_projects && !projects.is_empty() %}
164 + <button class="tab" style="display: block; width: 100%; text-align: left; padding: 0.5rem 1rem;"
165 + hx-get="/dashboard/tabs/synckit"
166 + hx-target="#tab-content"
167 + hx-swap="innerHTML"
168 + hx-indicator="#tab-spinner"
169 + onclick="setActiveTab(this); this.closest('.tab-overflow-menu').style.display='none';">SyncKit</button>
170 + <button class="tab" style="display: block; width: 100%; text-align: left; padding: 0.5rem 1rem;"
171 + hx-get="/dashboard/tabs/media"
172 + hx-target="#tab-content"
173 + hx-swap="innerHTML"
174 + hx-indicator="#tab-spinner"
175 + onclick="setActiveTab(this); this.closest('.tab-overflow-menu').style.display='none';">Media</button>
176 + {% endif %}{% endif %}
177 + {% if git_enabled %}
178 + <button class="tab" style="display: block; width: 100%; text-align: left; padding: 0.5rem 1rem;"
179 + hx-get="/dashboard/tabs/ssh-keys"
180 + hx-target="#tab-content"
181 + hx-swap="innerHTML"
182 + hx-indicator="#tab-spinner"
183 + onclick="setActiveTab(this); this.closest('.tab-overflow-menu').style.display='none';">SSH Keys</button>
184 + {% endif %}
185 + {% if has_mt_memberships %}
186 + <button class="tab" style="display: block; width: 100%; text-align: left; padding: 0.5rem 1rem;"
187 + hx-get="/dashboard/tabs/forums"
188 + hx-target="#tab-content"
189 + hx-swap="innerHTML"
190 + hx-indicator="#tab-spinner"
191 + onclick="setActiveTab(this); this.closest('.tab-overflow-menu').style.display='none';">Forums</button>
192 + {% endif %}
193 + </div>
194 + </div>
211 195 <span id="tab-spinner" class="htmx-indicator" style="margin-left: 1rem;" aria-live="polite"> Loading...</span>
212 196 </div>
213 197
198 + <script>
199 + document.addEventListener('click', function(e) {
200 + var menus = document.querySelectorAll('.tab-overflow-menu');
201 + menus.forEach(function(m) {
202 + if (!m.parentElement.contains(e.target)) m.style.display = 'none';
203 + });
204 + });
205 + </script>
206 +
214 207 <!-- Tab Content Container - initially loaded with Details -->
215 208 <div id="tab-content" class="tab-content active"
216 209 role="tabpanel"
@@ -375,6 +375,10 @@
375 375 <footer class="item-footer">
376 376 <p>Powered by <a href="/">Makenot<span class="dot">.</span>work</a> &middot; Fair distribution for creatives of all kinds</p>
377 377 <p style="margin-top: 0.5rem;">
378 + {% if is_owner %}
379 + <a href="/dashboard/item/{{ item.id }}">Edit</a> &middot;
380 + <a href="/dashboard/item/{{ item.id }}#embed">Embed</a> &middot;
381 + {% endif %}
378 382 <a href="javascript:void(0)" onclick="navigator.clipboard.writeText(window.location.origin + '/i/{{ item.id }}').then(() => { this.textContent = 'Copied!'; setTimeout(() => this.textContent = 'Copy link', 1500) })">Copy link</a> &middot;
379 383 {% if session_user.is_some() %}
380 384 <a href="javascript:void(0)" onclick="document.getElementById('report-modal').style.display='flex'">Report this item</a> &middot;
@@ -371,14 +371,14 @@
371 371 {% include "partials/tabs/user_sessions.html" %}
372 372 </details>
373 373
374 - <details class="form-section">
375 - <summary><h2>Your Data</h2></summary>
374 + <div class="form-section">
375 + <h2>Your Data</h2>
376 376 <p class="muted" style="margin-bottom: 1rem; text-align: left;">
377 377 Download all your projects, items, sales, and purchases.
378 378 </p>
379 379 <a href="/dashboard/export"><button class="secondary">Export Portal</button></a>
380 380 <a href="/dashboard/import" style="margin-left: 0.5rem;"><button class="secondary">Import Data</button></a>
381 - </details>
381 + </div>
382 382
383 383 <details class="form-section">
384 384 <summary><h2>Danger Zone</h2></summary>