48ea61cdcc
- Product documentation in docs/products/ - Updated MEMORY.md with product info - quiXzoom Auth Core as AAMOS Identity product
329 lines
15 KiB
HTML
329 lines
15 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="sv">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Mina Sidor — quiXzoom</title>
|
|
<link rel="icon" type="image/svg+xml" href="/favicon.svg">
|
|
<style>
|
|
:root{
|
|
--bg:#F7F7F5;
|
|
--white:#FFFFFF;
|
|
--blue:#0066FF;
|
|
--blue-dark:#0052CC;
|
|
--green:#00C853;
|
|
--text:#1a1a1a;
|
|
--text-body:#3a3a3a;
|
|
--text-muted:#6B6B6B;
|
|
--radius:16px;
|
|
--shadow:0 4px 24px rgba(0,0,0,.08);
|
|
}
|
|
*{box-sizing:border-box;margin:0;padding:0}
|
|
body{font-family:-apple-system,BlinkMacSystemFont,'Inter','Helvetica Neue',sans-serif;background:var(--bg);color:var(--text-body);min-height:100vh}
|
|
|
|
/* Header */
|
|
.header{background:var(--white);border-bottom:1px solid rgba(0,0,0,.06);padding:16px 24px;display:flex;align-items:center;justify-content:space-between;position:sticky;top:0;z-index:100}
|
|
.logo{display:flex;align-items:center;gap:12px;text-decoration:none}
|
|
.logo-icon{width:36px;height:36px;background:var(--blue);border-radius:10px;display:flex;align-items:center;justify-content:center;color:#fff;font-weight:700;font-size:18px}
|
|
.logo-text{font-size:20px;font-weight:700;color:var(--text);letter-spacing:-.5px}
|
|
.logo-text span{color:var(--blue)}
|
|
|
|
.nav{display:flex;gap:8px;align-items:center}
|
|
.nav-link{padding:8px 16px;border-radius:8px;color:var(--text-body);text-decoration:none;font-size:14px;font-weight:500;transition:.2s}
|
|
.nav-link:hover{background:rgba(0,102,255,.06);color:var(--blue)}
|
|
.nav-link.active{background:var(--blue);color:#fff}
|
|
|
|
.user-menu{display:flex;align-items:center;gap:12px}
|
|
.user-avatar{width:36px;height:36px;border-radius:50%;background:var(--blue);color:#fff;display:flex;align-items:center;justify-content:center;font-weight:600;font-size:14px}
|
|
.user-name{font-size:14px;font-weight:600;color:var(--text)}
|
|
.logout-btn{padding:8px 16px;border:1px solid rgba(0,0,0,.1);border-radius:8px;background:transparent;color:var(--text-muted);font-size:13px;cursor:pointer;transition:.2s}
|
|
.logout-btn:hover{border-color:#ff4444;color:#ff4444}
|
|
|
|
/* Main */
|
|
.main{max-width:1200px;margin:0 auto;padding:32px 24px}
|
|
|
|
/* Dashboard Grid */
|
|
.dashboard{display:grid;grid-template-columns:280px 1fr;gap:24px}
|
|
|
|
/* Sidebar */
|
|
.sidebar{background:var(--white);border-radius:var(--radius);padding:20px;box-shadow:var(--shadow)}
|
|
.sidebar-menu{display:flex;flex-direction:column;gap:4px}
|
|
.sidebar-item{padding:12px 16px;border-radius:10px;color:var(--text-body);text-decoration:none;font-size:14px;font-weight:500;display:flex;align-items:center;gap:12px;transition:.2s;cursor:pointer;border:none;background:transparent;width:100%;text-align:left}
|
|
.sidebar-item:hover{background:rgba(0,102,255,.06);color:var(--blue)}
|
|
.sidebar-item.active{background:var(--blue);color:#fff}
|
|
.sidebar-icon{width:20px;height:20px;display:flex;align-items:center;justify-content:center}
|
|
|
|
.sidebar-divider{height:1px;background:rgba(0,0,0,.06);margin:12px 0}
|
|
|
|
/* Content */
|
|
.content{display:flex;flex-direction:column;gap:24px}
|
|
|
|
/* Stats Cards */
|
|
.stats{display:grid;grid-template-columns:repeat(3,1fr);gap:16px}
|
|
.stat-card{background:var(--white);border-radius:var(--radius);padding:24px;box-shadow:var(--shadow)}
|
|
.stat-label{font-size:12px;text-transform:uppercase;letter-spacing:.5px;color:var(--text-muted);margin-bottom:8px}
|
|
.stat-value{font-size:32px;font-weight:700;color:var(--text);letter-spacing:-1px}
|
|
.stat-change{font-size:13px;color:var(--green);margin-top:4px;font-weight:500}
|
|
.stat-change.negative{color:#ff4444}
|
|
|
|
/* Wallet Card */
|
|
.wallet-card{background:linear-gradient(135deg,var(--blue),var(--blue-dark));border-radius:var(--radius);padding:28px;color:#fff;position:relative;overflow:hidden}
|
|
.wallet-card::before{content:'';position:absolute;top:-50%;right:-20%;width:300px;height:300px;background:rgba(255,255,255,.05);border-radius:50%}
|
|
.wallet-label{font-size:12px;text-transform:uppercase;letter-spacing:1px;opacity:.8;margin-bottom:8px}
|
|
.wallet-balance{font-size:42px;font-weight:700;letter-spacing:-1px}
|
|
.wallet-currency{font-size:18px;opacity:.8;margin-left:4px}
|
|
.wallet-actions{display:flex;gap:12px;margin-top:20px}
|
|
.wallet-btn{padding:10px 20px;border-radius:8px;background:rgba(255,255,255,.15);color:#fff;border:none;font-size:14px;font-weight:500;cursor:pointer;transition:.2s}
|
|
.wallet-btn:hover{background:rgba(255,255,255,.25)}
|
|
.wallet-btn.primary{background:#fff;color:var(--blue)}
|
|
|
|
/* Missions */
|
|
.section{background:var(--white);border-radius:var(--radius);padding:24px;box-shadow:var(--shadow)}
|
|
.section-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:20px}
|
|
.section-title{font-size:18px;font-weight:700;color:var(--text)}
|
|
.section-action{color:var(--blue);text-decoration:none;font-size:14px;font-weight:500}
|
|
|
|
.mission-list{display:flex;flex-direction:column;gap:12px}
|
|
.mission-item{display:flex;align-items:center;gap:16px;padding:16px;border-radius:12px;background:var(--bg);transition:.2s}
|
|
.mission-item:hover{box-shadow:0 2px 8px rgba(0,0,0,.06)}
|
|
.mission-icon{width:48px;height:48px;border-radius:12px;background:rgba(0,102,255,.1);display:flex;align-items:center;justify-content:center;color:var(--blue);font-size:20px}
|
|
.mission-info{flex:1}
|
|
.mission-title{font-size:15px;font-weight:600;color:var(--text);margin-bottom:4px}
|
|
.mission-meta{font-size:13px;color:var(--text-muted)}
|
|
.mission-reward{font-size:16px;font-weight:700;color:var(--green)}
|
|
.mission-status{padding:6px 12px;border-radius:6px;font-size:12px;font-weight:600}
|
|
.mission-status.pending{background:rgba(255,193,7,.15);color:#f9a825}
|
|
.mission-status.approved{background:rgba(0,200,83,.15);color:var(--green)}
|
|
.mission-status.rejected{background:rgba(255,68,68,.15);color:#ff4444}
|
|
|
|
/* Login State */
|
|
.login-prompt{text-align:center;padding:80px 24px}
|
|
.login-prompt h1{font-size:32px;font-weight:700;color:var(--text);margin-bottom:16px}
|
|
.login-prompt p{font-size:16px;color:var(--text-muted);margin-bottom:32px;max-width:400px;margin-left:auto;margin-right:auto}
|
|
.login-btn{padding:14px 32px;border-radius:12px;background:var(--blue);color:#fff;border:none;font-size:16px;font-weight:600;cursor:pointer;transition:.2s;text-decoration:none;display:inline-block}
|
|
.login-btn:hover{background:var(--blue-dark);transform:translateY(-1px)}
|
|
|
|
/* Loading */
|
|
.loading{display:flex;align-items:center;justify-content:center;min-height:60vh}
|
|
.loading-spinner{width:40px;height:40px;border:3px solid rgba(0,102,255,.1);border-top-color:var(--blue);border-radius:50%;animation:spin 1s linear infinite}
|
|
@keyframes spin{to{transform:rotate(360deg)}}
|
|
|
|
/* Mobile */
|
|
@media(max-width:768px){
|
|
.dashboard{grid-template-columns:1fr}
|
|
.stats{grid-template-columns:1fr}
|
|
.header{padding:12px 16px}
|
|
.nav{display:none}
|
|
.main{padding:16px}
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<!-- Header -->
|
|
<header class="header">
|
|
<a href="/" class="logo">
|
|
<div class="logo-icon">Q</div>
|
|
<div class="logo-text">qui<span>X</span>zoom</div>
|
|
</a>
|
|
<nav class="nav">
|
|
<a href="/" class="nav-link">Hem</a>
|
|
<a href="/missions" class="nav-link">Uppdrag</a>
|
|
<a href="/mina-sidor" class="nav-link active">Mina Sidor</a>
|
|
<a href="/help" class="nav-link">Hjälp</a>
|
|
</nav>
|
|
<div class="user-menu" id="userMenu" style="display:none">
|
|
<div class="user-avatar" id="userAvatar">Z</div>
|
|
<span class="user-name" id="userName">Zoomer</span>
|
|
<button class="logout-btn" onclick="logout()">Logga ut</button>
|
|
</div>
|
|
</header>
|
|
|
|
<!-- Main Content -->
|
|
<main class="main">
|
|
<!-- Loading State -->
|
|
<div class="loading" id="loadingState">
|
|
<div class="loading-spinner"></div>
|
|
</div>
|
|
|
|
<!-- Login Prompt (shown if not authenticated) -->
|
|
<div class="login-prompt" id="loginPrompt" style="display:none">
|
|
<h1>Välkommen till quiXzoom</h1>
|
|
<p>Logga in för att se dina uppdrag, saldo och inställningar. Samma konto fungerar i appen och på webben.</p>
|
|
<a href="https://auth.quixzoom.com/login?redirect=https://www.quixzoom.com/mina-sidor" class="login-btn">Logga in</a>
|
|
<p style="margin-top:24px;font-size:14px">
|
|
Har du inget konto? <a href="https://auth.quixzoom.com/register" style="color:var(--blue);text-decoration:none;font-weight:600">Registrera dig</a>
|
|
</p>
|
|
</div>
|
|
|
|
<!-- Dashboard (shown if authenticated) -->
|
|
<div class="dashboard" id="dashboard" style="display:none">
|
|
<!-- Sidebar -->
|
|
<aside class="sidebar">
|
|
<nav class="sidebar-menu">
|
|
<button class="sidebar-item active" onclick="showSection('overview')">
|
|
<span class="sidebar-icon">📊</span>
|
|
Översikt
|
|
</button>
|
|
<button class="sidebar-item" onclick="showSection('missions')">
|
|
<span class="sidebar-icon">📍</span>
|
|
Mina Uppdrag
|
|
</button>
|
|
<button class="sidebar-item" onclick="showSection('wallet')">
|
|
<span class="sidebar-icon">💰</span>
|
|
Plånbok
|
|
</button>
|
|
<button class="sidebar-item" onclick="showSection('profile')">
|
|
<span class="sidebar-icon">👤</span>
|
|
Profil
|
|
</button>
|
|
<div class="sidebar-divider"></div>
|
|
<button class="sidebar-item" onclick="showSection('settings')">
|
|
<span class="sidebar-icon">⚙️</span>
|
|
Inställningar
|
|
</button>
|
|
</nav>
|
|
</aside>
|
|
|
|
<!-- Content -->
|
|
<div class="content">
|
|
<!-- Wallet Card -->
|
|
<div class="wallet-card">
|
|
<div class="wallet-label">Tillgängligt Saldo</div>
|
|
<div class="wallet-balance">
|
|
<span id="walletBalance">0.00</span>
|
|
<span class="wallet-currency">USD</span>
|
|
</div>
|
|
<div style="margin-top:4px;opacity:.7;font-size:14px">
|
|
<span id="tokenBalance">0</span> QZ Tokens
|
|
</div>
|
|
<div class="wallet-actions">
|
|
<button class="wallet-btn primary">Begär utbetalning</button>
|
|
<button class="wallet-btn">Transaktionshistorik</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Stats -->
|
|
<div class="stats">
|
|
<div class="stat-card">
|
|
<div class="stat-label">Genomförda Uppdrag</div>
|
|
<div class="stat-value" id="completedMissions">0</div>
|
|
<div class="stat-change">+12 denna vecka</div>
|
|
</div>
|
|
<div class="stat-card">
|
|
<div class="stat-label">Godkännandegrad</div>
|
|
<div class="stat-value" id="approvalRate">0%</div>
|
|
<div class="stat-change">+2.3% denna månad</div>
|
|
</div>
|
|
<div class="stat-card">
|
|
<div class="stat-label">Veckans Intjäning</div>
|
|
<div class="stat-value" id="weeklyEarnings">$0</div>
|
|
<div class="stat-change">+$45 vs förra veckan</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Recent Missions -->
|
|
<div class="section">
|
|
<div class="section-header">
|
|
<h2 class="section-title">Senaste Uppdrag</h2>
|
|
<a href="/missions" class="section-action">Se alla →</a>
|
|
</div>
|
|
<div class="mission-list" id="missionList">
|
|
<!-- Populated by JS -->
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</main>
|
|
|
|
<script type="module">
|
|
import { quixzoomAuth } from './sso-client.js';
|
|
|
|
// DOM elements
|
|
const loadingState = document.getElementById('loadingState');
|
|
const loginPrompt = document.getElementById('loginPrompt');
|
|
const dashboard = document.getElementById('dashboard');
|
|
const userMenu = document.getElementById('userMenu');
|
|
const userAvatar = document.getElementById('userAvatar');
|
|
const userName = document.getElementById('userName');
|
|
|
|
// Initialize
|
|
async function init() {
|
|
const user = await quixzoomAuth.init();
|
|
|
|
loadingState.style.display = 'none';
|
|
|
|
if (user) {
|
|
showDashboard(user);
|
|
} else {
|
|
showLoginPrompt();
|
|
}
|
|
}
|
|
|
|
function showDashboard(user) {
|
|
dashboard.style.display = 'grid';
|
|
userMenu.style.display = 'flex';
|
|
|
|
// Update user info
|
|
userAvatar.textContent = (user.first_name?.[0] || 'Z').toUpperCase();
|
|
userName.textContent = `${user.first_name || ''} ${user.last_name || ''}`.trim() || 'Zoomer';
|
|
|
|
// Load dashboard data
|
|
loadDashboardData();
|
|
}
|
|
|
|
function showLoginPrompt() {
|
|
loginPrompt.style.display = 'block';
|
|
}
|
|
|
|
async function loadDashboardData() {
|
|
// Mock data - replace with API calls
|
|
document.getElementById('walletBalance').textContent = '1,247.50';
|
|
document.getElementById('tokenBalance').textContent = '1,247.5';
|
|
document.getElementById('completedMissions').textContent = '47';
|
|
document.getElementById('approvalRate').textContent = '94%';
|
|
document.getElementById('weeklyEarnings').textContent = '$312';
|
|
|
|
// Mock missions
|
|
const missions = [
|
|
{title: 'Fasadinspektion — Stureplan 4', location: 'Stockholm', reward: 85, status: 'approved', date: 'Idag'},
|
|
{title: 'Vägskade dokumentation', location: 'Södermalm', reward: 45, status: 'pending', date: 'Igår'},
|
|
{title: 'Butiksfasad — Kungsgatan', location: 'Stockholm', reward: 60, status: 'approved', date: '2 dagar sedan'},
|
|
];
|
|
|
|
const missionList = document.getElementById('missionList');
|
|
missionList.innerHTML = missions.map(m => `
|
|
<div class="mission-item">
|
|
<div class="mission-icon">📸</div>
|
|
<div class="mission-info">
|
|
<div class="mission-title">${m.title}</div>
|
|
<div class="mission-meta">${m.location} • ${m.date}</div>
|
|
</div>
|
|
<div class="mission-reward">$${m.reward}</div>
|
|
<div class="mission-status ${m.status}">${m.status === 'approved' ? 'Godkänd' : m.status === 'pending' ? 'Granskas' : 'Avvisad'}</div>
|
|
</div>
|
|
`).join('');
|
|
}
|
|
|
|
// Global functions for onclick handlers
|
|
window.logout = async () => {
|
|
await quixzoomAuth.logout();
|
|
window.location.reload();
|
|
};
|
|
|
|
window.showSection = (section) => {
|
|
// Update active sidebar item
|
|
document.querySelectorAll('.sidebar-item').forEach(item => {
|
|
item.classList.remove('active');
|
|
});
|
|
event.target.closest('.sidebar-item').classList.add('active');
|
|
|
|
// TODO: Show different content sections
|
|
console.log('Show section:', section);
|
|
};
|
|
|
|
// Start
|
|
init();
|
|
</script>
|
|
</body>
|
|
</html>
|