| 1 |
<div class="tab-docs"><a href="/docs/profile">Docs: Profile →</a></div> |
| 2 |
|
| 3 |
<div class="form-section"> |
| 4 |
<h2 class="subsection-title">Links</h2> |
| 5 |
<p class="muted mb-4">Add links to your public profile.</p> |
| 6 |
|
| 7 |
<div id="links-list"> |
| 8 |
{% for link in custom_links %} |
| 9 |
<div class="link-row" data-id="{{ link.id }}"> |
| 10 |
<div class="link-order-buttons"> |
| 11 |
<button type="button" class="order-btn" data-action="moveLinkUp" title="Move up">▲</button> |
| 12 |
<button type="button" class="order-btn" data-action="moveLinkDown" title="Move down">▼</button> |
| 13 |
</div> |
| 14 |
<input type="text" value="{{ link.title }}" placeholder="Title" name="title" disabled> |
| 15 |
<input type="url" value="{{ link.url }}" placeholder="https://..." name="url" disabled> |
| 16 |
<button class="btn-secondary link-edit-btn" data-action="editLinkBtn">Edit</button> |
| 17 |
<button class="btn-primary link-save-btn hidden" data-action="saveLinkBtn">Save</button> |
| 18 |
<button class="btn-secondary link-cancel-btn hidden" data-action="cancelLinkBtn">Cancel</button> |
| 19 |
<button class="btn-danger link-remove-btn" |
| 20 |
hx-delete="/api/links/{{ link.id }}" |
| 21 |
hx-target="closest .link-row" |
| 22 |
hx-swap="outerHTML" |
| 23 |
hx-confirm="Remove this link?">Remove</button> |
| 24 |
</div> |
| 25 |
{% endfor %} |
| 26 |
</div> |
| 27 |
|
| 28 |
<script src="/static/tab-user-profile.js?v=0623"></script> |
| 29 |
|
| 30 |
<form hx-post="/api/links" |
| 31 |
hx-target="#links-list" |
| 32 |
hx-swap="beforeend" |
| 33 |
data-hx-reset> |
| 34 |
<div class="field-row"> |
| 35 |
<div class="form-group is-grow"> |
| 36 |
<label for="link-title">Title</label> |
| 37 |
<input type="text" id="link-title" name="title" placeholder="My Website" required> |
| 38 |
</div> |
| 39 |
<div class="form-group is-grow-2"> |
| 40 |
<label for="link-url">URL</label> |
| 41 |
<input type="url" id="link-url" name="url" placeholder="https://..." required> |
| 42 |
</div> |
| 43 |
<button class="btn-secondary" type="submit">Add Link</button> |
| 44 |
</div> |
| 45 |
</form> |
| 46 |
</div> |
| 47 |
|
| 48 |
<div class="form-section"> |
| 49 |
<h2 class="subsection-title">Profile</h2> |
| 50 |
<p class="muted mb-4">Your public page: <a href="/u/{{ user.username }}">makenot.work/u/{{ user.username }}</a></p> |
| 51 |
|
| 52 |
<form hx-put="/api/users/me" |
| 53 |
hx-target="#profile-save-status" |
| 54 |
hx-swap="innerHTML" |
| 55 |
hx-indicator="#profile-spinner"> |
| 56 |
<div class="form-group"> |
| 57 |
<label for="display-name">Display Name</label> |
| 58 |
<input type="text" id="display-name" name="display_name" value="{{ user.display_name_or_username() }}"> |
| 59 |
<div class="hint">Defaults to your username if left empty</div> |
| 60 |
</div> |
| 61 |
|
| 62 |
<div class="form-group"> |
| 63 |
<label for="bio">Bio</label> |
| 64 |
<textarea id="bio" name="bio">{{ user.bio.as_deref().unwrap_or("") }}</textarea> |
| 65 |
</div> |
| 66 |
|
| 67 |
<button class="btn-primary" type="submit"> |
| 68 |
Save Changes |
| 69 |
<span id="profile-spinner" class="htmx-indicator"> ...</span> |
| 70 |
</button> |
| 71 |
<span id="profile-save-status"></span> |
| 72 |
</form> |
| 73 |
</div> |
| 74 |
|
| 75 |
<div class="form-section"> |
| 76 |
<h2 class="subsection-title">Theme</h2> |
| 77 |
<p class="muted mb-4">Color palette for your public profile page.</p> |
| 78 |
|
| 79 |
<form hx-put="/api/users/me/theme" |
| 80 |
hx-target="#profile-theme-save-status" |
| 81 |
hx-swap="innerHTML" |
| 82 |
hx-indicator="#profile-theme-spinner"> |
| 83 |
<div class="form-group"> |
| 84 |
<label for="profile-theme">Profile theme</label> |
| 85 |
<select id="profile-theme" name="theme_id"> |
| 86 |
{% for t in theme_options %} |
| 87 |
<option value="{{ t.id }}"{% if t.selected %} selected{% endif %}>{{ t.name }}</option> |
| 88 |
{% endfor %} |
| 89 |
</select> |
| 90 |
<div class="hint">Applies to makenot.work/u/{{ user.username }}. Projects can set their own theme.</div> |
| 91 |
</div> |
| 92 |
|
| 93 |
<button class="btn-primary" type="submit"> |
| 94 |
Save Theme |
| 95 |
<span id="profile-theme-spinner" class="htmx-indicator"> ...</span> |
| 96 |
</button> |
| 97 |
<span id="profile-theme-save-status"></span> |
| 98 |
</form> |
| 99 |
</div> |
| 100 |
|
| 101 |
<div class="form-section"> |
| 102 |
<h2 class="subsection-title">Custom page</h2> |
| 103 |
<p class="muted mb-4"> |
| 104 |
Go beyond the theme: write your own HTML and CSS for your public profile. |
| 105 |
No JavaScript, on-platform links only. |
| 106 |
</p> |
| 107 |
<a class="btn-secondary" href="/dashboard/custom-page">Edit custom page</a> |
| 108 |
</div> |
| 109 |
|
| 110 |
{% if can_create_projects %} |
| 111 |
<details class="form-section"> |
| 112 |
<summary><h2 class="subsection-title">Custom Domain</h2></summary> |
| 113 |
<p class="muted mb-4">Point your own domain at your makenot.work profile.</p> |
| 114 |
|
| 115 |
{% match custom_domain %} |
| 116 |
{% when Some with (cd) %} |
| 117 |
<div class="profile-domain-header"> |
| 118 |
<strong>{{ cd.domain }}</strong> |
| 119 |
{% if cd.verified %} |
| 120 |
<span class="profile-domain-status is-verified">Verified</span> |
| 121 |
{% else %} |
| 122 |
<span class="profile-domain-status is-pending">Pending verification</span> |
| 123 |
{% endif %} |
| 124 |
</div> |
| 125 |
|
| 126 |
{% if !cd.verified %} |
| 127 |
<div class="callout callout--warning"> |
| 128 |
<p class="m-0 mb-3"><strong>DNS setup required:</strong> add two records at your domain registrar, then click Check DNS.</p> |
| 129 |
|
| 130 |
<p class="m-0 mb-2"><strong>1. Point your domain</strong> (routes visitors to your page)</p> |
| 131 |
<div class="dns-row"> |
| 132 |
<span class="dns-row-label">CNAME:</span> |
| 133 |
<code id="dns-route-host" class="dns-row-value">{{ cd.domain }}</code> |
| 134 |
<button type="button" class="btn-secondary dns-row-copy" |
| 135 |
data-action="copyElementText" data-arg="dns-route-host">Copy</button> |
| 136 |
</div> |
| 137 |
<div class="dns-row"> |
| 138 |
<span class="dns-row-label">Value:</span> |
| 139 |
<code id="dns-route-value" class="dns-row-value">connect.makenot.work</code> |
| 140 |
<button type="button" class="btn-secondary dns-row-copy" |
| 141 |
data-action="copyElementText" data-arg="dns-route-value">Copy</button> |
| 142 |
</div> |
| 143 |
<p class="callout-hint mb-3">On Cloudflare, set this record to <strong>DNS only</strong> (grey cloud), not Proxied. For an apex/root domain, use CNAME flattening or ALIAS.</p> |
| 144 |
|
| 145 |
<p class="m-0 mb-2"><strong>2. Verify ownership</strong></p> |
| 146 |
<div class="dns-row"> |
| 147 |
<span class="dns-row-label">TXT:</span> |
| 148 |
<code id="dns-record" class="dns-row-value">_mnw-verify.{{ cd.domain }}</code> |
| 149 |
<button type="button" class="btn-secondary dns-row-copy" |
| 150 |
data-action="copyElementText" data-arg="dns-record">Copy</button> |
| 151 |
</div> |
| 152 |
<div class="dns-row"> |
| 153 |
<span class="dns-row-label">Value:</span> |
| 154 |
<code id="dns-value" class="dns-row-value">{{ cd.verification_token }}</code> |
| 155 |
<button type="button" class="btn-secondary dns-row-copy" |
| 156 |
data-action="copyElementText" data-arg="dns-value">Copy</button> |
| 157 |
</div> |
| 158 |
<p class="callout-hint">DNS changes can take up to 24 hours to propagate. <a href="/docs/guide/custom-domains">Setup guide</a></p> |
| 159 |
</div> |
| 160 |
<div class="profile-domain-actions"> |
| 161 |
<button class="btn-primary" |
| 162 |
hx-post="/api/domains/verify" |
| 163 |
hx-vals='{"domain_id": "{{ cd.id }}"}' |
| 164 |
hx-target="#domain-verify-result" |
| 165 |
hx-swap="innerHTML" |
| 166 |
data-hx-behavior="refresh-profile-if-verified">Check DNS</button> |
| 167 |
<button class="btn-danger" |
| 168 |
hx-delete="/api/domains/{{ cd.id }}" |
| 169 |
hx-confirm="Remove this domain?" |
| 170 |
hx-target="closest .form-section" |
| 171 |
hx-swap="outerHTML" |
| 172 |
data-hx-behavior="refresh-profile">Remove</button> |
| 173 |
</div> |
| 174 |
<div id="domain-verify-result" class="profile-domain-result"></div> |
| 175 |
{% else %} |
| 176 |
<p class="profile-domain-lead"> |
| 177 |
Visitors to <strong>{{ cd.domain }}</strong> will see your profile, projects, and items. |
| 178 |
</p> |
| 179 |
<button class="btn-danger" |
| 180 |
hx-delete="/api/domains/{{ cd.id }}" |
| 181 |
hx-confirm="Remove this domain? Your domain will stop serving your content." |
| 182 |
hx-target="closest .form-section" |
| 183 |
hx-swap="outerHTML" |
| 184 |
data-hx-behavior="refresh-profile">Remove Domain</button> |
| 185 |
{% endif %} |
| 186 |
{% when None %} |
| 187 |
<form hx-post="/api/domains" |
| 188 |
hx-target="#domain-add-result" |
| 189 |
hx-swap="innerHTML" |
| 190 |
data-hx-behavior="refresh-profile"> |
| 191 |
<div class="field-row"> |
| 192 |
<div class="form-group is-grow"> |
| 193 |
<label for="custom-domain">Domain</label> |
| 194 |
<input type="text" id="custom-domain" name="domain" placeholder="mysite.com" required> |
| 195 |
</div> |
| 196 |
<button class="btn-primary" type="submit">Add Domain</button> |
| 197 |
</div> |
| 198 |
</form> |
| 199 |
<div id="domain-add-result" class="profile-domain-add-result"></div> |
| 200 |
{% endmatch %} |
| 201 |
</details> |
| 202 |
{% endif %} |
| 203 |
|
| 204 |
<details class="form-section"> |
| 205 |
<summary><h2 class="subsection-title">Personal Feed</h2></summary> |
| 206 |
<p class="muted profile-feed-lead">An RSS feed of new content from creators and projects you follow. Add this URL to your RSS reader.</p> |
| 207 |
<div class="profile-feed-row" id="feed-url-row"> |
| 208 |
<input type="text" id="feed-url" value="{{ feed_url }}" readonly class="profile-feed-input"> |
| 209 |
<button class="btn-secondary nowrap" type="button" |
| 210 |
data-action="copyFeedUrl">Copy URL</button> |
| 211 |
<button class="btn-secondary nowrap" type="button" |
| 212 |
hx-post="/dashboard/feed/regenerate" hx-target="#feed-url-row" hx-swap="outerHTML">Regenerate</button> |
| 213 |
</div> |
| 214 |
<p class="muted profile-feed-lead">Regenerating issues a new URL and immediately stops the old one from working. Use it if your feed link leaks.</p> |
| 215 |
</details> |
| 216 |
|