Skip to main content

max / makenotwork

14.6 KB · 328 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="submitOwnForm">
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 <button class="btn-secondary text-sm"
158 data-action="exportCsvButton" data-arg="/api/export/splits" data-arg2="collaborator-payouts.csv">Export CSV</button>
159 </div>
160 {% if !pending_invitations.is_empty() %}
161 <div class="split-invitations">
162 <h3 class="section-header">Waiting on you</h3>
163 {% for invite in pending_invitations %}
164 <div class="card-muted split-invitation">
165 <p>
166 <strong>@{{ invite.owner_username }}</strong> invited you to
167 <strong>{{ invite.split_percent }}%</strong> of
168 <a href="/p/{{ invite.project_slug }}">{{ invite.project_title }}</a>.
169 </p>
170 {% if invite.project_currency != own_currency %}
171 <p class="text-sm muted">
172 This project sells in {{ invite.project_currency }} and you are paid in
173 {{ own_currency }}. Your share arrives in {{ invite.project_currency }} and
174 Stripe converts it when it reaches you, so the conversion comes out of your
175 share, not the project's. We cannot tell you the rate in advance because
176 Stripe sets it at payout.
177 </p>
178 {% endif %}
179 <p class="text-sm muted">
180 If you accept, your share starts from that moment. Sales before then stay
181 with @{{ invite.owner_username }}.
182 </p>
183 <div class="split-invitation-actions">
184 <form method="post" action="/api/projects/{{ invite.project_id }}/members/accept">
185 {% if let Some(token) = csrf_token %}<input type="hidden" name="_csrf" value="{{ token }}">{% endif %}
186 <button type="submit" class="btn-primary">Accept {{ invite.split_percent }}%</button>
187 </form>
188 <form method="post" action="/api/projects/{{ invite.project_id }}/members/decline">
189 {% if let Some(token) = csrf_token %}<input type="hidden" name="_csrf" value="{{ token }}">{% endif %}
190 <button type="submit" class="btn-secondary">Decline</button>
191 </form>
192 </div>
193 </div>
194 {% endfor %}
195 </div>
196 {% endif %}
197
198 <div class="splits-grid">
199 {% if splits_incoming_count > 0 %}
200 <div class="card-muted">
201 <div class="text-xs dimmed">Owed to you (as collaborator)</div>
202 <div class="amount-big">{{ splits_incoming_total }}</div>
203 <div class="text-sm muted">from {{ splits_incoming_count }} sales</div>
204 {% if splits_incoming_foreign %}
205 <div class="text-sm muted">
206 Some of this is in a currency you don't get paid in. It is denominated in
207 the currency of the project that earned it, and Stripe converts it when it
208 reaches you, so the conversion comes out of your share.
209 </div>
210 {% endif %}
211 </div>
212 {% endif %}
213 {% if splits_outgoing_any %}
214 <div class="card-muted">
215 <div class="text-xs dimmed">You owe collaborators</div>
216 <div class="amount-big">{{ splits_outgoing_total }}</div>
217 </div>
218 {% endif %}
219 </div>
220 <p class="text-sm dimmed">
221 Collaborator payouts are recorded automatically when sales complete on projects with collaborators.
222 Payouts to collaborators are currently settled between project members directly.
223 We plan to automate this process in a future update.
224 </p>
225 </details>
226 {% endif %}
227
228 {% if tips_count > 0 %}
229 <details class="form-section" open>
230 <summary><h2 class="subsection-title">Tips Received ({{ tips_count }})</h2></summary>
231 <div class="card-muted mb-pane">
232 <div class="amount-big mb-peer">{{ tips_total }} total</div>
233 <div class="text-sm muted">{{ tips_count }} tips received</div>
234 </div>
235 <table class="data-table well">
236 <thead>
237 <tr>
238 <th>Date</th>
239 <th>From</th>
240 <th>Amount</th>
241 <th>Message</th>
242 </tr>
243 </thead>
244 <tbody>
245 {% for tip in tips_received %}
246 <tr>
247 <td>{{ tip.date }}</td>
248 <td>{{ tip.tipper_name }}</td>
249 <td>{{ tip.amount }}</td>
250 <td class="tip-message">{% if let Some(msg) = tip.message %}{{ msg }}{% else %}<span class="dimmer">-</span>{% endif %}</td>
251 </tr>
252 {% endfor %}
253 </tbody>
254 </table>
255 </details>
256 {% endif %}
257 {% endif %}
258
259 <div class="transactions-header">
260 <h2 class="m-0 transactions-title">Transactions</h2>
261 <button class="btn-secondary text-sm"
262 hx-post="/api/export/sales"
263 hx-swap="none"
264 data-action="exportCsvButton" data-arg="/api/export/sales" data-arg2="sales.csv">Export CSV</button>
265 </div>
266
267 <div class="filter-controls">
268 <div class="filter-group">
269 <select name="type"
270 hx-get="/dashboard/transactions"
271 hx-target="#transactions-tbody"
272 hx-include="[name='period']">
273 <option value="">All Transactions</option>
274 {% if can_create_projects %}
275 <option value="incoming">Sales</option>
276 <option value="outgoing">Payouts</option>
277 <option value="refund">Refunds</option>
278 {% endif %}
279 <option value="purchases">Purchases</option>
280 </select>
281 </div>
282 <div class="filter-group">
283 <select name="period"
284 hx-get="/dashboard/transactions"
285 hx-target="#transactions-tbody"
286 hx-include="[name='type']">
287 <option value="">All Time</option>
288 <option value="month">This Month</option>
289 <option value="3months">Last 3 Months</option>
290 <option value="year">Last Year</option>
291 </select>
292 </div>
293 </div>
294
295 <table class="data-table well">
296 <thead>
297 <tr>
298 <th>Date</th>
299 <th>Type</th>
300 <th>Description</th>
301 <th>Amount</th>
302 <th>Status</th>
303 <th>Details</th>
304 </tr>
305 </thead>
306 <tbody id="transactions-tbody">
307 {% for tx in transactions %}
308 <tr>
309 <td>{{ tx.date }}</td>
310 <td>{{ tx.tx_type }}</td>
311 <td>{{ tx.description }}</td>
312 <td class="{% if tx.is_incoming %}amount-in{% else %}amount-out{% endif %}">{{ tx.amount }}</td>
313 <td><span class="badge" data-tone="{{ tx.status_tone }}">{{ tx.status }}</span></td>
314 <td>{{ tx.details }}</td>
315 </tr>
316 {% endfor %}
317 {% if transactions.is_empty() %}
318 <tr>
319 <td colspan="6" class="text-center dimmed">No transactions found</td>
320 </tr>
321 {% endif %}
322 </tbody>
323 </table>
324
325 {% if can_create_projects %}
326 <div hx-get="/dashboard/tabs/contacts" hx-trigger="revealed" hx-swap="innerHTML" id="contacts-section"></div>
327 {% endif %}
328