Skip to main content

max / makenotwork

15.7 KB · 338 lines History Blame Raw
1 {% extends "base.html" %}
2 {%- import "partials/carousel.html" as carousel -%}
3
4 {% block title %}{{ item.title }} - Makenotwork{% endblock %}
5 {% block body_attrs %} class="padded-page item-page"{% endblock %}
6
7 {% block head %}
8 {# Tier 0 creator theme (inherited from parent project): primitive-layer override. #}
9 <style id="creator-theme">{{ theme_css|safe }}</style>
10 <meta property="og:title" content="{{ item.title }} - {{ creator_username }}">
11 <meta property="og:description" content="{{ item.description }}">
12 <meta property="og:type" content="product">
13 <meta property="og:url" content="{{ host_url }}/i/{{ item.id }}">
14 <link rel="canonical" href="{{ host_url }}/i/{{ item.id }}">
15 <meta property="og:site_name" content="Makenot.work">
16 <meta property="product:price:amount" content="{{ item.price }}">
17 {% if item.cover_image_url.is_some() || project_cover_image_url.is_some() %}<meta name="twitter:card" content="summary_large_image">{% else %}<meta name="twitter:card" content="summary">{% endif %}
18 <meta name="twitter:title" content="{{ item.title }} - {{ creator_username }}">
19 <meta name="twitter:description" content="{{ item.description }}">
20 {% if let Some(img) = item.cover_image_url %}
21 <meta property="og:image" content="{{ img }}">
22 <meta name="twitter:image" content="{{ img }}">
23 {% else if let Some(img) = project_cover_image_url %}
24 <meta property="og:image" content="{{ img }}">
25 <meta name="twitter:image" content="{{ img }}">
26 {% else %}
27 <meta property="og:image" content="{{ host_url }}/static/images/og-card.png">
28 <meta name="twitter:image" content="{{ host_url }}/static/images/og-card.png">
29 {% endif %}
30 <script type="application/ld+json">
31 {
32 "@context": "https://schema.org",
33 "@type": "Product",
34 "name": "{{ item.title_json()|safe }}",
35 "description": "{{ item.description_json()|safe }}",
36 "url": "{{ host_url }}/i/{{ item.id }}",
37 "brand": {
38 "@type": "Person",
39 "name": "{{ creator_username }}",
40 "url": "{{ host_url }}/u/{{ creator_username }}"
41 },
42 "offers": {
43 "@type": "Offer",
44 "price": "{{ item.price_decimal() }}",
45 "priceCurrency": "USD",
46 "availability": "https://schema.org/InStock",
47 "url": "{{ host_url }}/i/{{ item.id }}"
48 },
49 "category": "{{ item.item_type }}"{% if let Some(img) = item.cover_image_url %},
50 "image": "{{ img }}"{% endif %}
51 }
52 </script>
53 {% endblock %}
54
55 {% block content %}
56 {% include "partials/site_header.html" %}
57
58 <div class="container">
59 <nav class="breadcrumb">
60 <div>
61 <a href="/">Home</a> /
62 <a href="/u/{{ creator_username }}">{{ creator_username }}</a> /
63 <a href="/p/{{ project_slug }}">{{ project_title }}</a> /
64 <span>{{ item.title }}</span>
65 </div>
66 {% if is_owner %}
67 <a href="/dashboard/item/{{ item.id }}" class="breadcrumb-edit-link">Edit Item</a>
68 {% endif %}
69 </nav>
70
71 <div class="item-layout{% if item.cover_image_url.is_none() && item.item_type != "video" %} no-media{% endif %}">
72 {% match item.content %}
73 {% when ItemContent::Video with { video_s3_key, cover_url, .. } %}
74 <div class="item-media content-section">
75 <div id="video-container">
76 <video id="item-player" controls preload="metadata"
77 {% if let Some(url) = cover_url %}poster="{{ url }}"{% endif %}
78 class="video-player">
79 Your browser does not support the video tag.
80 </video>
81 </div>
82 </div>
83 {% when _ %}
84 {% if item.cover_image_url.is_some() %}
85 <div class="item-media content-section">
86 {% if let Some(img) = item.cover_image_url %}
87 <img src="{{ img }}" alt="{{ item.title }}" class="item-cover-img">
88 {% endif %}
89 </div>
90 {% endif %}
91 {% endmatch %}
92
93 <div class="item-details content-section">
94 <h1 class="item-title">{{ item.title }}</h1>
95 <div class="item-creator">
96 by <a href="/u/{{ creator_username }}">{{ creator_username }}</a>
97 </div>
98
99 <div class="item-price">{{ item.price }}</div>
100
101 {# AI disclosure tier — visible before purchase per
102 site-docs/public/about/generative-ai.md. #}
103 <div class="item-ai-tier">
104 <span class="badge ai-tier ai-tier-{{ item.ai_tier }}">{{ item.ai_tier.label() }}</span>
105 </div>
106
107 <div class="item-meta">
108 <div class="meta-item">
109 <div class="meta-label">Type</div>
110 <div class="meta-value">{{ item.item_type }}</div>
111 </div>
112 <div class="meta-item">
113 <div class="meta-label">Released</div>
114 <div class="meta-value">{{ item.release_date }}</div>
115 </div>
116 <div class="meta-item">
117 <div class="meta-label">Sales</div>
118 <div class="meta-value">{{ item.sales_count }}</div>
119 </div>
120 </div>
121
122 <div class="item-tags">
123 {% for tag in item.tags %}
124 <a href="/discover?tag={{ tag.slug }}" class="tag tag-link{% if tag.is_primary %} tag-primary{% endif %}">{{ tag.name }}</a>
125 {% endfor %}
126 </div>
127
128 {% if !item.listed %}
129 <div class="bundle-notice bundle-notice-inline">
130 <p class="bundle-notice-title">Included in a bundle</p>
131 <p class="bundle-notice-desc">This item isn't sold separately. You can get it as part of:</p>
132 {% for bundle in containing_bundles %}
133 <p class="bundle-notice-entry"><a href="/i/{{ bundle.id }}">{{ bundle.title }}</a> <span class="bundle-notice-price">{{ bundle.price }}</span></p>
134 {% endfor %}
135 {% if containing_bundles.is_empty() %}
136 <p class="bundle-notice-empty">No bundles currently available for this item.</p>
137 {% endif %}
138 </div>
139 {% else %}
140 {# Assisted items disclose AI use above the buy CTA so
141 fans see it before purchase. Handmade and Generated
142 carry the badge above; only Assisted owes prose. #}
143 {% if let crate::db::AiTier::Assisted = item.ai_tier %}
144 {% if let Some(disclosure) = item.ai_disclosure.as_ref() %}
145 <div class="ai-disclosure">
146 <div class="ai-disclosure-label">AI use</div>
147 <div class="ai-disclosure-text">{{ disclosure }}</div>
148 </div>
149 {% endif %}
150 {% endif %}
151
152 <div class="purchase-box card-muted">
153 <h3>What's included:</h3>
154 <ul>
155 {% if item.item_type == "bundle" %}
156 <li>{{ bundle_items.len() }} items in this bundle</li>
157 {% endif %}
158 <li>Lifetime access to all versions</li>
159 <li>Automatic update notifications</li>
160 <li>Direct creator support</li>
161 </ul>
162 </div>
163
164 {% if has_access %}
165 <a href="/l/{{ item.id }}" class="btn-primary">View in library &rarr;</a>
166 {% else if item.is_free %}
167 <button class="btn-primary"
168 hx-post="/api/library/add/{{ item.id }}"
169 hx-swap="outerHTML">Add to Library - Free</button>
170 {% else %}
171 <details class="promo-details">
172 <summary class="promo-summary">Have a promo code?</summary>
173 <form hx-post="/api/promo-codes/claim"
174 hx-swap="outerHTML"
175 class="promo-form">
176 <input type="hidden" name="item_id" value="{{ item.id }}">
177 <input type="text" name="code" placeholder="Enter code" required
178 class="promo-input">
179 <button class="btn-secondary nowrap" type="submit">Apply</button>
180 </form>
181 </details>
182
183 {% if item.pwyw_enabled %}
184 <a href="/purchase/{{ item.id }}" class="btn-primary">Pay What You Want - {{ item.price }}</a>
185 {% else %}
186 <a href="/purchase/{{ item.id }}" class="btn-primary">Buy Once - {{ item.price }}</a>
187 {% endif %}
188
189 <div class="payment-note">
190 Secure payment processing
191 </div>
192 {% endif %}
193 {% endif %}
194
195 {% if session_user.is_some() %}
196 {% if !is_owner %}
197 <div class="action-row">
198 <button class="btn-secondary full-width-btn" id="cart-btn"
199 data-action="toggleCart" data-arg="{{ item.id }}">{% if in_cart %}In Cart{% else %}Add to Cart{% endif %}</button>
200 </div>
201 <div class="action-row-tight">
202 <button class="btn-secondary full-width-btn" id="wishlist-btn"
203 data-action="toggleWishlist" data-arg="{{ item.id }}">{% if is_wishlisted %}Wishlisted{% else %}Add to Wishlist{% endif %}</button>
204 </div>
205 {% endif %}
206 <div class="action-row-tight collection-picker-anchor">
207 <button class="btn-secondary full-width-btn{% if collection_count > 0 %} saved{% endif %}"
208 data-collection-trigger data-item-id="{{ item.id }}" data-collection-label
209 data-action="onOpenCollectionPicker" data-arg="{{ item.id }}">{% if collection_count > 0 %}Saved ({{ collection_count }}){% else %}Save to collection{% endif %}</button>
210 </div>
211 {% endif %}
212 </div>
213 </div>
214
215 {% if !gallery.is_empty() %}
216 <section class="item-gallery content-section">
217 {% call carousel::carousel("item-gallery", gallery) %}{% endcall %}
218 </section>
219 {% endif %}
220
221 <section class="item-description content-section">
222 <h2 class="section-header">Description</h2>
223 <div>
224 <p>{{ item.description }}</p>
225 </div>
226 </section>
227
228 {% if !sections.is_empty() %}
229 <section class="item-sections content-section">
230 <div class="section-tabs">
231 {% for section in sections %}
232 <button class="section-tab{% if loop.first %} is-selected{% endif %}"
233 data-tab="section-{{ section.slug }}"
234 data-action="onSwitchSectionTab" data-arg="section-{{ section.slug }}">{{ section.title }}</button>
235 {% endfor %}
236 </div>
237 {% for section in sections %}
238 <div class="section-panel{% if loop.first %} active{% endif %}" id="section-{{ section.slug }}">
239 {{ section.body_html|safe }}
240 </div>
241 {% endfor %}
242 </section>
243 {% endif %}
244
245 {% if item.license_preset.is_some() %}
246 <section class="item-description content-section">
247 <h2 class="section-header">License</h2>
248 <p class="license-preset-name">
249 {% if item.license_preset.as_deref() == Some("personal_use") %}Personal Use Only
250 {% else if item.license_preset.as_deref() == Some("royalty_free") %}Royalty-Free Commercial
251 {% else if item.license_preset.as_deref() == Some("mit") %}MIT License
252 {% else if item.license_preset.as_deref() == Some("apache2") %}Apache License 2.0
253 {% else if item.license_preset.as_deref() == Some("cc_by_4") %}CC BY 4.0
254 {% else if item.license_preset.as_deref() == Some("cc_by_nc_4") %}CC BY-NC 4.0
255 {% else if item.license_preset.as_deref() == Some("cc0") %}Public Domain (CC0)
256 {% else if item.license_preset.as_deref() == Some("custom") %}Custom License
257 {% else %}License
258 {% endif %}
259 </p>
260 <details>
261 <summary class="license-summary">View full license text</summary>
262 <pre class="license-text" id="license-text-content">Loading...</pre>
263 <div id="page-item-1-cfg" hidden data-item-id="{{ item.id }}"></div>
264 <script src="/static/page-item-1.js?v=0623" defer></script>
265 </details>
266 <p class="license-download">
267 <a href="/api/items/{{ item.id }}/license.txt" download="LICENSE.txt">Download LICENSE.txt</a>
268 </p>
269 </section>
270 {% endif %}
271
272 {% if !bundle_items.is_empty() %}
273 <section class="bundle-contents content-section">
274 <h2 class="section-header">What's Included ({{ bundle_items.len() }} items)</h2>
275 {% for child in bundle_items %}
276 <div class="bundle-child list-row">
277 <span class="bundle-child-type">{{ child.item_type }}</span>
278 <span class="bundle-child-title">
279 {% if child.listed %}
280 <a href="/i/{{ child.id }}">{{ child.title }}</a>
281 {% else %}
282 {{ child.title }}
283 {% endif %}
284 </span>
285 {% if child.price_cents > 0 %}
286 <span class="bundle-child-price">{{ child.price }}</span>
287 {% else %}
288 <span class="bundle-child-price">Free</span>
289 {% endif %}
290 </div>
291 {% endfor %}
292 </section>
293 {% endif %}
294
295 {% if !containing_bundles.is_empty() && !item.listed %}
296 <section class="bundle-notice">
297 <p>This item is available as part of:</p>
298 {% for bundle in containing_bundles %}
299 <p class="bundle-list-entry"><a href="/i/{{ bundle.id }}">{{ bundle.title }} - {{ bundle.price }}</a></p>
300 {% endfor %}
301 </section>
302 {% endif %}
303
304 {% include "partials/discussion_section.html" %}
305
306 <footer class="item-footer">
307 <p>Powered by <a href="/">Makenot<span class="dot">.</span>work</a> &middot; Fair distribution for creatives of all kinds</p>
308 <p class="footer-links">
309 {% if is_owner %}
310 <a href="/dashboard/item/{{ item.id }}">Edit</a> &middot;
311 <a href="/dashboard/item/{{ item.id }}#embed">Embed</a> &middot;
312 {% endif %}
313 <a href="/i/{{ item.id }}" data-copy-link>Copy link</a> &middot;
314 {% if session_user.is_some() %}
315 <a href="#" data-prevent data-action="show" data-target="report-modal">Report this item</a> &middot;
316 {% else %}
317 <a href="/login">Report this item</a> &middot;
318 {% endif %}
319 <a href="/policy">Policy</a>
320 </p>
321 </footer>
322 </div>
323
324 {% if session_user.is_some() %}
325 {% let report_target_type = "item" %}
326 {% let report_target_id = item.id %}
327 {% let report_has_labels = true %}
328 {% include "partials/report_modal.html" %}
329 {% endif %}
330 {% endblock %}
331
332 {% block scripts %}
333 <div id="page-item-2-cfg" hidden data-item-id="{{ item.id }}"></div>
334 <script src="/static/collections.js?v=0514"></script>
335 <script src="/static/page-item-2.js?v=0623" defer></script>
336 <script src="/static/page-item-3.js?v=0623" defer></script>
337 {% endblock %}
338