Skip to main content

max / makenotwork

13.2 KB · 292 lines History Blame Raw
1 <div class="tab-docs"><a href="/docs/account-lifecycle">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 data-after="reset"
67 data-check-submit="checkPasswordMatch">
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-peer">
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 notifications.login %}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 notifications.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 notifications.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 notifications.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 notifications.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 notifications.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 notifications.status %}checked{% endif %}>
218 <label for="notify-status">Email me when platform status changes (outages, recovery)</label>
219 </div>
220 <div class="checkbox-group">
221 <input type="checkbox" id="notify-invite" name="notify_invite" value="on"
222 {% if notifications.invite %}checked{% endif %}>
223 <label for="notify-invite">Email me when someone signs up with one of my invite codes</label>
224 </div>
225 </fieldset>
226 <div id="preferences-result"></div>
227 <button type="submit" class="btn-primary mt-section">Save Preferences</button>
228 </form>
229
230 <fieldset class="pref-fieldset pref-fieldset--last">
231 <legend class="pref-legend">Mail you will receive regardless</legend>
232 <p class="section-lead">
233 These are not settings. Every message below reaches you whatever the
234 boxes above say, because it carries something you cannot act on if it
235 never arrives. Each one says why.
236 </p>
237 <dl class="operational-mail">
238 {% for kind in operational_mail %}
239 <dt>{{ kind.label }}</dt>
240 <dd>{{ kind.justification }}</dd>
241 {% endfor %}
242 </dl>
243 </fieldset>
244 </details>
245
246 <div class="section-group-label">Data</div>
247
248 <div class="form-section">
249 <h2 class="subsection-title">Your Data</h2>
250 <p class="section-lead">
251 Download all your projects, items, sales, and purchases.
252 </p>
253 <a href="/dashboard/export" class="btn-secondary">Export Your Data</a>
254 <a href="/dashboard/import" class="btn-secondary ml-2">Import from Another Platform</a>
255 </div>
256
257 <div class="section-group-label">Account Management</div>
258
259 {% if can_create_projects && !creator_paused %}
260 <details class="form-section">
261 <summary><h2 class="subsection-title">Pause Creator Account</h2></summary>
262 <p class="section-lead mb-section">
263 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.
264 </p>
265 <p class="section-lead mb-section text-sm dimmed">
266 <strong>Pause vs Delete:</strong> Pausing keeps your content and account intact. Deleting is permanent.
267 </p>
268 <button class="btn-danger"
269 hx-post="/api/users/me/pause-creator"
270 hx-confirm="Pause your creator account? Your subscription will be canceled and no new sales can be made."
271 data-after="reload">
272 Pause Creator Account
273 </button>
274 </details>
275 {% endif %}
276
277 <details class="form-section">
278 <summary><h2 class="subsection-title">Delete Account</h2></summary>
279 <p class="section-lead mb-section">
280 Permanently delete your account. This cannot be undone.
281 </p>
282 <p class="section-lead mb-section text-sm dimmed">
283 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 %}
284 </p>
285 <button class="btn-danger"
286 hx-delete="/api/users/me"
287 hx-confirm="Delete your account? This cannot be undone."
288 data-after="nav" data-href="/login">
289 Delete Account
290 </button>
291 </details>
292