Files
boc/quixzoom-landing-fixed/zoomer-profile.html.bak.stripe-removal-20260625-111832
T
Bernt aee0f09db8 landvex: Fixar och tester klara för alla komponenter
- Datafabrik: Dockerfile fix, agentorkestrering fungerar
- Vision: Identify-modell, FAISS, OCR alla testade
- API: Alla 7 integrationstester passerade
- Upplösare: Entitetsupplösning verifierad
2026-07-05 06:41:32 +00:00

922 lines
36 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="sv" data-theme="dark">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<title>Profil — quiXzoom</title>
<link rel="stylesheet" href="/_design-system/apple-native/tokens.css">
<style>
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
body {
font-family: var(--font-text);
background: var(--grouped-background-primary);
color: var(--label-primary);
min-height: 100dvh;
padding-top: env(safe-area-inset-top, 44px);
padding-bottom: calc(env(safe-area-inset-bottom, 34px) + 83px);
-webkit-font-smoothing: antialiased;
}
/* Nav */
.nav-bar {
position: sticky; top: 0; z-index: 200;
display: flex; align-items: center; justify-content: center;
height: 44px; padding: 0 16px;
background: var(--navbar-bg);
backdrop-filter: saturate(180%) blur(20px);
-webkit-backdrop-filter: saturate(180%) blur(20px);
border-bottom: 0.5px solid var(--separator);
}
.nav-title { font-size: 17px; font-weight: 600; letter-spacing: -0.41px; }
/* Profile Hero */
.profile-hero {
display: flex; flex-direction: column; align-items: center;
padding: 32px 20px 24px;
}
.avatar {
width: 88px; height: 88px; border-radius: 50%;
background: linear-gradient(135deg, var(--ios-blue), var(--ios-indigo));
display: flex; align-items: center; justify-content: center;
font-size: 42px; margin-bottom: 14px;
position: relative;
}
.avatar-badge {
position: absolute; bottom: 0; right: 0;
width: 26px; height: 26px; border-radius: 50%;
border: 2.5px solid var(--grouped-background-primary);
display: flex; align-items: center; justify-content: center;
font-size: 14px;
}
.avatar-badge.verified { background: var(--ios-green); }
.avatar-badge.unverified { background: var(--ios-orange); }
.profile-name {
font-family: var(--font-display);
font-size: 24px; font-weight: 700; letter-spacing: -0.5px;
margin-bottom: 4px;
}
.profile-id {
font-size: 13px; color: var(--label-secondary);
font-family: var(--font-mono);
}
.rating-row {
display: flex; align-items: center; gap: 6px;
margin-top: 10px;
}
.stars { color: var(--ios-yellow); font-size: 16px; }
.rating-num { font-size: 15px; font-weight: 600; }
.rating-count { font-size: 13px; color: var(--label-secondary); }
/* Stats */
.stats-grid {
display: grid; grid-template-columns: 1fr 1fr 1fr;
margin: 0 16px 20px;
background: var(--background-secondary);
border-radius: 14px;
border: 0.5px solid var(--separator);
overflow: hidden;
}
.stat-cell {
padding: 16px 8px; text-align: center;
border-right: 0.5px solid var(--separator);
}
.stat-cell:last-child { border-right: none; }
.stat-val { font-size: 22px; font-weight: 700; font-family: var(--font-display); }
.stat-lbl { font-size: 11px; color: var(--label-secondary); margin-top: 2px; }
.green { color: var(--ios-green); }
.blue { color: var(--ios-blue); }
.yellow { color: var(--ios-yellow); }
/* Section */
.section { margin: 0 16px 20px; }
.section-header {
font-size: 13px; font-weight: 500; color: var(--label-secondary);
text-transform: uppercase; letter-spacing: 0.5px;
padding: 0 4px 8px;
}
.section-card {
background: var(--background-secondary);
border-radius: 14px; overflow: hidden;
border: 0.5px solid var(--separator);
}
/* List row */
.list-row {
display: flex; align-items: center; gap: 14px;
padding: 13px 16px;
border-bottom: 0.5px solid var(--separator);
cursor: pointer; transition: opacity 0.12s;
}
.list-row:last-child { border-bottom: none; }
.list-row:active { opacity: 0.7; }
.row-icon-wrap {
width: 32px; height: 32px; border-radius: 8px;
display: flex; align-items: center; justify-content: center;
font-size: 18px; flex-shrink: 0;
}
.row-body { flex: 1; }
.row-title { font-size: 16px; }
.row-sub { font-size: 13px; color: var(--label-secondary); margin-top: 2px; }
.row-right {
display: flex; align-items: center; gap: 6px;
color: var(--label-tertiary); font-size: 15px;
}
.chevron { font-size: 15px; color: var(--label-tertiary); }
/* KYC Card */
.kyc-card {
background: var(--background-secondary);
border-radius: 14px; overflow: hidden;
border: 0.5px solid var(--separator);
}
.kyc-top {
display: flex; gap: 14px; align-items: center;
padding: 16px;
}
.kyc-icon {
width: 52px; height: 52px; border-radius: 12px;
display: flex; align-items: center; justify-content: center;
font-size: 26px; flex-shrink: 0;
}
.kyc-icon.unverified { background: rgba(255,149,0,0.15); }
.kyc-icon.verified { background: rgba(52,199,89,0.15); }
.kyc-icon.pending { background: rgba(0,122,255,0.15); }
.kyc-info { flex: 1; }
.kyc-title { font-size: 17px; font-weight: 600; margin-bottom: 3px; }
.kyc-desc { font-size: 13px; color: var(--label-secondary); }
.kyc-status-bar {
padding: 10px 16px;
font-size: 13px;
border-top: 0.5px solid var(--separator);
display: flex; align-items: center; gap: 8px;
}
.status-chip {
display: inline-flex; align-items: center; gap: 4px;
padding: 4px 10px; border-radius: 20px;
font-size: 12px; font-weight: 600;
}
.chip-unverified { background: rgba(255,149,0,0.15); color: var(--ios-orange); }
.chip-verified { background: rgba(52,199,89,0.15); color: var(--ios-green); }
.chip-pending { background: rgba(0,122,255,0.15); color: var(--ios-blue); }
.kyc-btn {
display: block; width: 100%;
padding: 15px;
border: none; border-radius: 0;
font-family: var(--font-text); font-size: 16px; font-weight: 700;
cursor: pointer; transition: opacity 0.15s;
border-top: 0.5px solid var(--separator);
letter-spacing: -0.2px;
}
.kyc-btn.unverified { background: var(--ios-orange); color: #fff; }
.kyc-btn.verified { background: var(--fill-secondary); color: var(--label-secondary); pointer-events: none; }
.kyc-btn.pending { background: var(--fill-secondary); color: var(--ios-blue); pointer-events: none; }
.kyc-btn:active { opacity: 0.8; }
/* Mission type breakdown */
.type-row {
display: flex; align-items: center; gap: 10px;
padding: 11px 16px;
border-bottom: 0.5px solid var(--separator);
}
.type-row:last-child { border-bottom: none; }
.type-icon { font-size: 16px; width: 24px; text-align: center; }
.type-label { flex: 1; font-size: 14px; }
.type-count { font-size: 14px; font-weight: 600; color: var(--label-secondary); }
.type-bar-wrap { width: 80px; }
.type-bar { height: 4px; border-radius: 2px; background: var(--ios-blue); }
/* Onboarding Progress Bar */
.onboarding-card {
margin: 0 16px 20px;
background: var(--background-secondary);
border-radius: 16px; overflow: hidden;
border: 0.5px solid var(--separator);
}
.onboarding-header {
display: flex; justify-content: space-between; align-items: center;
padding: 14px 16px 10px;
}
.onboarding-title { font-size: 16px; font-weight: 600; }
.onboarding-fraction { font-size: 13px; color: var(--label-secondary); font-weight: 500; }
.onboarding-track {
height: 5px; background: var(--fill-secondary);
margin: 0 16px 14px; border-radius: 3px; overflow: hidden;
}
.onboarding-fill {
height: 100%; border-radius: 3px;
background: linear-gradient(90deg, var(--ios-blue), var(--ios-indigo));
transition: width 0.6s cubic-bezier(0.34,1.56,0.64,1);
}
.onboarding-steps {
display: flex; border-top: 0.5px solid var(--separator);
}
.onboarding-step {
flex: 1; padding: 13px 8px; text-align: center;
border-right: 0.5px solid var(--separator);
}
.onboarding-step:last-child { border-right: none; }
.os-icon { font-size: 22px; margin-bottom: 5px; }
.os-label { font-size: 11px; font-weight: 500; color: var(--label-secondary); }
.os-label.done { color: var(--ios-green); }
.os-label.active { color: var(--ios-blue); }
.onboarding-cta {
display: block; width: calc(100% - 32px);
margin: 0 16px 16px;
padding: 15px;
background: var(--ios-blue); color: #fff;
border: none; border-radius: 12px;
font-family: var(--font-text); font-size: 16px; font-weight: 700;
cursor: pointer; transition: opacity 0.15s;
letter-spacing: -0.2px; text-align: center;
}
.onboarding-cta:active { opacity: 0.8; }
.onboarding-cta:disabled { background: var(--fill-secondary); color: var(--label-secondary); pointer-events: none; }
/* Earnings Calculator */
.calc-card {
background: var(--background-secondary);
border-radius: 16px; overflow: hidden;
border: 0.5px solid var(--separator);
}
.calc-header { padding: 14px 16px 12px; border-bottom: 0.5px solid var(--separator); }
.calc-title { font-size: 16px; font-weight: 600; }
.calc-sub { font-size: 13px; color: var(--label-secondary); margin-top: 2px; }
.calc-body { padding: 16px; }
.calc-slider-row {
display: flex; justify-content: space-between; align-items: center;
margin-bottom: 6px;
}
.calc-label { font-size: 14px; color: var(--label-secondary); }
.calc-value { font-size: 14px; font-weight: 600; }
.calc-slider {
width: 100%; margin-bottom: 16px;
-webkit-appearance: none; appearance: none;
height: 4px; border-radius: 2px;
background: var(--ios-blue); outline: none;
}
.calc-slider::-webkit-slider-thumb {
-webkit-appearance: none; appearance: none;
width: 22px; height: 22px; border-radius: 50%;
background: #fff; box-shadow: 0 2px 8px rgba(0,0,0,0.25);
cursor: pointer;
}
.calc-result {
background: rgba(48,209,88,0.08);
border: 0.5px solid rgba(48,209,88,0.2);
border-radius: 12px; padding: 16px; text-align: center;
}
.calc-result-label { font-size: 13px; color: var(--label-secondary); margin-bottom: 6px; }
.calc-result-amount {
font-family: var(--font-display);
font-size: 36px; font-weight: 800; letter-spacing: -1px;
color: var(--ios-green);
}
.calc-result-unit { font-size: 13px; color: var(--label-secondary); margin-top: 4px; }
.calc-breakdown {
display: flex; justify-content: space-between;
margin-top: 12px; padding-top: 12px;
border-top: 0.5px solid var(--separator);
}
.calc-breakdown-item { text-align: center; }
.calc-breakdown-num { font-size: 16px; font-weight: 700; }
.calc-breakdown-label { font-size: 11px; color: var(--label-secondary); margin-top: 2px; }
/* Referral Program */
.referral-card {
background: linear-gradient(135deg, rgba(10,132,255,0.08), rgba(94,92,230,0.08));
border: 0.5px solid rgba(10,132,255,0.2);
border-radius: 16px; padding: 20px;
}
.referral-header {
display: flex; gap: 12px; align-items: center; margin-bottom: 14px;
}
.referral-icon {
width: 48px; height: 48px; border-radius: 12px;
background: rgba(10,132,255,0.15);
display: flex; align-items: center; justify-content: center;
font-size: 24px;
}
.referral-info {}
.referral-title { font-size: 17px; font-weight: 700; }
.referral-desc { font-size: 13px; color: var(--label-secondary); margin-top: 3px; }
.referral-code-row {
display: flex; gap: 10px; align-items: stretch; margin-bottom: 14px;
}
.referral-code {
flex: 1; background: var(--fill-secondary); border-radius: 10px;
padding: 12px 14px;
font-family: var(--font-mono); font-size: 16px; font-weight: 700;
letter-spacing: 2px; color: var(--ios-blue); text-align: center;
}
.referral-copy-btn {
background: var(--ios-blue); color: #fff;
border: none; border-radius: 10px;
padding: 12px 16px; font-size: 14px; font-weight: 700;
cursor: pointer; transition: opacity 0.15s;
}
.referral-copy-btn:active { opacity: 0.8; }
.referral-share-btn {
display: block; width: 100%;
padding: 13px; background: rgba(10,132,255,0.12); color: var(--ios-blue);
border: 0.5px solid rgba(10,132,255,0.2); border-radius: 10px;
font-size: 15px; font-weight: 600;
cursor: pointer; transition: opacity 0.15s; text-align: center;
margin-bottom: 12px;
}
.referral-share-btn:active { opacity: 0.7; }
.referral-stats {
display: flex; gap: 10px;
}
.referral-stat {
flex: 1; background: var(--fill-secondary); border-radius: 10px;
padding: 10px; text-align: center;
}
.referral-stat-num { font-size: 20px; font-weight: 700; font-family: var(--font-display); }
.referral-stat-label { font-size: 11px; color: var(--label-secondary); margin-top: 2px; }
/* Loader */
@keyframes spin { to { transform: rotate(360deg); } }
.spinner { width:24px; height:24px; border-radius:50%; border:3px solid var(--fill-secondary); border-top-color:var(--ios-blue); animation:spin 0.8s linear infinite; display:inline-block; }
/* Tab Bar */
.tab-bar {
position: fixed; bottom: 0; left: 0; right: 0; z-index: 300;
height: calc(49px + env(safe-area-inset-bottom, 34px));
background: var(--tabbar-bg);
backdrop-filter: saturate(180%) blur(20px);
-webkit-backdrop-filter: saturate(180%) blur(20px);
border-top: 0.5px solid var(--separator);
display: flex; align-items: flex-start; padding-top: 8px;
}
.tab-item { flex:1; display:flex; flex-direction:column; align-items:center; gap:3px; cursor:pointer; text-decoration:none; padding:0 4px; }
.tab-icon { font-size:24px; line-height:1; }
.tab-label { font-size:10px; font-weight:500; color:var(--label-tertiary); }
.tab-item.active .tab-label { color:var(--ios-blue); }
/* Toast */
.toast { position:fixed; bottom:calc(env(safe-area-inset-bottom,34px)+100px); left:50%; transform:translateX(-50%) translateY(20px); background:var(--background-secondary); border:0.5px solid var(--separator); border-radius:20px; padding:10px 18px; font-size:14px; font-weight:500; box-shadow:0 8px 32px rgba(0,0,0,0.3); opacity:0; transition:all 0.3s; z-index:9999; white-space:nowrap; }
.toast.show { opacity:1; transform:translateX(-50%) translateY(0); }
</style>
<style id="wcag-contrast-enforcement">
/* AAMOS GLOBAL CONTRAST ENFORCEMENT — WCAG AA (4.5:1 minimum) */
/* Regel: Ingen text under 70% opacity. Aldrig. */
body { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; }
/* Fix common low-contrast patterns */
[style*="rgba(60,60,67,0.4)"] { color: rgba(60,60,67,0.65) !important; }
[style*="rgba(60,60,67,0.3)"] { color: rgba(60,60,67,0.65) !important; }
[style*="rgba(255,255,255,0.4)"] { color: rgba(255,255,255,0.72) !important; }
[style*="rgba(255,255,255,0.3)"] { color: rgba(255,255,255,0.72) !important; }
[style*="opacity: 0.4"] { opacity: 0.72 !important; }
[style*="opacity: 0.3"] { opacity: 0.72 !important; }
</style>
<script src="/quixzoom/i18n.js"></script>
<meta property="og:type" content="website">
<meta property="og:title" content="Profil — quiXzoom">
<meta property="og:description" content="quiXzoom — Earn per Mission. No Platform Fee.">
<meta property="og:url" content="https://www.quixzoom.com/zoomer-profile.html">
<meta property="og:image" content="https://www.quixzoom.com/images/zoomer-earnings.jpg">
<meta property="og:image:width" content="1200">
<meta property="og:image:height" content="630">
<meta property="og:image:alt" content="quiXzoom Zoomer profile and weekly earnings">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:image" content="https://www.quixzoom.com/images/zoomer-earnings.jpg">
<meta name="twitter:title" content="Profil — quiXzoom">
</head>
<body>
<!-- Nav -->
<div class="nav-bar">
<span class="nav-title">Profil</span>
</div>
<!-- Profile Hero -->
<div class="profile-hero">
<div class="avatar" id="avatar">
🧑
<div class="avatar-badge unverified" id="avatar-badge">⚠️</div>
</div>
<div class="profile-name" id="profile-name">Zoomer</div>
<div class="profile-id" id="profile-id">Laddar...</div>
<div class="rating-row">
<span class="stars">★★★★★</span>
<span class="rating-num" id="rating-num">—</span>
<span class="rating-count" id="rating-count">(0 uppdrag)</span>
</div>
</div>
<!-- Stats -->
<div class="stats-grid">
<div class="stat-cell">
<div class="stat-val green" id="stat-completed">—</div>
<div class="stat-lbl">Slutförda</div>
</div>
<div class="stat-cell">
<div class="stat-val blue" id="stat-pågående">—</div>
<div class="stat-lbl">Pågående</div>
</div>
<div class="stat-cell">
<div class="stat-val yellow" id="stat-earned">—</div>
<div class="stat-lbl">Intjänat kr</div>
</div>
</div>
<!-- Onboarding Progress -->
<div class="onboarding-card" id="onboarding-section">
<div class="onboarding-header">
<span class="onboarding-title">🚀 Kom igång</span>
<span class="onboarding-fraction" id="ob-fraction">0 / 3 klara</span>
</div>
<div class="onboarding-track">
<div class="onboarding-fill" id="ob-fill" style="width:0%"></div>
</div>
<div class="onboarding-steps">
<div class="onboarding-step" id="ob-step-1">
<div class="os-icon">🪪</div>
<div class="os-label" id="ob-lbl-1">KYC</div>
</div>
<div class="onboarding-step" id="ob-step-2">
<div class="os-icon">🏦</div>
<div class="os-label" id="ob-lbl-2">Utbetalning</div>
</div>
<div class="onboarding-step" id="ob-step-3">
<div class="os-icon">🎯</div>
<div class="os-label" id="ob-lbl-3">Första uppdrag</div>
</div>
</div>
</div>
<button class="onboarding-cta" id="ob-cta" onclick="goToFirstMission()" disabled>Ta mitt första uppdrag →</button>
<!-- KYC Section -->
<div class="section">
<div class="section-header">Identitetsverifiering</div>
<div class="kyc-card">
<div class="kyc-top">
<div class="kyc-icon unverified" id="kyc-icon">🪪</div>
<div class="kyc-info">
<div class="kyc-title">Stripe Identity</div>
<div class="kyc-desc">Verifiering krävs för att ta emot utbetalningar över 10 000 kr/månad</div>
</div>
</div>
<div class="kyc-status-bar" id="kyc-status-bar">
<span class="status-chip chip-unverified" id="kyc-chip">⚠️ Ej verifierad</span>
<span style="color:var(--label-secondary)">Verifiera för att låsa upp alla uppdrag</span>
</div>
<button class="kyc-btn unverified" id="kyc-btn" onclick="startKYC()">
🪪 Verifiera identitet →
</button>
</div>
</div>
<!-- Earnings Calculator -->
<div class="section">
<div class="section-header">Inkomstpotential</div>
<div class="calc-card">
<div class="calc-header">
<div class="calc-title">💰 Vad kan du tjäna?</div>
<div class="calc-sub">Estimera din månadsinkomst</div>
</div>
<div class="calc-body">
<div class="calc-slider-row">
<span class="calc-label">Tillgänglighet</span>
<span class="calc-value" id="calc-hours-lbl">3 dagar/vecka</span>
</div>
<input type="range" class="calc-slider" id="calc-hours" min="1" max="7" value="3" oninput="updateCalc()">
<div class="calc-slider-row">
<span class="calc-label">Uppdragstyp</span>
<span class="calc-value" id="calc-type-lbl">Fasadscanning (800 kr)</span>
</div>
<select id="calc-type" onchange="updateCalc()" style="width:100%;background:var(--fill-secondary);border:0.5px solid var(--separator);border-radius:10px;padding:10px 14px;font-size:14px;color:var(--label-primary);font-family:var(--font-text);margin-bottom:16px;-webkit-appearance:none">
<option value="800">🏢 Fasadscanning — 800 kr</option>
<option value="1200">🏠 Takscanning — 1 200 kr</option>
<option value="600">🌳 Vegetationsinventering — 600 kr</option>
<option value="1500">🛣️ Väginspektionen — 1 500 kr</option>
<option value="2000">🏗️ Byggnadsdokumentation — 2 000 kr</option>
</select>
<div class="calc-result">
<div class="calc-result-label">Estimerad månadsinkomst</div>
<div class="calc-result-amount" id="calc-amount">—</div>
<div class="calc-result-unit">SEK / månad (efter avdrag)</div>
<div class="calc-breakdown">
<div class="calc-breakdown-item">
<div class="calc-breakdown-num" id="calc-missions">—</div>
<div class="calc-breakdown-label">Uppdrag/mån</div>
</div>
<div class="calc-breakdown-item">
<div class="calc-breakdown-num" id="calc-gross">—</div>
<div class="calc-breakdown-label">Brutto kr</div>
</div>
<div class="calc-breakdown-item">
<div class="calc-breakdown-num">20%</div>
<div class="calc-breakdown-label">quiXzoom-avg</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Referral Program -->
<div class="section">
<div class="section-header">Referral Program</div>
<div class="referral-card">
<div class="referral-header">
<div class="referral-icon">🤝</div>
<div class="referral-info">
<div class="referral-title">Tjäna 200 kr per ny Zoomer</div>
<div class="referral-desc">Dela din kod — du och din vän får 200 kr när de slutför sitt första uppdrag</div>
</div>
</div>
<div class="referral-code-row">
<div class="referral-code" id="referral-code">ZOOM-——</div>
<button class="referral-copy-btn" onclick="copyReferralCode()">📋 Kopiera</button>
</div>
<button class="referral-share-btn" onclick="shareReferral()">📤 Dela via iOS Share Sheet</button>
<div class="referral-stats">
<div class="referral-stat">
<div class="referral-stat-num" id="ref-count">0</div>
<div class="referral-stat-label">Inbjudna</div>
</div>
<div class="referral-stat">
<div class="referral-stat-num" id="ref-converted">0</div>
<div class="referral-stat-label">Aktiverade</div>
</div>
<div class="referral-stat">
<div class="referral-stat-num" id="ref-earned">0 kr</div>
<div class="referral-stat-label">Intjänat</div>
</div>
</div>
</div>
</div>
<!-- Mission breakdown -->
<div class="section">
<div class="section-header">Uppdragstyper</div>
<div class="section-card" id="type-breakdown">
<div style="padding:20px;text-align:center"><div class="spinner"></div></div>
</div>
</div>
<!-- Settings -->
<div class="section">
<div class="section-header">Inställningar</div>
<div class="section-card">
<div class="list-row" onclick="copyId()">
<div class="row-icon-wrap" style="background:rgba(0,122,255,0.15)">🆔</div>
<div class="row-body">
<div class="row-title">Zoomer-ID</div>
<div class="row-sub" id="full-id" style="font-family:var(--font-mono);font-size:11px">—</div>
</div>
<span class="row-right">Kopiera <span class="chevron"></span></span>
</div>
<div class="list-row" onclick="openEarnings()">
<div class="row-icon-wrap" style="background:rgba(52,199,89,0.15)">💰</div>
<div class="row-body">
<div class="row-title">Intjäning & utbetalningar</div>
<div class="row-sub">Utbetalningskonto, historik</div>
</div>
<span class="chevron"></span>
</div>
<div class="list-row" onclick="openSupport()">
<div class="row-icon-wrap" style="background:rgba(175,82,222,0.15)">💬</div>
<div class="row-body">
<div class="row-title">Support</div>
<div class="row-sub">support@quixzoom.com</div>
</div>
<span class="chevron"></span>
</div>
<div class="list-row" onclick="openTerms()">
<div class="row-icon-wrap" style="background:rgba(142,142,147,0.15)">📄</div>
<div class="row-body">
<div class="row-title">Användarvillkor & Integritet</div>
<div class="row-sub">Juridisk information</div>
</div>
<span class="chevron"></span>
</div>
</div>
</div>
<!-- App info -->
<div style="text-align:center;padding:8px 16px 8px;color:var(--label-quaternary);font-size:12px">
quiXzoom Zoomer App · v1.0.0 · 2026
</div>
<!-- Toast -->
<div class="toast" id="toast"></div>
<!-- Tab Bar -->
<nav class="tab-bar">
<a class="tab-item" href="zoomer-app.html">
<span class="tab-icon">🎯</span>
<span class="tab-label">Uppdrag</span>
</a>
<a class="tab-item" href="zoomer-earnings.html">
<span class="tab-icon">💰</span>
<span class="tab-label">Intjäning</span>
</a>
<a class="tab-item active" href="zoomer-profile.html">
<span class="tab-icon">👤</span>
<span class="tab-label">Profil</span>
</a>
</nav>
<script>
const API = 'https://api.quixzoom.com/api/qz/missions';
const KYC_API = 'https://api.quixzoom.com/api/qz/kyc';
function getZoomerId() {
let id = localStorage.getItem('qz_zoomer_id');
if (!id) { id = 'zoomer-' + Math.random().toString(36).slice(2,10); localStorage.setItem('qz_zoomer_id', id); }
return id;
}
function getKYCStatus() {
return localStorage.getItem('qz_kyc_status') || 'unverified';
}
function setKYCStatus(s) {
localStorage.setItem('qz_kyc_status', s);
}
function toast(msg, dur=3000) {
const el = document.getElementById('toast');
el.textContent = msg;
el.classList.add('show');
setTimeout(() => el.classList.remove('show'), dur);
}
function formatSEK(n) { return new Intl.NumberFormat('sv-SE').format(n); }
function missionTypeLabel(type) {
const m = { facade_scan:'Fasadscanning', roof_scan:'Takscanning', road_inspection:'Väginspekt.', interior_scan:'Interi ör', vegetation_survey:'Vegetation' };
return m[type] || type;
}
function missionTypeEmoji(type) {
const m = { facade_scan:'🏢', roof_scan:'🏠', road_inspection:'🛣️', interior_scan:'🏚️', vegetation_survey:'🌳', inspection:'🔍' };
return m[type] || '📍';
}
async function loadProfile() {
const zoomerId = getZoomerId();
document.getElementById('profile-id').textContent = zoomerId.slice(0,16) + '...';
document.getElementById('full-id').textContent = zoomerId;
document.getElementById('profile-name').textContent = 'Zoomer · ' + zoomerId.slice(-6).toUpperCase();
try {
const r = await fetch(`${API}/missions`);
const data = await r.json();
const all = data.missions || [];
const mine = all.filter(m => m.zoomer_id === zoomerId);
const completed = mine.filter(m => m.status === 'approved');
const ongoing = mine.filter(m => ['assigned','submitted'].includes(m.status));
const totalEarned = completed.reduce((s,m) => s + m.reward_sek, 0);
document.getElementById('stat-completed').textContent = completed.length;
document.getElementById('stat-pågående').textContent = ongoing.length;
document.getElementById('stat-earned').textContent = formatSEK(totalEarned);
// Rating (mock based on completions)
const rating = completed.length === 0 ? null : Math.min(5, 4.0 + completed.length * 0.1).toFixed(1);
document.getElementById('rating-num').textContent = rating || '—';
document.getElementById('rating-count').textContent = `(${completed.length} uppdrag)`;
// Type breakdown
const typeCounts = {};
mine.forEach(m => {
typeCounts[m.mission_type] = (typeCounts[m.mission_type] || 0) + 1;
});
const maxCount = Math.max(...Object.values(typeCounts), 1);
const breakdownEl = document.getElementById('type-breakdown');
if (Object.keys(typeCounts).length === 0) {
breakdownEl.innerHTML = `<div style="padding:24px;text-align:center;color:var(--label-secondary)">Inga uppdrag ännu</div>`;
} else {
breakdownEl.innerHTML = Object.entries(typeCounts)
.sort((a,b) => b[1]-a[1])
.map(([type, count]) => `
<div class="type-row">
<span class="type-icon">${missionTypeEmoji(type)}</span>
<span class="type-label">${missionTypeLabel(type)}</span>
<div class="type-bar-wrap">
<div class="type-bar" style="width:${Math.round(count/maxCount*100)}%"></div>
</div>
<span class="type-count">${count}st</span>
</div>`).join('');
}
} catch(e) {
console.error(e);
}
// KYC status
renderKYCStatus(getKYCStatus());
}
function renderKYCStatus(status) {
const iconEl = document.getElementById('kyc-icon');
const chipEl = document.getElementById('kyc-chip');
const btnEl = document.getElementById('kyc-btn');
const badgeEl = document.getElementById('avatar-badge');
if (status === 'verified') {
iconEl.className = 'kyc-icon verified';
iconEl.textContent = '✅';
chipEl.className = 'status-chip chip-verified';
chipEl.textContent = '✅ Verifierad';
btnEl.className = 'kyc-btn verified';
btnEl.textContent = '✅ Identitet verifierad';
badgeEl.className = 'avatar-badge verified';
badgeEl.textContent = '✅';
document.getElementById('kyc-status-bar').querySelector('span:last-child').textContent = 'Konto verifierat — alla uppdrag tillgängliga';
} else if (status === 'pending') {
iconEl.className = 'kyc-icon pending';
iconEl.textContent = '⏳';
chipEl.className = 'status-chip chip-pending';
chipEl.textContent = '⏳ Under granskning';
btnEl.className = 'kyc-btn pending';
btnEl.textContent = '⏳ Verifiering pågår...';
badgeEl.textContent = '⏳';
} else {
// unverified
iconEl.className = 'kyc-icon unverified';
iconEl.textContent = '🪪';
chipEl.className = 'status-chip chip-unverified';
chipEl.textContent = '⚠️ Ej verifierad';
btnEl.className = 'kyc-btn unverified';
btnEl.textContent = '🪪 Verifiera identitet →';
}
}
async function startKYC() {
const btn = document.getElementById('kyc-btn');
btn.textContent = '⏳ Startar verifiering...';
btn.disabled = true;
try {
const r = await fetch(`${KYC_API}/create-session`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
zoomer_id: getZoomerId(),
return_url: window.location.href
})
});
if (r.ok) {
const data = await r.json();
setKYCStatus('pending');
renderKYCStatus('pending');
toast('🪪 Verifieringssession skapad — slutför på webbplatsen');
if (data.url) { window.open(data.url, '_blank'); }
} else if (r.status === 401) {
toast('🔐 KYC kräver autentisering — kontakta support');
btn.textContent = '🪪 Verifiera identitet →';
btn.disabled = false;
} else {
// Show manual fallback
toast('️ Kontakta support@quixzoom.com för verifiering');
btn.textContent = '🪪 Verifiera identitet →';
btn.disabled = false;
}
} catch(e) {
toast('️ Stripe Identity — kontakta support@quixzoom.com');
btn.textContent = '🪪 Verifiera identitet →';
btn.disabled = false;
}
}
function copyId() {
const id = getZoomerId();
navigator.clipboard?.writeText(id).then(() => toast('📋 ID kopierat!')).catch(() => toast('ID: ' + id));
}
function openEarnings() { window.location.href = 'zoomer-earnings.html'; }
function openSupport() { window.open('https://quixzoom.com/support', '_blank'); }
function openTerms() { window.open('https://quixzoom.com/terms', '_blank'); }
loadProfile();
// === Onboarding Progress Bar ===
function updateOnboarding() {
const kycStatus = getKYCStatus();
const hasBank = localStorage.getItem('qz_bank_linked') === 'true';
const hasFirstMission = parseInt(localStorage.getItem('qz_missions_completed') || '0') > 0;
const steps = [
kycStatus === 'verified',
hasBank,
hasFirstMission
];
const done = steps.filter(Boolean).length;
const pct = Math.round(done / 3 * 100);
document.getElementById('ob-fill').style.width = pct + '%';
document.getElementById('ob-fraction').textContent = done + ' / 3 klara';
const labels = ['ob-lbl-1','ob-lbl-2','ob-lbl-3'];
const texts = ['KYC','Utbetalning','Första uppdrag'];
steps.forEach((s,i) => {
const el = document.getElementById(labels[i]);
el.className = 'os-label ' + (s ? 'done' : (i === done ? 'active' : ''));
if (s) el.textContent = '✅ ' + texts[i];
else if (i === done) el.textContent = ' ' + texts[i];
else el.textContent = texts[i];
});
const cta = document.getElementById('ob-cta');
if (done >= 2) {
// KYC + bank done — can take first mission
cta.disabled = false;
cta.textContent = steps[2] ? '✅ Första uppdrag slutfört!' : '🎯 Ta mitt första uppdrag →';
if (steps[2]) cta.disabled = true;
} else {
cta.disabled = true;
cta.textContent = done === 0 ? '🔒 Slutför KYC för att börja' :
done === 1 ? '🔒 Länka bankkonto för att fortsätta' :
'🎯 Ta mitt första uppdrag →';
}
}
updateOnboarding();
function goToFirstMission() {
window.location.href = 'zoomer-app.html';
}
// === Earnings Calculator ===
function updateCalc() {
const days = parseInt(document.getElementById('calc-hours').value);
const reward = parseInt(document.getElementById('calc-type').value);
const typeEl = document.getElementById('calc-type');
const selectedText = typeEl.options[typeEl.selectedIndex].text;
const rewardMatch = selectedText.match(/(\d[\d\s]+)\s*kr/);
const rewardLabel = rewardMatch ? rewardMatch[0] : reward + ' kr';
// Estimate: ~2 missions per working day
const missionsPerMonth = days * 4 * 2;
const gross = missionsPerMonth * reward;
const net = Math.round(gross * 0.80); // 20% platform fee
const dayLabels = ['','1 dag','2 dagar','3 dagar','4 dagar','5 dagar','6 dagar','7 dagar'];
document.getElementById('calc-hours-lbl').textContent = dayLabels[days] + '/vecka';
document.getElementById('calc-type-lbl').textContent = selectedText.replace(/^[^\s]+ /, '');
document.getElementById('calc-amount').textContent = new Intl.NumberFormat('sv-SE').format(net);
document.getElementById('calc-missions').textContent = missionsPerMonth;
document.getElementById('calc-gross').textContent = new Intl.NumberFormat('sv-SE').format(gross);
}
updateCalc();
// === Referral Program ===
function getReferralCode() {
let code = localStorage.getItem('qz_referral_code');
if (!code) {
const id = getZoomerId().slice(-6).toUpperCase();
code = 'ZOOM-' + id;
localStorage.setItem('qz_referral_code', code);
}
return code;
}
function loadReferralStats() {
const code = getReferralCode();
document.getElementById('referral-code').textContent = code;
// Mock stats from localStorage
const invited = parseInt(localStorage.getItem('qz_ref_invited') || '0');
const converted = parseInt(localStorage.getItem('qz_ref_converted') || '0');
document.getElementById('ref-count').textContent = invited;
document.getElementById('ref-converted').textContent = converted;
document.getElementById('ref-earned').textContent = (converted * 200) + ' kr';
}
loadReferralStats();
function copyReferralCode() {
const code = getReferralCode();
const msg = 'Gå med i quiXzoom med min kod ' + code + ' och tjäna 200 kr på ditt första uppdrag! https://quixzoom.com/join?ref=' + code;
navigator.clipboard?.writeText(msg).then(() => toast('📋 Referral-kod kopierad!')).catch(() => toast('Kod: ' + code));
}
function shareReferral() {
const code = getReferralCode();
const shareData = {
title: 'quiXzoom — Ta drönarppdrag',
text: 'Gå med i quiXzoom med min kod ' + code + ' och vi båda tjänar 200 kr!',
url: 'https://quixzoom.com/join?ref=' + code
};
if (navigator.share) {
navigator.share(shareData).catch(() => copyReferralCode());
} else {
copyReferralCode();
}
}
</script>
</body>
</html>