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

1636 lines
48 KiB
HTML

<!DOCTYPE html>
<html lang="sv" data-theme="light">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
<meta name="apple-native-compliance" content="full">
<meta name="module" content="dac7-reporting">
<title>DAC7 Rapportering · quiXzoom</title>
<link rel="stylesheet" >
<link rel="stylesheet" >
<script defer></script>
<style>
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
:root {
--ios-blue: #007AFF;
--ios-green: #34C759;
--ios-red: #FF3B30;
--ios-orange: #FF9500;
--ios-yellow: #FFCC00;
--ios-purple: #AF52DE;
--ios-gray: #8E8E93;
--ios-gray2: #AEAEB2;
--ios-gray3: #C7C7CC;
--ios-gray4: #D1D1D6;
--ios-gray5: #E5E5EA;
--ios-gray6: #F2F2F7;
--label-primary: rgba(0,0,0,1);
--label-secondary: rgba(60,60,67,.6);
--label-tertiary: rgba(60,60,67,.3);
--background-primary: #FFFFFF;
--background-secondary: #F2F2F7;
--grouped-bg: #F2F2F7;
--grouped-bg2: #FFFFFF;
--separator: rgba(60,60,67,.29);
--tint: var(--ios-blue);
--glass-bg: rgba(255,255,255,.72);
--glass-blur: blur(40px) saturate(180%);
--safe-top: env(safe-area-inset-top, 0px);
--safe-bottom: env(safe-area-inset-bottom, 0px);
}
body {
font-family: -apple-system, "SF Pro Text", "Helvetica Neue", sans-serif;
background: var(--grouped-bg);
color: var(--label-primary);
min-height: 100vh;
-webkit-font-smoothing: antialiased;
padding-top: calc(var(--safe-top) + 52px);
padding-bottom: calc(var(--safe-bottom) + 20px);
}
/* Navigation Bar */
.navbar {
position: fixed;
top: 0; left: 0; right: 0;
height: calc(var(--safe-top) + 52px);
padding-top: var(--safe-top);
background: var(--glass-bg);
backdrop-filter: var(--glass-blur);
-webkit-backdrop-filter: var(--glass-blur);
border-bottom: 0.5px solid var(--separator);
display: flex;
align-items: flex-end;
padding-bottom: 8px;
padding-left: 16px;
padding-right: 16px;
z-index: 1000;
gap: 12px;
}
.navbar-icon {
width: 30px; height: 30px;
background: linear-gradient(135deg, #007AFF, #5856D6);
border-radius: var(--radius-md);
display: flex; align-items: center; justify-content: center;
font-size: 16px;
flex-shrink: 0;
}
.navbar-title {
font-size: 17px;
font-weight: 600;
letter-spacing: -0.41px;
color: var(--label-primary);
flex: 1;
}
.navbar-badge {
font-size: 11px;
font-weight: 600;
color: var(--ios-blue);
background: rgba(0,122,255,.1);
padding: 3px 8px;
border-radius: var(--radius-xl);
}
/* Content */
.content { padding: 0 16px; }
/* Alert Banner */
.deadline-alert {
margin: 20px 0 0;
background: linear-gradient(135deg, rgba(255,59,48,.08), rgba(255,149,0,.08));
border: 1px solid rgba(255,59,48,.2);
border-radius: var(--radius-lg);
padding: 16px;
display: flex;
align-items: flex-start;
gap: 12px;
}
.alert-icon {
width: 36px; height: 36px;
background: rgba(255,59,48,.12);
border-radius: var(--radius-md);
display: flex; align-items: center; justify-content: center;
font-size: 18px;
flex-shrink: 0;
}
.alert-body { flex: 1; }
.alert-title {
font-size: 15px;
font-weight: 600;
color: var(--ios-red);
letter-spacing: -0.23px;
margin-bottom: 3px;
}
.alert-desc {
font-size: 13px;
color: var(--label-secondary);
line-height: 1.4;
}
.days-left {
font-size: 22px;
font-weight: 700;
color: var(--ios-red);
text-align: center;
}
.days-label {
font-size: 10px;
font-weight: 500;
color: var(--label-tertiary);
text-align: center;
text-transform: uppercase;
letter-spacing: 0.5px;
}
/* Section Header */
.section-header {
font-size: 13px;
font-weight: 600;
color: var(--label-secondary);
text-transform: uppercase;
letter-spacing: 0.5px;
padding: 24px 4px 8px;
}
/* Cards */
.card {
background: var(--grouped-bg2);
border-radius: var(--radius-lg);
overflow: hidden;
margin-bottom: 12px;
}
.card-header {
padding: 14px 16px 0;
}
.card-title {
font-size: 15px;
font-weight: 600;
letter-spacing: -0.23px;
margin-bottom: 2px;
}
.card-subtitle {
font-size: 13px;
color: var(--label-secondary);
line-height: 1.4;
}
/* Info Row */
.info-row {
display: flex;
align-items: center;
padding: 12px 16px;
border-bottom: 0.5px solid var(--separator);
gap: 12px;
}
.info-row:last-child { border-bottom: none; }
.info-icon {
width: 32px; height: 32px;
border-radius: var(--radius-md);
display: flex; align-items: center; justify-content: center;
font-size: 15px;
flex-shrink: 0;
}
.info-content { flex: 1; min-width: 0; }
.info-label { font-size: 15px; letter-spacing: -0.23px; }
.info-value {
font-size: 13px;
color: var(--label-secondary);
margin-top: 1px;
}
.info-right {
font-size: 15px;
font-weight: 600;
color: var(--label-primary);
text-align: right;
}
/* Stats Row */
.stats-grid {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
gap: 1px;
background: var(--separator);
border-radius: 0 0 16px 16px;
overflow: hidden;
}
.stat-cell {
background: var(--grouped-bg2);
padding: 14px 12px;
text-align: center;
}
.stat-value {
font-size: 20px;
font-weight: 700;
letter-spacing: -0.5px;
color: var(--label-primary);
}
.stat-label {
font-size: 11px;
color: var(--label-tertiary);
margin-top: 2px;
text-transform: uppercase;
letter-spacing: 0.3px;
}
/* Zoomer Table */
.zoomer-row {
display: flex;
align-items: center;
padding: 14px 16px;
border-bottom: 0.5px solid var(--separator);
gap: 12px;
cursor: pointer;
transition: background 0.15s;
}
.zoomer-row:last-child { border-bottom: none; }
.zoomer-row:active { background: var(--ios-gray6); }
.zoomer-avatar {
width: 40px; height: 40px;
border-radius: var(--radius-full);
display: flex; align-items: center; justify-content: center;
font-size: 16px;
font-weight: 700;
color: white;
flex-shrink: 0;
}
.zoomer-info { flex: 1; min-width: 0; }
.zoomer-name { font-size: 15px; letter-spacing: -0.23px; font-weight: 500; }
.zoomer-tin {
font-size: 12px;
color: var(--label-tertiary);
font-family: "SF Mono", Menlo, monospace;
margin-top: 2px;
}
.zoomer-right { text-align: right; }
.zoomer-amount {
font-size: 15px;
font-weight: 600;
letter-spacing: -0.23px;
color: var(--label-primary);
}
.status-badge {
display: inline-flex;
align-items: center;
gap: 4px;
font-size: 11px;
font-weight: 600;
padding: 3px 8px;
border-radius: var(--radius-xl);
margin-top: 4px;
}
.status-verified {
background: rgba(52,199,89,.12);
color: var(--ios-green);
}
.status-missing {
background: rgba(255,59,48,.1);
color: var(--ios-red);
}
.status-pending {
background: rgba(255,149,0,.12);
color: var(--ios-orange);
}
/* Info Section */
.info-card {
background: var(--grouped-bg2);
border-radius: var(--radius-lg);
padding: 16px;
margin-bottom: 12px;
}
.info-card-title {
font-size: 15px;
font-weight: 600;
letter-spacing: -0.23px;
margin-bottom: 10px;
display: flex;
align-items: center;
gap: 8px;
}
.info-card-text {
font-size: 14px;
color: var(--label-secondary);
line-height: 1.55;
}
.info-card-text strong {
color: var(--label-primary);
font-weight: 600;
}
/* Export Button */
.export-section {
margin: 8px 0 24px;
}
.btn-primary {
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
width: 100%;
padding: 16px;
background: var(--ios-blue);
color: white;
border: none;
border-radius: var(--radius-lg);
font-size: 17px;
font-weight: 600;
letter-spacing: -0.41px;
cursor: pointer;
transition: opacity 0.15s;
-webkit-tap-highlight-color: transparent;
}
.btn-primary:active { opacity: 0.8; }
.btn-secondary {
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
width: 100%;
padding: 14px;
background: var(--ios-gray6);
color: var(--ios-blue);
border: none;
border-radius: var(--radius-lg);
font-size: 15px;
font-weight: 600;
letter-spacing: -0.23px;
cursor: pointer;
margin-top: 10px;
transition: opacity 0.15s;
}
.btn-secondary:active { opacity: 0.7; }
/* Period Card */
.period-row {
display: flex;
align-items: center;
justify-content: space-between;
padding: 14px 16px;
border-bottom: 0.5px solid var(--separator);
}
.period-row:last-child { border-bottom: none; }
.period-label { font-size: 15px; letter-spacing: -0.23px; }
.period-value { font-size: 15px; font-weight: 500; color: var(--label-secondary); }
/* Divider Text */
.divider-text {
text-align: center;
font-size: 12px;
color: var(--label-tertiary);
padding: 8px 0 16px;
line-height: 1.4;
}
/* Toast */
.toast {
position: fixed;
bottom: calc(var(--safe-bottom) + 24px);
left: 50%; transform: translateX(-50%);
background: rgba(242,242,247,.85);
color: white;
font-size: 14px;
font-weight: 500;
padding: 12px 20px;
border-radius: 100px;
white-space: nowrap;
opacity: 0;
transition: opacity 0.25s;
pointer-events: none;
z-index: 9999;
display: flex;
align-items: center;
gap: 8px;
}
.toast.show { opacity: 1; }
/* Progress Bar */
.progress-track {
height: 4px;
background: var(--ios-gray5);
border-radius: var(--radius-sm);
margin: 12px 16px 14px;
overflow: hidden;
}
.progress-fill {
height: 100%;
border-radius: var(--radius-sm);
background: var(--ios-blue);
transition: width 0.5s ease;
}
@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 type="module" src="https://auth.quixzoom.com/site-auth-snippet.js"></script>
</head>
<body>
<!-- Navigation Bar -->
<div class="navbar">
<div class="navbar-icon"></div>
<div class="navbar-title">DAC7 Rapportering</div>
<div class="navbar-badge">2026</div>
</div>
<div class="content">
<!-- Deadline Alert -->
<div class="deadline-alert">
<div class="alert-icon"></div>
<div class="alert-body">
<div class="alert-title">Deadline: 31 januari 2027</div>
<div class="alert-desc">DAC7-rapporten för kalenderår 2026 ska vara inlämnad till Skatteverket senast 31 jan 2027.</div>
</div>
<div>
<div class="days-left" id="daysLeft"></div>
<div class="days-label">dagar kvar</div>
</div>
</div>
<!-- What is DAC7 -->
<div class="section-header">Om DAC7</div>
<div class="info-card">
<div class="info-card-title"><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> Vad är DAC7?</div>
<div class="info-card-text">
<strong>EU-direktiv 2021/514 (DAC7)</strong> kräver att digitala plattformar rapporterar säljares (zoomers) intäkter till skattemyndigheterna. quiXzoom är en <strong>rapporteringsskyldig plattformsoperatör</strong> och måste varje år lämna uppgifter om zoomers som:
<br><br>
• Genomfört <strong>≥ 30 transaktioner</strong> under kalenderåret, <em>eller</em><br>
• Tjänat in <strong>≥ 2 000 USD</strong> under kalenderåret
<br><br>
Rapporteringen sker i <strong>XML-format</strong> enligt Skatteverkets tekniska specifikation. Första rapportering avser kalenderår 2023.
</div>
</div>
<div class="info-card">
<div class="info-card-title"> Vår skyldighet</div>
<div class="info-card-text">
quiXzoom samlar in <strong>TIN (skatteregistreringsnummer)</strong> och personuppgifter från alla aktiva zoomers. Vi verifierar uppgifterna och rapporterar automatiskt till Skatteverket via deras API-integration. Zoomers informeras om rapporteringen i tjänstevillkoren.
</div>
</div>
<!-- Period -->
<div class="section-header">Rapporteringsperiod</div>
<div class="card">
<div class="period-row">
<span class="period-label">Rapporteringsår</span>
<span class="period-value">Kalenderår 2026</span>
</div>
<div class="period-row">
<span class="period-label">Period</span>
<span class="period-value">2026-01-01 — 2026-12-31</span>
</div>
<div class="period-row">
<span class="period-label">Inlämningsfrist</span>
<span class="period-value" style="color: var(--ios-red); font-weight: 600;">31 januari 2027</span>
</div>
<div class="period-row">
<span class="period-label">Rapporteringsformat</span>
<span class="period-value">XML (DAC7 schema)</span>
</div>
<div class="period-row">
<span class="period-label">Mottagare</span>
<span class="period-value">Skatteverket</span>
</div>
<div class="period-row">
<span class="period-label">Status</span>
<span class="period-value" style="color: var(--ios-orange);">⏳ Pågående insamling</span>
</div>
</div>
<!-- Summary Stats -->
<div class="section-header">Sammanfattning</div>
<div class="card">
<div style="padding: 14px 16px 0; border-bottom: 0.5px solid var(--separator);">
<div style="font-size: 13px; color: var(--label-secondary); margin-bottom: 6px;">Total rapporterad intäkt 2026</div>
<div style="font-size: 28px; font-weight: 700; letter-spacing: -1px; color: var(--label-primary); margin-bottom: 4px;">43 850 SEK</div>
<div style="font-size: 12px; color: var(--label-tertiary); padding-bottom: 12px;">Summa för 5 rapporterade zoomers</div>
<!-- Progress bar: TIN verified -->
<div style="font-size: 12px; color: var(--label-secondary); display: flex; justify-content: space-between; margin-bottom: 4px;">
<span>TIN-verifiering</span>
<span style="font-weight: 600; color: var(--ios-green);">3 av 5 klara</span>
</div>
<div class="progress-track">
<div class="progress-fill" style="width: 60%;"></div>
</div>
</div>
<div class="stats-grid">
<div class="stat-cell">
<div class="stat-value" style="color: var(--ios-green);">3</div>
<div class="stat-label">TIN verifierad</div>
</div>
<div class="stat-cell">
<div class="stat-value" style="color: var(--ios-red);">1</div>
<div class="stat-label">TIN saknas</div>
</div>
<div class="stat-cell">
<div class="stat-value" style="color: var(--ios-orange);">1</div>
<div class="stat-label">Under utredning</div>
</div>
</div>
</div>
<!-- Zoomer List -->
<div class="section-header">Rapporterade zoomers (5)</div>
<div class="card">
<!-- Zoomer 1 - verified -->
<div class="zoomer-row" onclick="showZoomerDetail('Alicia Bergström', '198804127654', '12600', 'verified')">
<div class="zoomer-avatar" style="background: linear-gradient(135deg, #007AFF, #5856D6);">A</div>
<div class="zoomer-info">
<div class="zoomer-name">Alicia Bergström</div>
<div class="zoomer-tin">TIN: 8804127654</div>
<span class="status-badge status-verified"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> TIN verifierad</span>
</div>
<div class="zoomer-right">
<div class="zoomer-amount">12 600 SEK</div>
<div style="font-size: 11px; color: var(--label-tertiary); margin-top: 2px;">47 transaktioner</div>
</div>
</div>
<!-- Zoomer 2 - verified -->
<div class="zoomer-row" onclick="showZoomerDetail('Mikael Thorsson', '199103284512', '9750', 'verified')">
<div class="zoomer-avatar" style="background: linear-gradient(135deg, #34C759, #30B0C7);">M</div>
<div class="zoomer-info">
<div class="zoomer-name">Mikael Thorsson</div>
<div class="zoomer-tin">TIN: 9103284512</div>
<span class="status-badge status-verified"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> TIN verifierad</span>
</div>
<div class="zoomer-right">
<div class="zoomer-amount">9 750 SEK</div>
<div style="font-size: 11px; color: var(--label-tertiary); margin-top: 2px;">38 transaktioner</div>
</div>
</div>
<!-- Zoomer 3 - missing TIN -->
<div class="zoomer-row" onclick="showZoomerDetail('Fatima Okonkwo', 'SAKNAS', '7200', 'missing')">
<div class="zoomer-avatar" style="background: linear-gradient(135deg, #FF3B30, #FF9500);">F</div>
<div class="zoomer-info">
<div class="zoomer-name">Fatima Okonkwo</div>
<div class="zoomer-tin">TIN: EJ REGISTRERAT</div>
<span class="status-badge status-missing"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M4 4L12 12M12 4L4 12" stroke="#ef4444" stroke-width="2" stroke-linecap="round"/></svg> TIN saknas</span>
</div>
<div class="zoomer-right">
<div class="zoomer-amount">7 200 SEK</div>
<div style="font-size: 11px; color: var(--label-tertiary); margin-top: 2px;">31 transaktioner</div>
</div>
</div>
<!-- Zoomer 4 - pending -->
<div class="zoomer-row" onclick="showZoomerDetail('Johan Lindkvist', '197612053311', '11900', 'pending')">
<div class="zoomer-avatar" style="background: linear-gradient(135deg, #FF9500, #FFCC00);">J</div>
<div class="zoomer-info">
<div class="zoomer-name">Johan Lindkvist</div>
<div class="zoomer-tin">TIN: 7612053311</div>
<span class="status-badge status-pending">⏳ Under utredning</span>
</div>
<div class="zoomer-right">
<div class="zoomer-amount">11 900 SEK</div>
<div style="font-size: 11px; color: var(--label-tertiary); margin-top: 2px;">44 transaktioner</div>
</div>
</div>
<!-- Zoomer 5 - verified -->
<div class="zoomer-row" onclick="showZoomerDetail('Sofia Wennberg', '200002157890', '2400', 'verified')">
<div class="zoomer-avatar" style="background: linear-gradient(135deg, #AF52DE, #5856D6);">S</div>
<div class="zoomer-info">
<div class="zoomer-name">Sofia Wennberg</div>
<div class="zoomer-tin">TIN: 0002157890</div>
<span class="status-badge status-verified"><svg width="16" height="16" viewBox="0 0 16 16" fill="none"><path d="M3 8L6.5 11.5L13 4.5" stroke="#22c55e" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg> TIN verifierad</span>
</div>
<div class="zoomer-right">
<div class="zoomer-amount">2 400 SEK</div>
<div style="font-size: 11px; color: var(--label-tertiary); margin-top: 2px;">32 transaktioner</div>
</div>
</div>
</div>
<div class="divider-text">
Alla zoomers med ≥30 transaktioner eller ≥2 000 USD intäkt under kalenderåret inkluderas i rapporten.
</div>
<!-- Export Section -->
<div class="section-header">Exportera rapport</div>
<div class="export-section">
<button class="btn-primary" onclick="exportXML()">
<span></span>
Exportera XML till Skatteverket
</button>
<button class="btn-secondary" onclick="downloadPreview()">
<span></span>
Förhandsgranska XML
</button>
</div>
<!-- Footer note -->
<div class="info-card" style="margin-bottom: 24px;">
<div class="info-card-title"><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> Viktigt att notera</div>
<div class="info-card-text">
Zoomers med <strong>saknat TIN</strong> måste kontaktas och komplettera sina uppgifter <strong>innan export</strong>. Rapporten kan inte inkludera saknade skattenummer.
<br><br>
Har du frågor om DAC7-rapportering? Kontakta <strong>legal@quixzoom.se</strong>
</div>
</div>
</div><!-- /content -->
<!-- Toast -->
<div class="toast" id="toast"></div>
<script>
// Days Until Deadline
(function() {
const deadline = new Date('2027-01-31T23:59:59');
const now = new Date();
const diff = Math.ceil((deadline - now) / (1000 * 60 * 60 * 24));
const el = document.getElementById('daysLeft');
if (el) el.textContent = diff > 0 ? diff : '0';
})();
// Toast Helper
function showToast(msg, duration = 2500) {
const t = document.getElementById('toast');
t.textContent = msg;
t.classList.add('show');
setTimeout(() => t.classList.remove('show'), duration);
}
// Zoomer Detail
function showZoomerDetail(name, tin, amount, status) {
const statusMap = {
verified: '<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> TIN verifierad',
missing: '<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> TIN saknas',
pending: '⏳ Under utredning'
};
showToast(`${name} · ${parseInt(amount).toLocaleString('sv-SE')} SEK · ${statusMap[status]}`);
}
// Export XML
function exportXML() {
const btn = document.querySelector('.btn-primary');
const orig = btn.innerHTML;
btn.innerHTML = '<span>⏳</span> Genererar XML...';
btn.style.opacity = '0.7';
setTimeout(() => {
const xml = generateDAC7XML();
const blob = new Blob([xml], { type: 'application/xml' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `DAC7_quiXzoom_2026_${new Date().toISOString().slice(0,10)}.xml`;
a.click();
URL.revokeObjectURL(url);
btn.innerHTML = orig;
btn.style.opacity = '';
showToast('<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> XML-fil exporterad!', 3000);
}, 1200);
}
// Preview XML
function downloadPreview() {
showToast(' Öppnar förhandsgranskning...');
setTimeout(() => {
const xml = generateDAC7XML();
const win = window.open('', '_blank');
if (win) {
win.document.write('<pre style="font-family:monospace;font-size:12px;padding:20px;white-space:pre-wrap;">' + escapeHtml(xml) + '</pre>');
}
}, 500);
}
function escapeHtml(str) {
return str.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
}
// DAC7 XML Generator
function generateDAC7XML() {
const now = new Date().toISOString();
const zoomers = [
{ name: 'Alicia Bergström', tin: '198804127654', amount: 12600, currency: 'SEK', transactions: 47, status: 'verified' },
{ name: 'Mikael Thorsson', tin: '199103284512', amount: 9750, currency: 'SEK', transactions: 38, status: 'verified' },
{ name: 'Sofia Wennberg', tin: '200002157890', amount: 2400, currency: 'SEK', transactions: 32, status: 'verified' },
// Excluded: Fatima (missing TIN), Johan (pending)
];
const sellerRows = zoomers.map(z => `
<Seller>
<SellerID IDType="TIN" issuedBy="SE">${z.tin}</SellerID>
<Name>${z.name}</Name>
<Address>
<CountryCode>SE</CountryCode>
</Address>
<ResCountryCode>SE</ResCountryCode>
<Consideration currCode="${z.currency}">${z.amount}.00</Consideration>
<Fees currCode="${z.currency}">0.00</Fees>
<Taxes currCode="${z.currency}">0.00</Taxes>
<NumberOfActivities>${z.transactions}</NumberOfActivities>
</Seller>`).join('');
return `<?xml version="1.0" encoding="UTF-8"?>
<!-- DAC7 Rapportering - EU-direktiv 2021/514 -->
<!-- Rapporteringsskyldig plattformsoperatör: quiXzoom AB -->
<!-- Genererad: ${now} -->
<DPI:FATCA_OECD version="2.0"
xmlns:DPI="urn:oecd:ties:dpi:v2"
xmlns:stf="urn:oecd:ties:stf:v4"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<DPI:MessageSpec>
<DPI:SendingEntityIN>5566778899</DPI:SendingEntityIN>
<DPI:TransmittingCountry>SE</DPI:TransmittingCountry>
<DPI:ReceivingCountry>SE</DPI:ReceivingCountry>
<DPI:MessageType>DPI</DPI:MessageType>
<DPI:MessageRefId>quixzoom-dac7-2026-${Date.now()}</DPI:MessageRefId>
<DPI:ReportingPeriod>2026-12-31</DPI:ReportingPeriod>
<DPI:Timestamp>${now}</DPI:Timestamp>
</DPI:MessageSpec>
<DPI:DPIBody>
<DPI:PlatformOperator>
<DPI:ResCountryCode>SE</DPI:ResCountryCode>
<DPI:TIN issuedBy="SE">5566778899</DPI:TIN>
<DPI:Name>quiXzoom AB</DPI:Name>
<DPI:PlatformBusinessName>quiXzoom</DPI:PlatformBusinessName>
<DPI:Address>
<DPI:CountryCode>SE</DPI:CountryCode>
</DPI:Address>
</DPI:PlatformOperator>
<DPI:OtherActivities>${sellerRows}
</DPI:OtherActivities>
</DPI:DPIBody>
</DPI:FATCA_OECD>`;
}
</script>
<!-- quiXzoom Mina Sidor - Universal Header Component -->
<!-- Injicera detta i <head> på alla quiXzoom-sajter -->
<style>
.qz-auth-bar {
position: fixed;
top: 0;
left: 0;
right: 0;
height: 48px;
background: #0A0A0A;
border-bottom: 1px solid rgba(255,255,255,0.1);
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 20px;
z-index: 9999;
font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Display', 'Segoe UI', Roboto, sans-serif;
}
.qz-auth-bar .qz-logo {
font-size: 18px;
font-weight: 700;
color: #fff;
text-decoration: none;
letter-spacing: -0.5px;
}
.qz-auth-bar .qz-logo span {
color: #007AFF;
}
.qz-auth-bar .qz-nav {
display: flex;
gap: 8px;
align-items: center;
}
.qz-auth-bar .qz-btn {
padding: 8px 16px;
border-radius: 8px;
font-size: 14px;
font-weight: 500;
cursor: pointer;
transition: all 0.2s;
border: none;
text-decoration: none;
}
.qz-auth-bar .qz-btn-primary {
background: #007AFF;
color: white;
}
.qz-auth-bar .qz-btn-primary:hover {
background: #0056CC;
}
.qz-auth-bar .qz-btn-ghost {
background: transparent;
color: rgba(255,255,255,0.8);
}
.qz-auth-bar .qz-btn-ghost:hover {
background: rgba(255,255,255,0.1);
color: white;
}
.qz-auth-bar .qz-user {
display: flex;
align-items: center;
gap: 8px;
color: white;
font-size: 14px;
}
.qz-auth-bar .qz-avatar {
width: 32px;
height: 32px;
border-radius: 50%;
background: #007AFF;
display: flex;
align-items: center;
justify-content: center;
font-weight: 600;
font-size: 14px;
}
.qz-auth-bar .qz-dropdown {
position: relative;
}
.qz-auth-bar .qz-dropdown-menu {
position: absolute;
top: 40px;
right: 0;
background: #1C1C1E;
border: 1px solid rgba(255,255,255,0.1);
border-radius: 12px;
padding: 8px;
min-width: 200px;
display: none;
box-shadow: 0 8px 32px rgba(0,0,0,0.4);
}
.qz-auth-bar .qz-dropdown-menu.active {
display: block;
}
.qz-auth-bar .qz-dropdown-item {
padding: 10px 12px;
border-radius: 8px;
color: white;
text-decoration: none;
display: flex;
align-items: center;
gap: 10px;
font-size: 14px;
transition: background 0.2s;
}
.qz-auth-bar .qz-dropdown-item:hover {
background: rgba(255,255,255,0.1);
}
.qz-auth-bar .qz-dropdown-divider {
height: 1px;
background: rgba(255,255,255,0.1);
margin: 8px 0;
}
.qz-auth-bar .qz-balance {
background: rgba(0,122,255,0.15);
color: #007AFF;
padding: 4px 10px;
border-radius: 6px;
font-size: 13px;
font-weight: 600;
}
@media (max-width: 640px) {
.qz-auth-bar .qz-nav .qz-btn-ghost:not(.qz-user) {
display: none;
}
}
/* 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>
<!--
quiXzoom Universal Auth Snippet v2.0
Add this to ALL quiXzoom sites for seamless cross-domain login
Features:
- Silent login (auto-refresh tokens)
- Cross-domain cookie sync
- Real-time auth state updates
- Mobile app compatible
-->
<!-- Auth bar styles -->
<style>
#qz-auth-bar {
position: fixed;
top: 0;
left: 0;
right: 0;
height: 48px;
background: #0A0A0A;
border-bottom: 1px solid rgba(255,255,255,0.1);
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 20px;
z-index: 9999;
font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Display', 'Segoe UI', Roboto, sans-serif;
}
#qz-auth-bar .qz-logo {
font-size: 18px;
font-weight: 700;
color: #fff;
text-decoration: none;
letter-spacing: -0.5px;
}
#qz-auth-bar .qz-logo span { color: #007AFF; }
#qz-auth-bar .qz-nav {
display: flex;
gap: 8px;
align-items: center;
}
#qz-auth-bar .qz-btn {
padding: 8px 16px;
border-radius: 8px;
font-size: 14px;
font-weight: 500;
cursor: pointer;
transition: all 0.2s;
border: none;
text-decoration: none;
}
#qz-auth-bar .qz-btn-primary {
background: #007AFF;
color: white;
}
#qz-auth-bar .qz-btn-primary:hover { background: #0056CC; }
#qz-auth-bar .qz-btn-ghost {
background: transparent;
color: rgba(255,255,255,0.8);
}
#qz-auth-bar .qz-btn-ghost:hover {
background: rgba(255,255,255,0.1);
color: white;
}
#qz-auth-bar .qz-user-menu {
position: relative;
}
#qz-auth-bar .qz-dropdown {
position: absolute;
top: 40px;
right: 0;
background: #1C1C1E;
border: 1px solid rgba(255,255,255,0.1);
border-radius: 12px;
padding: 8px;
min-width: 220px;
display: none;
box-shadow: 0 8px 32px rgba(0,0,0,0.4);
}
#qz-auth-bar .qz-dropdown.active { display: block; }
#qz-auth-bar .qz-dropdown-item {
padding: 10px 12px;
border-radius: 8px;
color: white;
text-decoration: none;
display: flex;
align-items: center;
gap: 10px;
font-size: 14px;
transition: background 0.2s;
}
#qz-auth-bar .qz-dropdown-item:hover { background: rgba(255,255,255,0.1); }
#qz-auth-bar .qz-dropdown-divider {
height: 1px;
background: rgba(255,255,255,0.1);
margin: 8px 0;
}
#qz-auth-bar .qz-avatar {
width: 32px;
height: 32px;
border-radius: 50%;
background: #007AFF;
display: flex;
align-items: center;
justify-content: center;
font-weight: 600;
font-size: 14px;
color: white;
}
#qz-auth-bar .qz-balance {
background: rgba(0,122,255,0.15);
color: #007AFF;
padding: 4px 10px;
border-radius: 6px;
font-size: 13px;
font-weight: 600;
}
/* 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>
<!-- Auth bar HTML -->
<div id="qz-auth-bar">
<a href="https://quixzoom.com" class="qz-logo">qui<span>X</span>zoom</a>
<div class="qz-nav">
<!-- Guest view -->
<div id="qz-guest-view">
<a href="https://app.quixzoom.com/login" class="qz-btn qz-btn-ghost">Logga in</a>
<a href="https://app.quixzoom.com/register" class="qz-btn qz-btn-primary">Bli Zoomer</a>
</div>
<!-- Authenticated view -->
<div id="qz-user-view" style="display:none;">
<span class="qz-balance" id="qz-balance">$0.00</span>
<div class="qz-user-menu">
<button class="qz-btn qz-btn-ghost" onclick="qzToggleMenu()" style="display:flex;align-items:center;gap:8px;">
<div class="qz-avatar" id="qz-avatar">?</div>
<span id="qz-user-name" style="color:white;">User</span>
<svg width="12" height="12" viewBox="0 0 12 12" fill="none">
<path d="M2.5 4.5L6 8L9.5 4.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</button>
<div class="qz-dropdown" id="qz-dropdown">
<a href="https://app.quixzoom.com/dashboard" class="qz-dropdown-item">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<rect x="3" y="3" width="7" height="7" rx="1"/><rect x="14" y="3" width="7" height="7" rx="1"/>
<rect x="14" y="14" width="7" height="7" rx="1"/><rect x="3" y="14" width="7" height="7" rx="1"/>
</svg>
Dashboard
</a>
<a href="https://app.quixzoom.com/wallet" class="qz-dropdown-item">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M21 12V7H5a2 2 0 0 1 0-4h14v4"/><path d="M3 5v14a2 2 0 0 0 2 2h16v-5"/>
<path d="M18 12a2 2 0 0 0 0 4h4v-4h-4z"/>
</svg>
Plånbok
</a>
<a href="https://app.quixzoom.com/missions" class="qz-dropdown-item">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M12 2L2 7l10 5 10-5-10-5z"/><path d="M2 17l10 5 10-5"/><path d="M2 12l10 5 10-5"/>
</svg>
Mina uppdrag
</a>
<div class="qz-dropdown-divider"></div>
<a href="https://app.quixzoom.com/settings" class="qz-dropdown-item">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<circle cx="12" cy="12" r="3"/>
<path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06A1.65 1.65 0 0 0 4.67 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.67 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06A1.65 1.65 0 0 0 9 4.67a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"/>
</svg>
Inställningar
</a>
<a href="#" onclick="qzLogout(); return false;" class="qz-dropdown-item">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M9 21H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h4"/><polyline points="16 17 21 12 16 7"/>
<line x1="21" y1="12" x2="9" y2="12"/>
</svg>
Logga ut
</a>
</div>
</div>
</div>
</div>
</div>
<!-- SSO Client Script -->
<script>
(function() {
const SSO_BASE = 'https://auth.quixzoom.com';
// Check auth status on load
async function qzCheckAuth() {
try {
const res = await fetch(SSO_BASE + '/auth/silent', {
method: 'GET',
credentials: 'include',
});
if (res.ok) {
const data = await res.json();
if (data.authenticated) {
qzShowUser(data.user);
return;
}
}
// Try check endpoint
const checkRes = await fetch(SSO_BASE + '/auth/check', {
credentials: 'include',
});
if (checkRes.ok) {
const checkData = await checkRes.json();
if (checkData.authenticated && checkData.user) {
qzShowUser(checkData.user);
return;
}
}
qzShowGuest();
} catch (e) {
console.log('[QZAuth] Check failed, showing guest');
qzShowGuest();
}
}
function qzShowUser(user) {
document.getElementById('qz-guest-view').style.display = 'none';
document.getElementById('qz-user-view').style.display = 'flex';
document.getElementById('qz-user-name').textContent = user.name || user.email;
document.getElementById('qz-avatar').textContent = (user.name || user.email)[0].toUpperCase();
// Dispatch event for other scripts
window.dispatchEvent(new CustomEvent('qz-auth-change', {
detail: { authenticated: true, user }
}));
}
function qzShowGuest() {
document.getElementById('qz-guest-view').style.display = 'flex';
document.getElementById('qz-user-view').style.display = 'none';
window.dispatchEvent(new CustomEvent('qz-auth-change', {
detail: { authenticated: false }
}));
}
window.qzToggleMenu = function() {
document.getElementById('qz-dropdown').classList.toggle('active');
};
window.qzLogout = async function() {
try {
await fetch(SSO_BASE + '/auth/logout', {
method: 'POST',
credentials: 'include',
});
} catch (e) {}
// Clear all cookies
document.cookie = 'qz_access_token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; domain=.quixzoom.com; path=/;';
document.cookie = 'qz_refresh_token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; domain=.quixzoom.com; path=/;';
document.cookie = 'qz_auth=; expires=Thu, 01 Jan 1970 00:00:00 UTC; domain=.quixzoom.com; path=/;';
qzShowGuest();
window.location.reload();
};
// Close dropdown on outside click
document.addEventListener('click', function(e) {
if (!e.target.closest('.qz-user-menu')) {
document.getElementById('qz-dropdown').classList.remove('active');
}
});
// Check auth on page load
qzCheckAuth();
// Re-check auth periodically (every 5 minutes)
setInterval(qzCheckAuth, 5 * 60 * 1000);
// Listen for storage events (login from other tabs)
window.addEventListener('storage', function(e) {
if (e.key === 'qz_auth_event') {
const event = JSON.parse(e.newValue || '{}');
if (event.type === 'login') {
qzCheckAuth();
} else if (event.type === 'logout') {
qzShowGuest();
}
}
});
})();
</script>
</body>
</html>