Files
boc/services/frilans-payout/ui/PayoutSettings.html
T
Bernt 1a12fb870b dev-api: add developer portal, OpenAPI spec, and Flatenbadet case study
- New /developers/ page with API docs, SDKs, pricing, use cases
- OpenAPI 3.0 spec for Orders, Missions, Photos, Analytics
- Case study: Glasskiosken i Flatenbadet — complete ROI analysis
- Updated /order/ with recurring missions and frequency dropdown
2026-07-14 15:27:33 +00:00

493 lines
13 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
<title>quiXzoom Frilans Payout</title>
<link rel="stylesheet" href="/_design-system/apple-native/tokens.css">
<link rel="stylesheet" href="/_design-system/apple-native/components.css">
<style>
/* Apple iPhone Native Styles */
:root {
--ios-blue: #007AFF;
--ios-green: #34C759;
--ios-red: #FF3B30;
--ios-gray6: #F2F2F7;
--ios-gray5: #E5E5EA;
--label-primary: #000000;
--label-secondary: rgba(60,60,67,0.6);
--label-tertiary: rgba(60,60,67,0.3);
--system-background: #FFFFFF;
--secondary-system-background: #F2F2F7;
--separator: #C6C6C8;
--ios-radius: 13px;
}
@media (prefers-color-scheme: dark) {
:root {
--label-primary: #FFFFFF;
--label-secondary: rgba(235,235,245,0.6);
--system-background: #000000;
--secondary-system-background: #1C1C1E;
--separator: #38383A;
}
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
-webkit-tap-highlight-color: transparent;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Text', 'Helvetica Neue', system-ui, sans-serif;
background: var(--ios-gray6);
color: var(--label-primary);
line-height: 1.5;
-webkit-font-smoothing: antialiased;
}
.container {
max-width: 430px;
margin: 0 auto;
padding: 16px;
padding-top: max(16px, env(safe-area-inset-top));
padding-bottom: max(16px, env(safe-area-inset-bottom));
}
/* Header */
.header {
background: linear-gradient(135deg, var(--ios-blue), #5856D6);
border-radius: var(--ios-radius);
padding: 24px;
color: white;
margin-bottom: 16px;
}
.header h1 {
font-size: 28px;
font-weight: 700;
margin-bottom: 4px;
}
.header p {
opacity: 0.8;
font-size: 15px;
}
/* Cards */
.card {
background: var(--system-background);
border-radius: var(--ios-radius);
margin-bottom: 12px;
overflow: hidden;
}
.card-header {
padding: 16px;
border-bottom: 0.5px solid var(--separator);
}
.card-header h2 {
font-size: 20px;
font-weight: 600;
}
.card-body {
padding: 16px;
}
/* Market Grid */
.market-grid {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 8px;
}
.market-btn {
aspect-ratio: 1;
border: 2px solid var(--ios-gray5);
border-radius: var(--ios-radius);
background: var(--system-background);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
cursor: pointer;
transition: all 0.2s cubic-bezier(0.32, 0.72, 0, 1);
}
.market-btn:active {
transform: scale(0.95);
}
.market-btn.selected {
border-color: var(--ios-blue);
background: rgba(0,122,255,0.1);
}
.market-btn .flag {
font-size: 32px;
margin-bottom: 4px;
}
.market-btn .code {
font-size: 13px;
font-weight: 600;
color: var(--label-primary);
}
/* Options */
.option {
display: flex;
align-items: center;
padding: 16px;
border-bottom: 0.5px solid var(--separator);
cursor: pointer;
transition: background 0.2s;
}
.option:last-child {
border-bottom: none;
}
.option:active {
background: var(--ios-gray5);
}
.option-icon {
width: 32px;
height: 32px;
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
margin-right: 12px;
font-size: 18px;
}
.option-content {
flex: 1;
}
.option-title {
font-size: 16px;
font-weight: 500;
}
.option-desc {
font-size: 13px;
color: var(--label-secondary);
margin-top: 2px;
}
.option-check {
width: 24px;
height: 24px;
border-radius: 50%;
border: 2px solid var(--separator);
display: flex;
align-items: center;
justify-content: center;
transition: all 0.2s;
}
.option.selected .option-check {
background: var(--ios-blue);
border-color: var(--ios-blue);
}
.option.selected .option-check::after {
content: '✓';
color: white;
font-size: 14px;
}
/* Calculator */
.calc-input {
display: flex;
gap: 8px;
margin-bottom: 16px;
}
.calc-input input {
flex: 1;
padding: 16px;
border: 1px solid var(--separator);
border-radius: 10px;
font-size: 18px;
font-weight: 600;
background: var(--system-background);
color: var(--label-primary);
}
.calc-btn {
padding: 16px 24px;
background: var(--ios-blue);
color: white;
border: none;
border-radius: 10px;
font-size: 16px;
font-weight: 600;
cursor: pointer;
}
/* Breakdown */
.breakdown-row {
display: flex;
justify-content: space-between;
padding: 12px 0;
border-bottom: 0.5px solid var(--separator);
}
.breakdown-row:last-child {
border-bottom: none;
}
.breakdown-row.net {
font-weight: 600;
color: var(--ios-green);
}
.breakdown-row.fee {
color: var(--ios-red);
}
/* Primary Button */
.btn-primary {
width: 100%;
padding: 18px;
background: var(--ios-blue);
color: white;
border: none;
border-radius: 12px;
font-size: 17px;
font-weight: 600;
cursor: pointer;
margin-top: 16px;
transition: opacity 0.2s;
}
.btn-primary:active {
opacity: 0.8;
}
/* Info Box */
.info-box {
background: rgba(0,122,255,0.08);
border-radius: 10px;
padding: 16px;
margin-bottom: 16px;
}
.info-box p {
font-size: 13px;
color: var(--label-secondary);
line-height: 1.4;
}
/* Spring physics for all transitions */
* {
transition-timing-function: cubic-bezier(0.32, 0.72, 0, 1);
}
</style>
</head>
<body>
<div class="container">
<!-- Header -->
<div class="header">
<h1>Frilans Payout</h1>
<p>Get paid without a company</p>
</div>
<!-- Market Selection -->
<div class="card">
<div class="card-header">
<h2>Select Market</h2>
</div>
<div class="card-body">
<div class="market-grid">
<button class="market-btn selected" onclick="selectMarket('SE')">
<span class="flag">🇸🇪</span>
<span class="code">SE</span>
</button>
<button class="market-btn" onclick="selectMarket('NO')">
<span class="flag">🇳🇴</span>
<span class="code">NO</span>
</button>
<button class="market-btn" onclick="selectMarket('DK')">
<span class="flag">🇩🇰</span>
<span class="code">DK</span>
</button>
<button class="market-btn" onclick="selectMarket('FI')">
<span class="flag">🇫🇮</span>
<span class="code">FI</span>
</button>
<button class="market-btn" onclick="selectMarket('DE')">
<span class="flag">🇩🇪</span>
<span class="code">DE</span>
</button>
<button class="market-btn" onclick="selectMarket('NL')">
<span class="flag">🇳🇱</span>
<span class="code">NL</span>
</button>
<button class="market-btn" onclick="selectMarket('US')">
<span class="flag">🇺🇸</span>
<span class="code">US</span>
</button>
<button class="market-btn" onclick="selectMarket('GB')">
<span class="flag">🇬🇧</span>
<span class="code">GB</span>
</button>
</div>
</div>
</div>
<!-- Tax Handling -->
<div class="card">
<div class="card-header">
<h2>Tax Handling</h2>
</div>
<div class="card-body" style="padding: 0;">
<div class="option selected" onclick="selectTax('platform')">
<div class="option-icon" style="background: rgba(0,122,255,0.1);">🏢</div>
<div class="option-content">
<div class="option-title">quiXzoom handles tax</div>
<div class="option-desc">We withhold tax and send KU10</div>
</div>
<div class="option-check"></div>
</div>
<div class="option" onclick="selectTax('self')">
<div class="option-icon" style="background: rgba(255,149,0,0.1);">📄</div>
<div class="option-content">
<div class="option-title">I handle tax myself</div>
<div class="option-desc">You declare and pay tax yourself</div>
</div>
<div class="option-check"></div>
</div>
</div>
</div>
<!-- Payout Method -->
<div class="card">
<div class="card-header">
<h2>Payout Method</h2>
</div>
<div class="card-body" style="padding: 0;">
<div class="option selected" onclick="selectMethod('frilans')">
<div class="option-icon" style="background: rgba(52,199,89,0.1);">💳</div>
<div class="option-content">
<div class="option-title">quiXzoom Frilans</div>
<div class="option-desc">Direct to account • Weekly</div>
</div>
<div class="option-check"></div>
</div>
<div class="option" onclick="selectMethod('stripe')">
<div class="option-icon" style="background: rgba(88,86,214,0.1);"></div>
<div class="option-content">
<div class="option-title">Stripe Connect</div>
<div class="option-desc">Fastest • 1-2 days</div>
</div>
<div class="option-check"></div>
</div>
<div class="option" onclick="selectMethod('bank')">
<div class="option-icon" style="background: rgba(175,82,222,0.1);">🏦</div>
<div class="option-content">
<div class="option-title">Bank Transfer</div>
<div class="option-desc">Traditional • 3-5 days</div>
</div>
<div class="option-check"></div>
</div>
</div>
</div>
<!-- Calculator -->
<div class="card">
<div class="card-header">
<h2>Calculator</h2>
</div>
<div class="card-body">
<div class="calc-input">
<input type="number" id="calcAmount" value="1000" placeholder="Amount">
<button class="calc-btn" onclick="calculate()">Calculate</button>
</div>
<div id="breakdown" style="display: none;">
<div class="breakdown-row">
<span>Gross</span>
<span id="gross">1,000 kr</span>
</div>
<div class="breakdown-row fee">
<span>Platform (15%)</span>
<span>-150 kr</span>
</div>
<div class="breakdown-row fee">
<span>Processing (6%)</span>
<span>-60 kr</span>
</div>
<div class="breakdown-row fee">
<span>Tax (30%)</span>
<span>-237 kr</span>
</div>
<div class="breakdown-row net">
<span>Net to you</span>
<span id="net">553 kr</span>
</div>
</div>
</div>
</div>
<!-- Save Button -->
<button class="btn-primary" onclick="saveSettings()">Save Settings</button>
</div>
<script>
// State
let state = {
market: 'SE',
taxHandling: 'platform',
payoutMethod: 'frilans'
};
function selectMarket(code) {
state.market = code;
document.querySelectorAll('.market-btn').forEach(btn => {
btn.classList.toggle('selected', btn.querySelector('.code').textContent === code);
});
}
function selectTax(handling) {
state.taxHandling = handling;
document.querySelectorAll('.option').forEach((opt, i) => {
if (i < 2) opt.classList.toggle('selected', i === (handling === 'platform' ? 0 : 1));
});
}
function selectMethod(method) {
state.payoutMethod = method;
const methods = ['frilans', 'stripe', 'bank'];
document.querySelectorAll('.option').forEach((opt, i) => {
if (i >= 2) opt.classList.toggle('selected', methods[i-2] === method);
});
}
function calculate() {
const amount = parseInt(document.getElementById('calcAmount').value) || 0;
const platformFee = Math.round(amount * 0.15);
const processingFee = Math.round(amount * 0.06);
const taxAmount = state.taxHandling === 'platform' ? Math.round((amount - platformFee) * 0.30) : 0;
const net = amount - platformFee - processingFee - taxAmount;
document.getElementById('gross').textContent = amount.toLocaleString() + ' kr';
document.getElementById('net').textContent = net.toLocaleString() + ' kr';
document.getElementById('breakdown').style.display = 'block';
}
function saveSettings() {
alert('Settings saved!\n\nMarket: ' + state.market + '\nTax: ' + state.taxHandling + '\nMethod: ' + state.payoutMethod);
}
</script>
</body>
</html>