Skip to main content

max / makenotwork

14.4 KB · 324 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-pane">
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" data-tone="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-pane">
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 data-after="click" data-target="tab-payments">
65 Disconnect Stripe
66 </button>
67 </div>
68
69 <div class="section-group-label">Payouts</div>
70
71 {% if user.stripe_account_id.is_some() %}
72 <!-- Stripe balance is lazy-loaded so the tab render never blocks on the
73 Stripe round-trip (ultra-fuzz Run 11 Perf SER-2). -->
74 <div hx-get="/dashboard/tabs/payout-summary" hx-trigger="revealed" hx-swap="innerHTML" id="payout-summary-section">
75 <div class="card-muted mb-pane">
76 <h3 class="card-h">Payout Summary</h3>
77 <p class="m-0 text-sm muted">Loading balance.</p>
78 </div>
79 </div>
80 {% endif %}
81
82 <div class="section-group-label">Settings</div>
83
84 <details class="form-section">
85 <summary><h2 class="subsection-title">Payment Settings</h2></summary>
86 <div class="card-muted mb-pane">
87 <h3 class="card-h">Fee Breakdown</h3>
88 <div class="fee-breakdown">
89 <div class="fee-row">
90 <span class="muted">Payment processing</span>
91 <span>~2.9% + 30c</span>
92 </div>
93 <div class="fee-row">
94 <span class="muted">Platform fee</span>
95 <span class="fw-bold">$0.00 (0%)</span>
96 </div>
97 </div>
98 <p class="mt-section mb-0 text-sm muted">
99 We take 0%. ~3% payment processing is the only cost.
100 </p>
101 </div>
102
103 <div class="card-muted">
104 <h3 class="card-h">Sales Tax</h3>
105 <form hx-put="/api/users/me/stripe-tax" hx-swap="none" data-success-toast="Sales tax preference saved">
106 <label class="checkbox-row">
107 <input type="checkbox" name="stripe_tax_enabled" class="check-inline"
108 {% if user.stripe_tax_enabled %}checked{% endif %}
109 data-change="submit-form">
110 <span>Enable Stripe Tax on my checkouts</span>
111 </label>
112 </form>
113 <p class="mt-section mb-0 text-sm muted">
114 When enabled, Stripe automatically calculates and collects applicable taxes
115 (VAT, sales tax) based on the buyer's location at checkout.
116 You are responsible for configuring your tax registrations in your
117 <a href="https://dashboard.stripe.com/settings/tax" target="_blank" rel="noopener" class="unstyled-link">Stripe Tax settings</a>.
118 </p>
119 </div>
120 </details>
121
122 <p class="text-sm dimmer mb-pane">
123 Makenotwork uses Stripe Connect for secure payment processing. We never store your banking information.
124 </p>
125 {% else %}
126 <!-- Not Connected to Stripe -->
127 <div class="stripe-connect-prompt card-muted mb-pane">
128 <div class="mb-section">
129 <svg width="80" height="33" viewBox="0 0 60 25" fill="none" xmlns="http://www.w3.org/2000/svg" class="stripe-logo">
130 <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"/>
131 </svg>
132 </div>
133 <p class="mb-section muted">
134 Connect your Stripe account to start receiving payments from your sales.
135 </p>
136 <ul class="benefit-list">
137 <li>Direct deposits to your bank</li>
138 <li>Automatic 1099 tax forms</li>
139 <li>Real-time sales dashboard</li>
140 </ul>
141 <a href="/stripe/connect">
142 <button class="btn-primary stripe-connect-btn">
143 Connect with Stripe
144 </button>
145 </a>
146 </div>
147 {% endif %}
148 {% endif %}
149
150 <div class="section-group-label">History</div>
151
152 {% if can_create_projects %}
153 {% if splits_incoming_count > 0 || splits_outgoing_total != "$0.00" %}
154 <details class="form-section" open>
155 <summary><h2 class="subsection-title">Collaborator Payouts</h2></summary>
156 <div class="export-row">
157 {{ crate::quasi::export_act::html("/api/export/splits", "collaborator-payouts.csv")|safe }}
158 </div>
159 {% if !pending_invitations.is_empty() %}
160 <div class="split-invitations">
161 <h3 class="section-header">Waiting on you</h3>
162 {% for invite in pending_invitations %}
163 <div class="card-muted split-invitation">
164 <p>
165 <strong>@{{ invite.owner_username }}</strong> invited you to
166 <strong>{{ invite.split_percent }}%</strong> of
167 <a href="/p/{{ invite.project_slug }}">{{ invite.project_title }}</a>.
168 </p>
169 {% if invite.project_currency != own_currency %}
170 <p class="text-sm muted">
171 This project sells in {{ invite.project_currency }} and you are paid in
172 {{ own_currency }}. Your share arrives in {{ invite.project_currency }} and
173 Stripe converts it when it reaches you, so the conversion comes out of your
174 share, not the project's. We cannot tell you the rate in advance because
175 Stripe sets it at payout.
176 </p>
177 {% endif %}
178 <p class="text-sm muted">
179 If you accept, your share starts from that moment. Sales before then stay
180 with @{{ invite.owner_username }}.
181 </p>
182 <div class="split-invitation-actions">
183 <form method="post" action="/api/projects/{{ invite.project_id }}/members/accept">
184 {% if let Some(token) = csrf_token %}<input type="hidden" name="_csrf" value="{{ token }}">{% endif %}
185 <button type="submit" class="btn-primary">Accept {{ invite.split_percent }}%</button>
186 </form>
187 <form method="post" action="/api/projects/{{ invite.project_id }}/members/decline">
188 {% if let Some(token) = csrf_token %}<input type="hidden" name="_csrf" value="{{ token }}">{% endif %}
189 <button type="submit" class="btn-secondary">Decline</button>
190 </form>
191 </div>
192 </div>
193 {% endfor %}
194 </div>
195 {% endif %}
196
197 <div class="splits-grid">
198 {% if splits_incoming_count > 0 %}
199 <div class="card-muted">
200 <div class="text-xs dimmed">Owed to you (as collaborator)</div>
201 <div class="amount-big">{{ splits_incoming_total }}</div>
202 <div class="text-sm muted">from {{ splits_incoming_count }} sales</div>
203 {% if splits_incoming_foreign %}
204 <div class="text-sm muted">
205 Some of this is in a currency you don't get paid in. It is denominated in
206 the currency of the project that earned it, and Stripe converts it when it
207 reaches you, so the conversion comes out of your share.
208 </div>
209 {% endif %}
210 </div>
211 {% endif %}
212 {% if splits_outgoing_any %}
213 <div class="card-muted">
214 <div class="text-xs dimmed">You owe collaborators</div>
215 <div class="amount-big">{{ splits_outgoing_total }}</div>
216 </div>
217 {% endif %}
218 </div>
219 <p class="text-sm dimmed">
220 Collaborator payouts are recorded automatically when sales complete on projects with collaborators.
221 Payouts to collaborators are currently settled between project members directly.
222 We plan to automate this process in a future update.
223 </p>
224 </details>
225 {% endif %}
226
227 {% if tips_count > 0 %}
228 <details class="form-section" open>
229 <summary><h2 class="subsection-title">Tips Received ({{ tips_count }})</h2></summary>
230 <div class="card-muted mb-pane">
231 <div class="amount-big mb-peer">{{ tips_total }} total</div>
232 <div class="text-sm muted">{{ tips_count }} tips received</div>
233 </div>
234 <table class="data-table well">
235 <thead>
236 <tr>
237 <th>Date</th>
238 <th>From</th>
239 <th>Amount</th>
240 <th>Message</th>
241 </tr>
242 </thead>
243 <tbody>
244 {% for tip in tips_received %}
245 <tr>
246 <td>{{ tip.date }}</td>
247 <td>{{ tip.tipper_name }}</td>
248 <td>{{ tip.amount }}</td>
249 <td class="tip-message">{% if let Some(msg) = tip.message %}{{ msg }}{% else %}<span class="dimmer">-</span>{% endif %}</td>
250 </tr>
251 {% endfor %}
252 </tbody>
253 </table>
254 </details>
255 {% endif %}
256 {% endif %}
257
258 <div class="transactions-header">
259 <h2 class="m-0 transactions-title">Transactions</h2>
260 {{ crate::quasi::export_act::html("/api/export/sales", "sales.csv")|safe }}
261 </div>
262
263 <div class="filter-controls">
264 <div class="filter-group">
265 <select name="type"
266 hx-get="/dashboard/transactions"
267 hx-target="#transactions-tbody"
268 hx-include="[name='period']">
269 <option value="">All Transactions</option>
270 {% if can_create_projects %}
271 <option value="incoming">Sales</option>
272 <option value="outgoing">Payouts</option>
273 <option value="refund">Refunds</option>
274 {% endif %}
275 <option value="purchases">Purchases</option>
276 </select>
277 </div>
278 <div class="filter-group">
279 <select name="period"
280 hx-get="/dashboard/transactions"
281 hx-target="#transactions-tbody"
282 hx-include="[name='type']">
283 <option value="">All Time</option>
284 <option value="month">This Month</option>
285 <option value="3months">Last 3 Months</option>
286 <option value="year">Last Year</option>
287 </select>
288 </div>
289 </div>
290
291 <table class="data-table well">
292 <thead>
293 <tr>
294 <th>Date</th>
295 <th>Type</th>
296 <th>Description</th>
297 <th>Amount</th>
298 <th>Status</th>
299 <th>Details</th>
300 </tr>
301 </thead>
302 <tbody id="transactions-tbody">
303 {% for tx in transactions %}
304 <tr>
305 <td>{{ tx.date }}</td>
306 <td>{{ tx.tx_type }}</td>
307 <td>{{ tx.description }}</td>
308 <td class="{% if tx.is_incoming %}amount-in{% else %}amount-out{% endif %}">{{ tx.amount }}</td>
309 <td><span class="badge" data-tone="{{ tx.status_tone }}">{{ tx.status }}</span></td>
310 <td>{{ tx.details }}</td>
311 </tr>
312 {% endfor %}
313 {% if transactions.is_empty() %}
314 <tr>
315 <td colspan="6" class="text-center dimmed">No transactions found</td>
316 </tr>
317 {% endif %}
318 </tbody>
319 </table>
320
321 {% if can_create_projects %}
322 <div hx-get="/dashboard/tabs/contacts" hx-trigger="revealed" hx-swap="innerHTML" id="contacts-section"></div>
323 {% endif %}
324