Skip to main content

max / makenotwork

12.6 KB · 272 lines History Blame Raw
1 <div class="tab-docs"><a href="/docs/account">Docs: Account &rarr;</a></div>
2
3 {% if !moderation_active.is_empty() || !moderation_history.is_empty() %}
4 <div class="form-section">
5 <h2 class="subsection-title">Account Status</h2>
6 {% if !moderation_active.is_empty() %}
7 {% for action in moderation_active %}
8 <div class="callout callout--danger">
9 <strong class="callout-title">{{ action.action_label }}</strong>
10 <span class="callout-meta">{{ action.created_at }}</span>
11 <p class="callout-body">{{ action.reason }}</p>
12 </div>
13 {% endfor %}
14 <p class="callout-hint">
15 If you believe an action was taken in error, you can <a href="/docs/appeals">submit an appeal</a> or contact <a href="mailto:info@makenot.work">info@makenot.work</a>.
16 </p>
17 {% endif %}
18 {% if !moderation_history.is_empty() %}
19 <details class="account-mod-history">
20 <summary>View past actions</summary>
21 <div class="account-mod-history-list">
22 {% for action in moderation_history %}
23 <div class="account-mod-history-item">
24 <strong class="callout-title">{{ action.action_label }}</strong>
25 <span class="callout-meta">{{ action.created_at }}</span>
26 {% if let Some(resolved) = action.resolved_at %}
27 <span class="callout-meta">Resolved {{ resolved }}</span>
28 {% endif %}
29 <p class="account-mod-history-reason">{{ action.reason }}</p>
30 </div>
31 {% endfor %}
32 </div>
33 </details>
34 {% endif %}
35 </div>
36 {% endif %}
37
38 <div class="section-group-label">Security</div>
39
40 <div class="card-muted">
41 <div class="account-tip-card-title">Secure your account</div>
42 <div class="account-tip-list">
43 <span>1. Use a strong, unique password</span>
44 <span>2. Enable two-factor authentication (authenticator app or passkey)</span>
45 <span>3. Review active sessions periodically</span>
46 </div>
47 </div>
48
49 {% if !email_verified %}
50 <div class="callout callout--warning">
51 <strong>Email not verified.</strong> Some features require a verified email.
52 <button class="btn-secondary small account-resend-btn"
53 hx-post="/api/resend-verification"
54 hx-target="#verify-nudge-result"
55 hx-swap="innerHTML">Resend verification email</button>
56 <span id="verify-nudge-result"></span>
57 </div>
58 {% endif %}
59
60 <details class="form-section">
61 <summary><h2 class="subsection-title">Change Password</h2></summary>
62 <form hx-put="/api/users/me/password"
63 hx-target="#password-status"
64 hx-swap="innerHTML"
65 hx-indicator="#password-spinner"
66 hx-on::after-request="if(event.detail.successful) this.reset()"
67 onsubmit="var np=document.getElementById('new-password').value,cp=document.getElementById('confirm-password').value;if(np!==cp){var s=document.getElementById('password-status');s.textContent='Passwords do not match';s.classList.add('danger-text');return false;}">
68 <div class="form-group">
69 <label for="current-password">Current Password</label>
70 <input type="password" id="current-password" name="current_password" required>
71 </div>
72 <div class="form-group">
73 <label for="new-password">New Password</label>
74 <input type="password" id="new-password" name="new_password" minlength="8" required>
75 </div>
76 <div class="form-group">
77 <label for="confirm-password">Confirm New Password</label>
78 <input type="password" id="confirm-password" name="confirm_password" required>
79 </div>
80 <button class="btn-primary" type="submit">
81 Update Password
82 <span id="password-spinner" class="htmx-indicator"> ...</span>
83 </button>
84 <span id="password-status"></span>
85 </form>
86 </details>
87
88 <details class="form-section">
89 <summary><h2 class="subsection-title">Two-Factor Authentication (authenticator app)</h2></summary>
90 <div id="totp-section"
91 hx-get="/api/users/me/totp/status"
92 hx-trigger="revealed"
93 hx-swap="innerHTML">
94 </div>
95 </details>
96
97 <details class="form-section">
98 <summary><h2 class="subsection-title">Passkeys (fingerprint, face, or security key)</h2></summary>
99 <div id="passkey-section"
100 hx-get="/api/users/me/passkeys"
101 hx-trigger="revealed"
102 hx-swap="innerHTML">
103 </div>
104 </details>
105
106 <details class="form-section">
107 <summary><h2 class="subsection-title">Active Sessions</h2></summary>
108 {% include "partials/tabs/user_sessions.html" %}
109 </details>
110
111 <div class="section-group-label">Account</div>
112
113 <div class="form-section">
114 <div class="form-group">
115 <label for="email">Email</label>
116 <input type="email" id="email" value="{{ user.email }}" disabled>
117 <div class="hint">Contact support to update your email address</div>
118 </div>
119 <div class="form-group">
120 <label for="username">Username</label>
121 <input type="text" id="username" value="{{ user.username }}" disabled>
122 <div class="hint">Username cannot be changed</div>
123 </div>
124 <div class="form-group">
125 <label>Fan+ membership</label>
126 {% if let Some(fp) = fan_plus %}
127 <div class="hint mb-2">
128 {% if fp.cancel_at_period_end %}
129 Cancellation scheduled.{% if let Some(end) = fp.period_end %} Your membership stays active until {{ end }}.{% endif %}
130 {% else %}
131 Active.{% if let Some(end) = fp.period_end %} Next renewal: {{ end }}.{% endif %}
132 {% endif %}
133 </div>
134 <div class="form-inline-row">
135 {% if fp.cancel_at_period_end %}
136 <form method="post" action="/stripe/fan-plus/resume" class="inline-form">
137 {% if let Some(token) = csrf_token %}<input type="hidden" name="_csrf" value="{{ token }}">{% endif %}
138 <button type="submit" class="btn-secondary small">Resume</button>
139 </form>
140 {% else %}
141 <form method="post" action="/stripe/fan-plus/cancel" class="inline-form"
142 hx-confirm="Cancel Fan+? You'll keep access until the end of the month you've paid for.">
143 {% if let Some(token) = csrf_token %}<input type="hidden" name="_csrf" value="{{ token }}">{% endif %}
144 <button type="submit" class="btn-secondary small">Cancel</button>
145 </form>
146 {% endif %}
147 <form method="post" action="/stripe/billing-portal" class="inline-form">
148 {% if let Some(token) = csrf_token %}<input type="hidden" name="_csrf" value="{{ token }}">{% endif %}
149 <button type="submit" class="btn-secondary small" data-loading-text="Opening Stripe...">Manage billing</button>
150 </form>
151 </div>
152 {% else %}
153 <div class="hint">
154 Not subscribed. <a href="/fan-plus">Learn about Fan+</a>.
155 </div>
156 {% endif %}
157 </div>
158 </div>
159
160 <div class="section-group-label">Preferences</div>
161
162 <details class="form-section" open>
163 <summary><h2 class="subsection-title">Notification Preferences</h2></summary>
164 <form hx-put="/api/users/me/preferences" hx-target="#preferences-result" hx-swap="innerHTML">
165 <fieldset class="pref-fieldset">
166 <legend class="pref-legend">Security</legend>
167 <div class="checkbox-group">
168 <input type="checkbox" id="login-notification" name="login_notification_enabled" value="on"
169 {% if user.login_notification_enabled %}checked{% endif %}>
170 <label for="login-notification">Email me when a new device signs into my account</label>
171 </div>
172 </fieldset>
173 {% if can_create_projects %}
174 <fieldset class="pref-fieldset">
175 <legend class="pref-legend">Sales & Revenue</legend>
176 <div class="checkbox-group">
177 <input type="checkbox" id="notify-sale" name="notify_sale" value="on"
178 {% if user.notify_sale %}checked{% endif %}>
179 <label for="notify-sale">Email me when someone buys my content</label>
180 </div>
181 <div class="checkbox-group">
182 <input type="checkbox" id="notify-follower" name="notify_follower" value="on"
183 {% if user.notify_follower %}checked{% endif %}>
184 <label for="notify-follower">Email me when I get a new follower</label>
185 </div>
186 <div class="checkbox-group">
187 <input type="checkbox" id="tips-enabled" name="tips_enabled" value="on"
188 {% if user.tips_enabled %}checked{% endif %}>
189 <label for="tips-enabled">Accept tips on my profile and project pages</label>
190 </div>
191 <div class="checkbox-group">
192 <input type="checkbox" id="notify-tip" name="notify_tip" value="on"
193 {% if user.notify_tip %}checked{% endif %}>
194 <label for="notify-tip">Email me when I receive a tip</label>
195 </div>
196 </fieldset>
197 {% endif %}
198 <fieldset class="pref-fieldset">
199 <legend class="pref-legend">Content & Community</legend>
200 <div class="checkbox-group">
201 <input type="checkbox" id="notify-release" name="notify_release" value="on"
202 {% if user.notify_release %}checked{% endif %}>
203 <label for="notify-release">Email me when creators I follow publish new content</label>
204 </div>
205 {% if can_create_projects %}
206 <div class="checkbox-group">
207 <input type="checkbox" id="notify-issues" name="notify_issues" value="on"
208 {% if user.notify_issues %}checked{% endif %}>
209 <label for="notify-issues">Email me about new issues and comments on my repos</label>
210 </div>
211 {% endif %}
212 </fieldset>
213 <fieldset class="pref-fieldset pref-fieldset--last">
214 <legend class="pref-legend">Platform</legend>
215 <div class="checkbox-group">
216 <input type="checkbox" id="notify-status" name="notify_status" value="on"
217 {% if user.notify_status %}checked{% endif %}>
218 <label for="notify-status">Email me when platform status changes (outages, recovery)</label>
219 </div>
220 </fieldset>
221 <div id="preferences-result"></div>
222 <button type="submit" class="btn-primary mt-4">Save Preferences</button>
223 </form>
224 </details>
225
226 <div class="section-group-label">Data</div>
227
228 <div class="form-section">
229 <h2 class="subsection-title">Your Data</h2>
230 <p class="section-lead">
231 Download all your projects, items, sales, and purchases.
232 </p>
233 <a href="/dashboard/export" class="btn-secondary">Export Your Data</a>
234 <a href="/dashboard/import" class="btn-secondary ml-2">Import from Another Platform</a>
235 </div>
236
237 <div class="section-group-label">Account Management</div>
238
239 {% if can_create_projects && !creator_paused %}
240 <details class="form-section">
241 <summary><h2 class="subsection-title">Pause Creator Account</h2></summary>
242 <p class="section-lead mb-3">
243 Stop paying your creator plan. Anyone with an active membership to your projects keeps access until the end of the month they've paid for, then stops renewing. One-time purchases remain accessible to buyers. Your content stays hosted. Resume anytime by re-subscribing to a creator plan.
244 </p>
245 <p class="section-lead mb-4 text-sm dimmed">
246 <strong>Pause vs Delete:</strong> Pausing keeps your content and account intact. Deleting is permanent.
247 </p>
248 <button class="btn-danger"
249 hx-post="/api/users/me/pause-creator"
250 hx-confirm="Pause your creator account? Your subscription will be canceled and no new sales can be made."
251 hx-on::after-request="if(event.detail.successful) window.location.reload()">
252 Pause Creator Account
253 </button>
254 </details>
255 {% endif %}
256
257 <details class="form-section">
258 <summary><h2 class="subsection-title">Delete Account</h2></summary>
259 <p class="section-lead mb-3">
260 Permanently delete your account. This cannot be undone.
261 </p>
262 <p class="section-lead mb-4 text-sm dimmed">
263 Before deleting, consider <a href="/dashboard/export">exporting your data</a>.{% if can_create_projects && !creator_paused %} If you just want to stop paying, <strong>pause</strong> your account instead.{% endif %}
264 </p>
265 <button class="btn-danger"
266 hx-delete="/api/users/me"
267 hx-confirm="Delete your account? This cannot be undone."
268 hx-on::after-request="if(event.detail.successful) window.location.href='/login'">
269 Delete Account
270 </button>
271 </details>
272