1a12fb870b
- 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
1157 lines
47 KiB
HTML
1157 lines
47 KiB
HTML
<!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" >
|
||
<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: var(--radius-full);
|
||
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: var(--radius-full);
|
||
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: var(--radius-lg);
|
||
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: var(--radius-lg); 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: var(--radius-md);
|
||
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: var(--radius-lg); 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: var(--radius-md);
|
||
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: var(--radius-xl);
|
||
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: var(--radius-sm); background: var(--ios-blue); }
|
||
|
||
/* Onboarding Progress Bar */
|
||
.onboarding-card {
|
||
margin: 0 16px 20px;
|
||
background: var(--background-secondary);
|
||
border-radius: var(--radius-lg); 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: var(--radius-sm); overflow: hidden;
|
||
}
|
||
.onboarding-fill {
|
||
height: 100%; border-radius: var(--radius-sm);
|
||
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: var(--radius-md);
|
||
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: var(--radius-lg); 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: var(--radius-sm);
|
||
background: var(--ios-blue); outline: none;
|
||
}
|
||
.calc-slider::-webkit-slider-thumb {
|
||
-webkit-appearance: none; appearance: none;
|
||
width: 22px; height: 22px; border-radius: var(--radius-full);
|
||
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: var(--radius-md); 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: var(--radius-lg); padding: 20px;
|
||
}
|
||
.referral-header {
|
||
display: flex; gap: 12px; align-items: center; margin-bottom: 14px;
|
||
}
|
||
.referral-icon {
|
||
width: 48px; height: 48px; border-radius: var(--radius-md);
|
||
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: var(--radius-md);
|
||
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: var(--radius-md);
|
||
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: var(--radius-md);
|
||
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: var(--radius-md);
|
||
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: var(--radius-full); 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: var(--radius-xl); 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); }
|
||
@media (max-width: 640px) {
|
||
.hero h1 { font-size: 2rem !important; }
|
||
.hero p { font-size: 1rem !important; }
|
||
.card { padding: 1.5rem !important; }
|
||
.grid { grid-template-columns: 1fr !important; }
|
||
.btn { padding: 0.75rem 1.5rem !important; font-size: 1rem !important; }
|
||
}
|
||
/* QUIXZOOM STANDARD — Dark Theme */
|
||
:root {
|
||
--qz-bg: #0a0a0a;
|
||
--qz-surface: #111111;
|
||
--qz-surface-2: #1a1a1a;
|
||
--qz-border: rgba(255,255,255,0.08);
|
||
--qz-text: #e8e8e8;
|
||
--qz-text-dim: #a0a0a0;
|
||
--qz-text-muted: #6b6b6b;
|
||
--qz-blue: #007AFF;
|
||
--qz-blue-dark: #0056CC;
|
||
--qz-radius: 10px;
|
||
}
|
||
|
||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||
|
||
body {
|
||
font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif;
|
||
background: var(--qz-bg);
|
||
color: var(--qz-text);
|
||
line-height: 1.6;
|
||
-webkit-font-smoothing: antialiased;
|
||
}
|
||
|
||
/* Navigation */
|
||
.qz-nav {
|
||
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
|
||
display: flex; align-items: center; justify-content: space-between;
|
||
padding: 0 24px; height: 56px;
|
||
background: rgba(255,255,255,0.92);
|
||
backdrop-filter: blur(16px);
|
||
border-bottom: 1px solid var(--qz-border);
|
||
}
|
||
|
||
.qz-nav-logo { font-size: 18px; font-weight: 700; color: #fff; text-decoration: none; }
|
||
.qz-nav-links { display: flex; gap: 24px; list-style: none; }
|
||
.qz-nav-links a { font-size: 14px; color: var(--qz-text-dim); text-decoration: none; transition: color 0.2s; }
|
||
.qz-nav-links a:hover { color: #fff; }
|
||
|
||
/* Buttons */
|
||
.qz-btn {
|
||
display: inline-flex; align-items: center; gap: 6px;
|
||
padding: 12px 24px; border-radius: var(--qz-radius);
|
||
font-size: 15px; font-weight: 600; text-decoration: none;
|
||
transition: all 0.2s; cursor: pointer; border: none;
|
||
}
|
||
|
||
.qz-btn-primary {
|
||
background: var(--qz-blue); color: #fff;
|
||
}
|
||
.qz-btn-primary:hover {
|
||
background: var(--qz-blue-dark); transform: translateY(-1px);
|
||
}
|
||
|
||
.qz-btn-ghost {
|
||
background: transparent; color: var(--qz-text);
|
||
border: 1px solid var(--qz-border);
|
||
}
|
||
.qz-btn-ghost:hover {
|
||
border-color: var(--qz-blue); color: var(--qz-blue);
|
||
}
|
||
|
||
/* Cards */
|
||
.qz-card {
|
||
background: var(--qz-surface);
|
||
border: 1px solid var(--qz-border);
|
||
border-radius: var(--qz-radius);
|
||
padding: 24px;
|
||
transition: transform 0.2s, border-color 0.2s;
|
||
}
|
||
.qz-card:hover {
|
||
transform: translateY(-2px);
|
||
border-color: rgba(0,122,255,0.3);
|
||
}
|
||
|
||
/* Footer */
|
||
.qz-footer {
|
||
border-top: 1px solid var(--qz-border);
|
||
padding: 40px 24px;
|
||
text-align: center;
|
||
color: var(--qz-text-dim);
|
||
font-size: 14px;
|
||
}
|
||
|
||
/* Banner */
|
||
.qz-banner {
|
||
background: #001E50;
|
||
color: rgba(255,255,255,0.55);
|
||
text-align: center;
|
||
padding: 10px;
|
||
font-size: 12px;
|
||
}
|
||
.qz-banner a { color: rgba(255,255,255,0.8); text-decoration: none; }
|
||
|
||
@media (max-width: 768px) {
|
||
.qz-nav-links { display: none; }
|
||
}
|
||
</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; }
|
||
@media (max-width: 640px) {
|
||
.hero h1 { font-size: 2rem !important; }
|
||
.hero p { font-size: 1rem !important; }
|
||
.card { padding: 1.5rem !important; }
|
||
.grid { grid-template-columns: 1fr !important; }
|
||
.btn { padding: 0.75rem 1.5rem !important; font-size: 1rem !important; }
|
||
}
|
||
/* QUIXZOOM STANDARD — Dark Theme */
|
||
:root {
|
||
--qz-bg: #0a0a0a;
|
||
--qz-surface: #111111;
|
||
--qz-surface-2: #1a1a1a;
|
||
--qz-border: rgba(255,255,255,0.08);
|
||
--qz-text: #e8e8e8;
|
||
--qz-text-dim: #a0a0a0;
|
||
--qz-text-muted: #6b6b6b;
|
||
--qz-blue: #007AFF;
|
||
--qz-blue-dark: #0056CC;
|
||
--qz-radius: 10px;
|
||
}
|
||
|
||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||
|
||
body {
|
||
font-family: -apple-system, BlinkMacSystemFont, 'Inter', sans-serif;
|
||
background: var(--qz-bg);
|
||
color: var(--qz-text);
|
||
line-height: 1.6;
|
||
-webkit-font-smoothing: antialiased;
|
||
}
|
||
|
||
/* Navigation */
|
||
.qz-nav {
|
||
position: fixed; top: 0; left: 0; right: 0; z-index: 100;
|
||
display: flex; align-items: center; justify-content: space-between;
|
||
padding: 0 24px; height: 56px;
|
||
background: rgba(255,255,255,0.92);
|
||
backdrop-filter: blur(16px);
|
||
border-bottom: 1px solid var(--qz-border);
|
||
}
|
||
|
||
.qz-nav-logo { font-size: 18px; font-weight: 700; color: #fff; text-decoration: none; }
|
||
.qz-nav-links { display: flex; gap: 24px; list-style: none; }
|
||
.qz-nav-links a { font-size: 14px; color: var(--qz-text-dim); text-decoration: none; transition: color 0.2s; }
|
||
.qz-nav-links a:hover { color: #fff; }
|
||
|
||
/* Buttons */
|
||
.qz-btn {
|
||
display: inline-flex; align-items: center; gap: 6px;
|
||
padding: 12px 24px; border-radius: var(--qz-radius);
|
||
font-size: 15px; font-weight: 600; text-decoration: none;
|
||
transition: all 0.2s; cursor: pointer; border: none;
|
||
}
|
||
|
||
.qz-btn-primary {
|
||
background: var(--qz-blue); color: #fff;
|
||
}
|
||
.qz-btn-primary:hover {
|
||
background: var(--qz-blue-dark); transform: translateY(-1px);
|
||
}
|
||
|
||
.qz-btn-ghost {
|
||
background: transparent; color: var(--qz-text);
|
||
border: 1px solid var(--qz-border);
|
||
}
|
||
.qz-btn-ghost:hover {
|
||
border-color: var(--qz-blue); color: var(--qz-blue);
|
||
}
|
||
|
||
/* Cards */
|
||
.qz-card {
|
||
background: var(--qz-surface);
|
||
border: 1px solid var(--qz-border);
|
||
border-radius: var(--qz-radius);
|
||
padding: 24px;
|
||
transition: transform 0.2s, border-color 0.2s;
|
||
}
|
||
.qz-card:hover {
|
||
transform: translateY(-2px);
|
||
border-color: rgba(0,122,255,0.3);
|
||
}
|
||
|
||
/* Footer */
|
||
.qz-footer {
|
||
border-top: 1px solid var(--qz-border);
|
||
padding: 40px 24px;
|
||
text-align: center;
|
||
color: var(--qz-text-dim);
|
||
font-size: 14px;
|
||
}
|
||
|
||
/* Banner */
|
||
.qz-banner {
|
||
background: #001E50;
|
||
color: rgba(255,255,255,0.55);
|
||
text-align: center;
|
||
padding: 10px;
|
||
font-size: 12px;
|
||
}
|
||
.qz-banner a { color: rgba(255,255,255,0.8); text-decoration: none; }
|
||
|
||
@media (max-width: 768px) {
|
||
.qz-nav-links { display: none; }
|
||
}
|
||
</style>
|
||
<script src="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">
|
||
<script type="module" src="https://auth.quixzoom.com/site-auth-snippet.js"></script>
|
||
<link rel="alternate" hreflang="en" href="https://www.quixzoom.com/" />
|
||
<link rel="alternate" hreflang="sv" href="https://www.quixzoom.com/sv/" />
|
||
<link rel="alternate" hreflang="de" href="https://www.quixzoom.com/de/" />
|
||
<link rel="alternate" hreflang="fr" href="https://www.quixzoom.com/fr/" />
|
||
<link rel="alternate" hreflang="es" href="https://www.quixzoom.com/es/" />
|
||
<link rel="alternate" hreflang="it" href="https://www.quixzoom.com/it/" />
|
||
<link rel="alternate" hreflang="nl" href="https://www.quixzoom.com/nl/" />
|
||
<link rel="alternate" hreflang="x-default" href="https://www.quixzoom.com/" />
|
||
</head>
|
||
<body>
|
||
<h1 style="position:absolute;left:-9999px;">Zoomer Profile - quiXzoom</h1>
|
||
|
||
<!-- 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"><svg width="20" height="20" viewBox="0 0 20 20" fill="none"><path d="M10 3L17 17H3L10 3Z" stroke="#f59e0b" stroke-width="2"/><path d="M10 8V12M10 14V14.5" stroke="#f59e0b" stroke-width="2" stroke-linecap="round"/></svg></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"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M8 2L10 8L14 9L8 14L6 8L2 7L8 2Z" fill="#666"/></svg> 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">Identitetsverifiering</div>
|
||
<div class="kyc-desc">Verifiera din identitet för att ta emot utbetalningar. Krävs enligt lag (AML).</div>
|
||
</div>
|
||
</div>
|
||
<div class="kyc-status-bar" id="kyc-status-bar">
|
||
<span class="status-chip chip-unverified" id="kyc-chip"><svg width="20" height="20" viewBox="0 0 20 20" fill="none"><path d="M10 3L17 17H3L10 3Z" stroke="#f59e0b" stroke-width="2"/><path d="M10 8V12M10 14V14.5" stroke="#f59e0b" stroke-width="2" stroke-linecap="round"/></svg> 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: var(--radius-md);padding:10px 14px;font-size:14px;color:var(--label-primary);font-family:var(--font-text);margin-bottom:16px;-webkit-appearance:none">
|
||
<option value="800"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><rect x="3" y="2" width="10" height="12" rx="1" stroke="#666" stroke-width="1.5"/><path d="M6 5H7M6 8H7M6 11H7M9 5H10M9 8H10M9 11H10" stroke="#666" stroke-width="1.5" stroke-linecap="round"/></svg> 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"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M2 14H14M4 14V8L8 4L12 8V14" stroke="#666" stroke-width="1.5" stroke-linejoin="round"/><path d="M6 14V10H10V14" stroke="#666" stroke-width="1.5"/></svg> 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()"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><rect x="3" y="2" width="10" height="12" rx="1" stroke="#666" stroke-width="1.5"/><path d="M6 5H10M6 8H10M6 11H8" stroke="#666" stroke-width="1.5" stroke-linecap="round"/></svg> 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="qz-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:'<svg width="16" height="16" viewBox="0 0 16 16" fill="none"><rect x="3" y="2" width="10" height="12" rx="1" stroke="#666" stroke-width="1.5"/><path d="M6 5H7M6 8H7M6 11H7M9 5H10M9 8H10M9 11H10" stroke="#666" stroke-width="1.5" stroke-linecap="round"/></svg>', roof_scan:'', road_inspection:'', interior_scan:'', vegetation_survey:'', inspection:'<svg width="16" height="16" viewBox="0 0 16 16" fill="none"><circle cx="7" cy="7" r="5" stroke="#666" stroke-width="2"/><path d="M11 11L14 14" stroke="#666" stroke-width="2" stroke-linecap="round"/></svg>' };
|
||
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 = '<svg width="20" height="20" viewBox="0 0 20 20" fill="none"><circle cx="10" cy="10" r="9" stroke="#22c55e" stroke-width="2"/><path d="M6 10L9 13L14 7" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>';
|
||
chipEl.className = 'status-chip chip-verified';
|
||
chipEl.textContent = '<svg width="20" height="20" viewBox="0 0 20 20" fill="none"><circle cx="10" cy="10" r="9" stroke="#22c55e" stroke-width="2"/><path d="M6 10L9 13L14 7" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Verifierad';
|
||
btnEl.className = 'kyc-btn verified';
|
||
btnEl.textContent = '<svg width="20" height="20" viewBox="0 0 20 20" fill="none"><circle cx="10" cy="10" r="9" stroke="#22c55e" stroke-width="2"/><path d="M6 10L9 13L14 7" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Identitet verifierad';
|
||
badgeEl.className = 'avatar-badge verified';
|
||
badgeEl.textContent = '<svg width="20" height="20" viewBox="0 0 20 20" fill="none"><circle cx="10" cy="10" r="9" stroke="#22c55e" stroke-width="2"/><path d="M6 10L9 13L14 7" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg>';
|
||
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 = '<svg width="20" height="20" viewBox="0 0 20 20" fill="none"><path d="M10 3L17 17H3L10 3Z" stroke="#f59e0b" stroke-width="2"/><path d="M10 8V12M10 14V14.5" stroke="#f59e0b" stroke-width="2" stroke-linecap="round"/></svg> 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 {
|
||
// Skapa unik session
|
||
const sessionId = 'kyc-' + Date.now() + '-' + Math.random().toString(36).slice(2, 8);
|
||
|
||
// Öppna KYC-verifiering i ny flik
|
||
const kycUrl = 'https://app.quixzoom.com/kyc-verify.html?session=' + sessionId;
|
||
|
||
setKYCStatus('pending');
|
||
renderKYCStatus('pending');
|
||
toast(' Öppnar verifiering...');
|
||
window.open(kycUrl, '_blank');
|
||
|
||
// Starta polling för status
|
||
pollKYCStatus(sessionId);
|
||
|
||
} catch(e) {
|
||
console.error('KYC error:', e);
|
||
toast('<svg width="20" height="20" viewBox="0 0 20 20" fill="none"><circle cx="10" cy="10" r="9" stroke="#ef4444" stroke-width="2"/><path d="M7 7L13 13M13 7L7 13" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> Ett fel uppstod: ' + e.message);
|
||
btn.textContent = ' Verifiera identitet →';
|
||
btn.disabled = false;
|
||
}
|
||
}
|
||
|
||
// Polla KYC-status
|
||
async function pollKYCStatus(sessionId) {
|
||
const check = async () => {
|
||
try {
|
||
const res = await fetch(KYC_API + '/status/' + sessionId);
|
||
const data = await res.json();
|
||
|
||
if (data.assurance_level === 'photo_low' || data.status === 'approved') {
|
||
setKYCStatus('verified');
|
||
renderKYCStatus('verified');
|
||
toast('<svg width="20" height="20" viewBox="0 0 20 20" fill="none"><circle cx="10" cy="10" r="9" stroke="#22c55e" stroke-width="2"/><path d="M6 10L9 13L14 7" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> Identitet verifierad!');
|
||
return;
|
||
} else if (data.assurance_level === 'rejected') {
|
||
setKYCStatus('unverified');
|
||
renderKYCStatus('unverified');
|
||
toast('<svg width="20" height="20" viewBox="0 0 20 20" fill="none"><circle cx="10" cy="10" r="9" stroke="#ef4444" stroke-width="2"/><path d="M7 7L13 13M13 7L7 13" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> Verifiering nekad: ' + (data.reject_reason || 'Okänt fel'));
|
||
return;
|
||
}
|
||
|
||
setTimeout(check, 5000);
|
||
} catch(e) {
|
||
console.error('Poll error:', e);
|
||
}
|
||
};
|
||
check();
|
||
}
|
||
|
||
function copyId() {
|
||
const id = getZoomerId();
|
||
navigator.clipboard?.writeText(id).then(() => toast('<svg width="16" height="16" viewBox="0 0 16 16" fill="none"><rect x="3" y="2" width="10" height="12" rx="1" stroke="#666" stroke-width="1.5"/><path d="M6 5H10M6 8H10M6 11H8" stroke="#666" stroke-width="1.5" stroke-linecap="round"/></svg> 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 = '<svg width="20" height="20" viewBox="0 0 20 20" fill="none"><circle cx="10" cy="10" r="9" stroke="#22c55e" stroke-width="2"/><path d="M6 10L9 13L14 7" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> ' + 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] ? '<svg width="20" height="20" viewBox="0 0 20 20" fill="none"><circle cx="10" cy="10" r="9" stroke="#22c55e" stroke-width="2"/><path d="M6 10L9 13L14 7" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> 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('<svg width="16" height="16" viewBox="0 0 16 16" fill="none"><rect x="3" y="2" width="10" height="12" rx="1" stroke="#666" stroke-width="1.5"/><path d="M6 5H10M6 8H10M6 11H8" stroke="#666" stroke-width="1.5" stroke-linecap="round"/></svg> 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>
|