Skip to main content

max / makenotwork

14.1 KB · 317 lines History Blame Raw
1 <div class="tab-docs"><a href="/docs/pricing">Docs: Pricing &rarr;</a></div>
2
3 {% if can_create_projects %}
4 {% if user.stripe_connected %}
5 <!-- Stripe Account Status -->
6 <div class="stripe-status-box card-muted mb-5">
7 <div class="stack-row">
8 <div class="stack-row stack-row--tight">
9 {% if user.stripe_onboarding_complete && user.stripe_charges_enabled && user.stripe_payouts_enabled %}
10 <span class="badge badge-success">Ready</span>
11 <div>
12 <div class="fw-bold">Ready to receive payments</div>
13 <div class="text-xs dimmed">Stripe is fully configured</div>
14 </div>
15 {% else if !user.stripe_onboarding_complete %}
16 <span class="warn-glyph">!</span>
17 <div>
18 <div class="fw-bold">Action required: Complete Stripe setup</div>
19 <div class="text-xs dimmed">Provide identity verification, business details, and bank account</div>
20 </div>
21 {% else if !user.stripe_charges_enabled %}
22 <span class="warn-glyph">!</span>
23 <div>
24 <div class="fw-bold">Pending: Stripe is reviewing your account</div>
25 <div class="text-xs dimmed">Charges will be enabled within 1-2 business days</div>
26 </div>
27 {% else %}
28 <span class="warn-glyph">!</span>
29 <div>
30 <div class="fw-bold">Action required: Verify bank account</div>
31 <div class="text-xs dimmed">Set up payouts in your Stripe settings</div>
32 </div>
33 {% endif %}
34 </div>
35 {% if !user.stripe_onboarding_complete %}
36 <a href="/stripe/connect">
37 <button class="btn-primary text-sm">Complete Setup</button>
38 </a>
39 {% else %}
40 <a href="https://dashboard.stripe.com" target="_blank" rel="noopener">
41 <button class="btn-secondary text-sm">Stripe Dashboard</button>
42 </a>
43 {% endif %}
44 </div>
45
46 {% if let Some(account_id) = user.stripe_account_id %}
47 <details class="account-details">
48 <summary class="text-xs dimmer">Account details</summary>
49 <div class="account-details-id">{{ account_id }}</div>
50 </details>
51 {% endif %}
52 </div>
53
54 <div class="stripe-actions mb-5">
55 <a href="https://dashboard.stripe.com/payouts" target="_blank" rel="noopener">
56 <button class="btn-secondary">View Payouts</button>
57 </a>
58 <a href="https://dashboard.stripe.com/settings/payouts" target="_blank" rel="noopener">
59 <button class="btn-secondary">Manage Bank Account</button>
60 </a>
61 <button class="btn-danger stripe-actions-disconnect"
62 hx-delete="/api/users/me/stripe"
63 hx-confirm="Disconnect your Stripe account? You won't be able to receive payments until you reconnect."
64 hx-on::after-request="if(event.detail.successful) document.getElementById('tab-payments').click()">
65 Disconnect Stripe
66 </button>
67 </div>
68
69 <div class="section-group-label">Payouts</div>
70
71 {% if let Some(summary) = payout_summary %}
72 <div class="stripe-payout-summary card-muted mb-5">
73 <div class="stack-row stack-row--top">
74 <div>
75 <h3 class="card-h">Payout Summary</h3>
76 <p class="m-0 text-sm muted">
77 Payouts are processed automatically via Stripe Connect.
78 </p>
79 </div>
80 <a href="https://dashboard.stripe.com/payouts" target="_blank" rel="noopener">
81 <button class="btn-secondary text-sm">View in Stripe</button>
82 </a>
83 </div>
84
85 <div class="payout-stats-grid">
86 <div>
87 <div class="text-xs dimmed">Available Balance</div>
88 <div class="amount-big">{{ summary.available }}</div>
89 </div>
90 <div>
91 <div class="text-xs dimmed">Pending</div>
92 <div class="amount-big">{{ summary.pending }}</div>
93 </div>
94 </div>
95
96 {% if !user.stripe_payouts_enabled %}
97 <div class="payouts-disabled-notice">
98 Payouts are not yet enabled. Complete your Stripe account setup above.
99 </div>
100 {% endif %}
101 </div>
102 {% else if user.stripe_account_id.is_some() %}
103 <div class="card-muted mb-5">
104 <div class="stack-row">
105 <div>
106 <h3 class="card-h">Payout Summary</h3>
107 <p class="m-0 text-sm dimmed">Unable to load balance. Check your Stripe dashboard for details.</p>
108 </div>
109 <a href="https://dashboard.stripe.com/payouts" target="_blank" rel="noopener">
110 <button class="btn-secondary text-sm">View in Stripe</button>
111 </a>
112 </div>
113 </div>
114 {% endif %}
115
116 <div class="section-group-label">Settings</div>
117
118 <details class="form-section">
119 <summary><h2 class="subsection-title">Payment Settings</h2></summary>
120 <div class="card-muted mb-5">
121 <h3 class="card-h">Fee Breakdown</h3>
122 <div class="fee-breakdown">
123 <div class="fee-row">
124 <span class="muted">Payment processing</span>
125 <span>~2.9% + 30c</span>
126 </div>
127 <div class="fee-row">
128 <span class="muted">Platform fee</span>
129 <span class="fw-bold">$0.00 (0%)</span>
130 </div>
131 </div>
132 <p class="mt-3 mb-0 text-sm muted">
133 We take 0%. ~3% payment processing is the only cost.
134 </p>
135 </div>
136
137 <div class="card-muted">
138 <h3 class="card-h">Sales Tax</h3>
139 <form hx-put="/api/users/me/stripe-tax" hx-swap="none" data-success-toast="Sales tax preference saved">
140 <label class="checkbox-row">
141 <input type="checkbox" name="stripe_tax_enabled" class="check-inline"
142 {% if user.stripe_tax_enabled %}checked{% endif %}
143 onchange="this.form.requestSubmit()">
144 <span>Enable Stripe Tax on my checkouts</span>
145 </label>
146 </form>
147 <p class="mt-3 mb-0 text-sm muted">
148 When enabled, Stripe automatically calculates and collects applicable taxes
149 (VAT, sales tax) based on the buyer's location at checkout.
150 You are responsible for configuring your tax registrations in your
151 <a href="https://dashboard.stripe.com/settings/tax" target="_blank" rel="noopener" class="unstyled-link">Stripe Tax settings</a>.
152 </p>
153 </div>
154 </details>
155
156 <p class="text-sm dimmer mb-5">
157 Makenotwork uses Stripe Connect for secure payment processing. We never store your banking information.
158 </p>
159 {% else %}
160 <!-- Not Connected to Stripe -->
161 <div class="stripe-connect-prompt card-muted mb-5">
162 <div class="mb-4">
163 <svg width="80" height="33" viewBox="0 0 60 25" fill="none" xmlns="http://www.w3.org/2000/svg" class="stripe-logo">
164 <path fill-rule="evenodd" clip-rule="evenodd" d="M59.64 14.28C59.64 9.94 57.58 6.52 53.52 6.52C49.44 6.52 47.04 9.94 47.04 14.24C47.04 19.38 49.88 22.04 54.04 22.04C56.08 22.04 57.6 21.56 58.76 20.92V17.74C57.6 18.32 56.24 18.68 54.52 18.68C52.84 18.68 51.36 18.1 51.18 16.06H59.6C59.6 15.82 59.64 14.8 59.64 14.28ZM51.12 13.16C51.12 11.22 52.24 10.38 53.5 10.38C54.72 10.38 55.78 11.22 55.78 13.16H51.12ZM40.68 6.52C38.98 6.52 37.92 7.34 37.32 7.9L37.12 6.78H33.38V24.5L37.54 23.62L37.56 17.8C38.18 18.24 39.08 18.86 40.66 18.86C43.86 18.86 46.78 16.3 46.78 12.52C46.76 9.08 43.82 6.52 40.68 6.52ZM39.7 15.42C38.62 15.42 37.98 15.06 37.56 14.62L37.54 10.58C38 10.1 38.66 9.76 39.7 9.76C41.34 9.76 42.48 11.52 42.48 12.58C42.48 13.68 41.36 15.42 39.7 15.42ZM28.3 5.58L32.48 4.68V1.42L28.3 2.3V5.58ZM28.3 6.78H32.48V18.58H28.3V6.78ZM23.94 7.96L23.68 6.78H20.02V18.58H24.18V11.08C25.1 9.92 26.66 10.14 27.14 10.3V6.78C26.64 6.6 24.86 6.26 23.94 7.96ZM15.68 2.82L11.62 3.68L11.6 15.1C11.6 17.12 13.14 18.86 15.16 18.86C16.28 18.86 17.08 18.66 17.52 18.42V15.16C17.1 15.34 15.66 15.76 15.66 13.74V10.06H17.52V6.78H15.66L15.68 2.82ZM7.36 10.64C7.36 10 7.9 9.74 8.76 9.74C10 9.74 11.54 10.12 12.78 10.82V6.98C11.42 6.42 10.08 6.18 8.76 6.18C5.44 6.18 3.2 7.98 3.2 10.88C3.2 15.4 9.38 14.64 9.38 16.56C9.38 17.32 8.7 17.58 7.78 17.58C6.42 17.58 4.72 17.02 3.34 16.22V20.12C4.88 20.78 6.44 21.06 7.78 21.06C11.18 21.06 13.56 19.32 13.56 16.38C13.54 11.5 7.36 12.42 7.36 10.64Z" fill="#635BFF"/>
165 </svg>
166 </div>
167 <p class="mb-4 muted">
168 Connect your Stripe account to start receiving payments from your sales.
169 </p>
170 <ul class="benefit-list">
171 <li>Direct deposits to your bank</li>
172 <li>Automatic 1099 tax forms</li>
173 <li>Real-time sales dashboard</li>
174 </ul>
175 <a href="/stripe/connect">
176 <button class="btn-primary stripe-connect-btn">
177 Connect with Stripe
178 </button>
179 </a>
180 </div>
181 {% endif %}
182 {% endif %}
183
184 <div class="section-group-label">History</div>
185
186 {% if can_create_projects %}
187 {% if splits_incoming_count > 0 || splits_outgoing_total != "$0.00" %}
188 <details class="form-section" open>
189 <summary><h2 class="subsection-title">Collaborator Payouts</h2></summary>
190 <div class="export-row">
191 <button class="btn-secondary text-sm"
192 onclick="this.textContent='Exporting...'; this.disabled=true; var btn=this; fetch('/api/export/splits', {method:'POST', headers: csrfHeaders()}).then(function(r){return r.blob()}).then(function(b){var a=document.createElement('a'); a.href=URL.createObjectURL(b); a.download='collaborator-payouts.csv'; a.click(); btn.textContent='Export CSV'; btn.disabled=false;}).catch(function(){btn.textContent='Export CSV'; btn.disabled=false; showToast('Export failed')});">Export CSV</button>
193 </div>
194 <div class="splits-grid">
195 {% if splits_incoming_count > 0 %}
196 <div class="card-muted">
197 <div class="text-xs dimmed">Owed to you (as collaborator)</div>
198 <div class="amount-big">{{ splits_incoming_total }}</div>
199 <div class="text-sm muted">from {{ splits_incoming_count }} sales</div>
200 </div>
201 {% endif %}
202 {% if splits_outgoing_total != "$0.00" %}
203 <div class="card-muted">
204 <div class="text-xs dimmed">You owe collaborators</div>
205 <div class="amount-big">{{ splits_outgoing_total }}</div>
206 </div>
207 {% endif %}
208 </div>
209 <p class="text-sm dimmed">
210 Collaborator payouts are recorded automatically when sales complete on projects with collaborators.
211 Payouts to collaborators are currently settled between project members directly.
212 We plan to automate this process in a future update.
213 </p>
214 </details>
215 {% endif %}
216
217 {% if tips_count > 0 %}
218 <details class="form-section" open>
219 <summary><h2 class="subsection-title">Tips Received ({{ tips_count }})</h2></summary>
220 <div class="card-muted mb-5">
221 <div class="amount-big mb-2">{{ tips_total }} total</div>
222 <div class="text-sm muted">{{ tips_count }} tips received</div>
223 </div>
224 <table class="data-table">
225 <thead>
226 <tr>
227 <th>Date</th>
228 <th>From</th>
229 <th>Amount</th>
230 <th>Message</th>
231 </tr>
232 </thead>
233 <tbody>
234 {% for tip in tips_received %}
235 <tr>
236 <td>{{ tip.date }}</td>
237 <td>{{ tip.tipper_name }}</td>
238 <td>{{ tip.amount }}</td>
239 <td class="tip-message">{% if let Some(msg) = tip.message %}{{ msg }}{% else %}<span class="dimmer">-</span>{% endif %}</td>
240 </tr>
241 {% endfor %}
242 </tbody>
243 </table>
244 </details>
245 {% endif %}
246 {% endif %}
247
248 <div class="transactions-header">
249 <h2 class="m-0 transactions-title">Transactions</h2>
250 <button class="btn-secondary text-sm"
251 hx-post="/api/export/sales"
252 hx-swap="none"
253 onclick="this.textContent='Exporting...'; this.disabled=true; var btn=this; fetch('/api/export/sales', {method:'POST', headers: csrfHeaders()}).then(function(r){return r.blob()}).then(function(b){var a=document.createElement('a'); a.href=URL.createObjectURL(b); a.download='sales.csv'; a.click(); btn.textContent='Export CSV'; btn.disabled=false;}).catch(function(){btn.textContent='Export CSV'; btn.disabled=false; showToast('Export failed')});">Export CSV</button>
254 </div>
255
256 <div class="filter-controls">
257 <div class="filter-group">
258 <select name="type"
259 hx-get="/dashboard/transactions"
260 hx-target="#transactions-tbody"
261 hx-include="[name='period']">
262 <option value="">All Transactions</option>
263 {% if can_create_projects %}
264 <option value="incoming">Sales</option>
265 <option value="outgoing">Payouts</option>
266 <option value="refund">Refunds</option>
267 {% endif %}
268 <option value="purchases">Purchases</option>
269 </select>
270 </div>
271 <div class="filter-group">
272 <select name="period"
273 hx-get="/dashboard/transactions"
274 hx-target="#transactions-tbody"
275 hx-include="[name='type']">
276 <option value="">All Time</option>
277 <option value="month">This Month</option>
278 <option value="3months">Last 3 Months</option>
279 <option value="year">Last Year</option>
280 </select>
281 </div>
282 </div>
283
284 <table class="data-table">
285 <thead>
286 <tr>
287 <th>Date</th>
288 <th>Type</th>
289 <th>Description</th>
290 <th>Amount</th>
291 <th>Status</th>
292 <th>Details</th>
293 </tr>
294 </thead>
295 <tbody id="transactions-tbody">
296 {% for tx in transactions %}
297 <tr>
298 <td>{{ tx.date }}</td>
299 <td>{{ tx.tx_type }}</td>
300 <td>{{ tx.description }}</td>
301 <td class="{% if tx.is_incoming %}amount-in{% else %}amount-out{% endif %}">{{ tx.amount }}</td>
302 <td><span class="badge {{ tx.status|lowercase }}">{{ tx.status }}</span></td>
303 <td>{{ tx.details }}</td>
304 </tr>
305 {% endfor %}
306 {% if transactions.is_empty() %}
307 <tr>
308 <td colspan="6" class="text-center dimmed">No transactions found</td>
309 </tr>
310 {% endif %}
311 </tbody>
312 </table>
313
314 {% if can_create_projects %}
315 <div hx-get="/dashboard/tabs/contacts" hx-trigger="revealed" hx-swap="innerHTML" id="contacts-section"></div>
316 {% endif %}
317