aee0f09db8
- Datafabrik: Dockerfile fix, agentorkestrering fungerar - Vision: Identify-modell, FAISS, OCR alla testade - API: Alla 7 integrationstester passerade - Upplösare: Entitetsupplösning verifierad
922 lines
36 KiB
Plaintext
922 lines
36 KiB
Plaintext
<!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>
|