602 lines
31 KiB
HTML
602 lines
31 KiB
HTML
<!DOCTYPE html>
|
|
|
|
<html lang="en"><head>
|
|
<meta charset="utf-8"/>
|
|
<meta content="width=device-width, initial-scale=1.0" name="viewport"/>
|
|
<title>Merchant Detail | Soundbox Ops</title>
|
|
<script src="https://cdn.tailwindcss.com?plugins=forms,container-queries"></script>
|
|
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600&family=Plus+Jakarta+Sans:wght@600;700;800&family=JetBrains+Mono&display=swap" rel="stylesheet"/>
|
|
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:wght,FILL@100..700,0..1&display=swap" rel="stylesheet"/>
|
|
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:wght,FILL@100..700,0..1&display=swap" rel="stylesheet"/>
|
|
<!-- Tailwind Config -->
|
|
<script id="tailwind-config">
|
|
tailwind.config = {
|
|
darkMode: "class",
|
|
theme: {
|
|
extend: {
|
|
"colors": {
|
|
"surface-container-lowest": "#ffffff",
|
|
"on-tertiary": "#ffffff",
|
|
"secondary-fixed-dim": "#b7c8e1",
|
|
"warning": "#F59E0B",
|
|
"on-primary-fixed-variant": "#003ea8",
|
|
"inverse-surface": "#2e3039",
|
|
"surface": "#faf8ff",
|
|
"surface-container-low": "#f3f3fe",
|
|
"outline": "#737686",
|
|
"on-primary": "#ffffff",
|
|
"tertiary-fixed": "#ffdbcd",
|
|
"primary": "#004ac6",
|
|
"on-error-container": "#93000a",
|
|
"surface-tint": "#0053db",
|
|
"tertiary-container": "#bc4800",
|
|
"surface-variant": "#e1e2ed",
|
|
"on-tertiary-fixed": "#360f00",
|
|
"surface-container-high": "#e7e7f3",
|
|
"info": "#0EA5E9",
|
|
"slate-500": "#64748B",
|
|
"tertiary-fixed-dim": "#ffb596",
|
|
"on-surface": "#191b23",
|
|
"outline-variant": "#c3c6d7",
|
|
"error": "#ba1a1a",
|
|
"inverse-on-surface": "#f0f0fb",
|
|
"on-primary-fixed": "#00174b",
|
|
"surface-bright": "#faf8ff",
|
|
"surface-container": "#ededf9",
|
|
"error-container": "#ffdad6",
|
|
"slate-900": "#0F172A",
|
|
"inverse-primary": "#b4c5ff",
|
|
"on-tertiary-fixed-variant": "#7d2d00",
|
|
"slate-200": "#E2E8F0",
|
|
"on-background": "#191b23",
|
|
"on-error": "#ffffff",
|
|
"on-secondary": "#ffffff",
|
|
"secondary": "#505f76",
|
|
"on-secondary-fixed": "#0b1c30",
|
|
"on-secondary-fixed-variant": "#38485d",
|
|
"danger": "#DC2626",
|
|
"on-primary-container": "#eeefff",
|
|
"success": "#16A34A",
|
|
"on-tertiary-container": "#ffede6",
|
|
"surface-container-highest": "#e1e2ed",
|
|
"primary-fixed": "#dbe1ff",
|
|
"on-surface-variant": "#434655",
|
|
"secondary-container": "#d0e1fb",
|
|
"primary-container": "#2563eb",
|
|
"background": "#F8FAFC",
|
|
"primary-fixed-dim": "#b4c5ff",
|
|
"tertiary": "#943700",
|
|
"secondary-fixed": "#d3e4fe",
|
|
"surface-dim": "#d9d9e5",
|
|
"on-secondary-container": "#54647a",
|
|
"slate-100": "#F1F5F9"
|
|
},
|
|
"borderRadius": {
|
|
"DEFAULT": "0.125rem",
|
|
"lg": "0.25rem",
|
|
"xl": "0.5rem",
|
|
"full": "0.75rem"
|
|
},
|
|
"spacing": {
|
|
"page-padding": "24px",
|
|
"gutter": "24px",
|
|
"topbar-height": "72px",
|
|
"card-padding": "20px",
|
|
"row-height": "52px"
|
|
},
|
|
"fontFamily": {
|
|
"display-lg": ["Plus Jakarta Sans"],
|
|
"label-md": ["Inter"],
|
|
"headline-md": ["Plus Jakarta Sans"],
|
|
"body-md": ["Inter"],
|
|
"headline-lg": ["Plus Jakarta Sans"],
|
|
"body-lg": ["Inter"],
|
|
"metric-lg": ["Inter"],
|
|
"metric-sm": ["Inter"],
|
|
"mono": ["JetBrains Mono"]
|
|
},
|
|
"fontSize": {
|
|
"display-lg": ["36px", {"lineHeight": "44px", "letterSpacing": "-0.02em", "fontWeight": "600"}],
|
|
"label-md": ["12px", {"lineHeight": "16px", "letterSpacing": "0.01em", "fontWeight": "500"}],
|
|
"headline-md": ["20px", {"lineHeight": "28px", "fontWeight": "600"}],
|
|
"body-md": ["14px", {"lineHeight": "20px", "fontWeight": "400"}],
|
|
"headline-lg": ["28px", {"lineHeight": "36px", "fontWeight": "600"}],
|
|
"body-lg": ["16px", {"lineHeight": "24px", "fontWeight": "400"}],
|
|
"metric-lg": ["32px", {"lineHeight": "40px", "fontWeight": "600"}],
|
|
"metric-sm": ["14px", {"lineHeight": "20px", "fontWeight": "600"}]
|
|
}
|
|
},
|
|
},
|
|
}
|
|
</script>
|
|
<style>
|
|
.material-symbols-outlined {
|
|
font-variation-settings: 'FILL' 0, 'wght' 400, 'GRAD' 0, 'opsz' 24;
|
|
vertical-align: middle;
|
|
}
|
|
.custom-scrollbar::-webkit-scrollbar {
|
|
width: 4px;
|
|
}
|
|
.custom-scrollbar::-webkit-scrollbar-track {
|
|
background: transparent;
|
|
}
|
|
.custom-scrollbar::-webkit-scrollbar-thumb {
|
|
background: #E2E8F0;
|
|
border-radius: 10px;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body class="bg-background font-body-md text-on-surface min-h-screen">
|
|
<!-- Side Navigation Shell -->
|
|
<aside class="w-64 h-full fixed left-0 top-0 bg-surface-container-lowest border-r border-slate-200 flex flex-col py-6 px-4 gap-2 z-50">
|
|
<div class="mb-8 px-2">
|
|
<h1 class="font-headline-md text-headline-md font-bold text-primary">Soundbox Ops</h1>
|
|
<p class="font-label-md text-label-md text-slate-500">Admin Console</p>
|
|
</div>
|
|
<nav class="flex-1 space-y-1">
|
|
<a class="flex items-center gap-3 px-3 py-2 text-on-surface-variant hover:bg-slate-100 transition-colors rounded-lg" href="#">
|
|
<span class="material-symbols-outlined">dashboard</span>
|
|
<span class="font-body-md">Overview</span>
|
|
</a>
|
|
<a class="flex items-center gap-3 px-3 py-2 bg-secondary-container text-on-secondary-container font-bold rounded-lg" href="#">
|
|
<span class="material-symbols-outlined">storefront</span>
|
|
<span class="font-body-md">Merchant Management</span>
|
|
</a>
|
|
<a class="flex items-center gap-3 px-3 py-2 text-on-surface-variant hover:bg-slate-100 transition-colors rounded-lg" href="#">
|
|
<span class="material-symbols-outlined">speaker_group</span>
|
|
<span class="font-body-md">Device Registry</span>
|
|
</a>
|
|
<a class="flex items-center gap-3 px-3 py-2 text-on-surface-variant hover:bg-slate-100 transition-colors rounded-lg" href="#">
|
|
<span class="material-symbols-outlined">receipt_long</span>
|
|
<span class="font-body-md">Transactions</span>
|
|
</a>
|
|
<a class="flex items-center gap-3 px-3 py-2 text-on-surface-variant hover:bg-slate-100 transition-colors rounded-lg" href="#">
|
|
<span class="material-symbols-outlined">account_balance</span>
|
|
<span class="font-body-md">Ledger & Settlement</span>
|
|
</a>
|
|
<a class="flex items-center gap-3 px-3 py-2 text-on-surface-variant hover:bg-slate-100 transition-colors rounded-lg" href="#">
|
|
<span class="material-symbols-outlined">history_edu</span>
|
|
<span class="font-body-md">Audit Control</span>
|
|
</a>
|
|
</nav>
|
|
<div class="pt-4 border-t border-slate-100 space-y-1">
|
|
<a class="flex items-center gap-3 px-3 py-2 text-on-surface-variant hover:bg-slate-100 transition-colors rounded-lg" href="#">
|
|
<span class="material-symbols-outlined">settings</span>
|
|
<span class="font-body-md">Settings</span>
|
|
</a>
|
|
<a class="flex items-center gap-3 px-3 py-2 text-on-surface-variant hover:bg-slate-100 transition-colors rounded-lg" href="#">
|
|
<span class="material-symbols-outlined">help</span>
|
|
<span class="font-body-md">Support</span>
|
|
</a>
|
|
</div>
|
|
</aside>
|
|
<!-- Top Navigation Shell -->
|
|
<header class="fixed top-0 right-0 h-[72px] flex justify-between items-center w-[calc(100%-256px)] ml-64 px-page-padding bg-surface-container-lowest border-b border-slate-200 z-40">
|
|
<div class="flex items-center gap-4 flex-1">
|
|
<div class="relative w-full max-w-md">
|
|
<span class="material-symbols-outlined absolute left-3 top-1/2 -translate-y-1/2 text-slate-400">search</span>
|
|
<input class="w-full pl-10 pr-4 py-2 bg-surface-container-low border-none rounded-full text-body-md focus:ring-2 focus:ring-primary/20" placeholder="Search merchants, devices, or IDs..." type="text"/>
|
|
</div>
|
|
</div>
|
|
<div class="flex items-center gap-6">
|
|
<div class="flex items-center gap-4 text-on-surface-variant">
|
|
<button class="hover:text-primary transition-colors"><span class="material-symbols-outlined">notifications</span></button>
|
|
<button class="hover:text-primary transition-colors"><span class="material-symbols-outlined">calendar_today</span></button>
|
|
</div>
|
|
<div class="flex items-center gap-3 pl-6 border-l border-slate-200">
|
|
<div class="text-right">
|
|
<p class="font-label-md text-on-surface font-bold">Admin User</p>
|
|
<p class="text-[10px] text-slate-500 uppercase tracking-wider">Super Administrator</p>
|
|
</div>
|
|
<img alt="Administrator Profile" class="w-10 h-10 rounded-full bg-slate-100" src="https://lh3.googleusercontent.com/aida-public/AB6AXuAuZk5-C2Lq7_jeFw6ridlBQguJ_tmIKvNn7XY2MxpyRmXSuNPt2noIVsdmRItBwIrvUXnUoV1Qf9aJ3Rfe3cTHCc7qSIwqfRL7Ax-d4stkwZ93ihHPTlEd9Ig1FzJdxjGVfS5K5n7JexPl5KxZDBlzFH70iUKrqSuSx4KtxKHi1HkkLaLVhy3-VeXwJh6z_WA5WSV5tqUStG2CyEkHlqHbjiWb4vfZQ2MMlilUHlLjahS4htwQE_aZ6lu7FGhSo8wmWS4-6m6dtsg"/>
|
|
</div>
|
|
</div>
|
|
</header>
|
|
<!-- Main Content Canvas -->
|
|
<main class="ml-64 pt-[72px] min-h-screen p-page-padding">
|
|
<!-- Breadcrumbs & Header -->
|
|
<div class="mb-8">
|
|
<nav class="flex items-center gap-2 text-slate-500 mb-2">
|
|
<a class="font-label-md hover:text-primary" href="#">Merchant Management</a>
|
|
<span class="material-symbols-outlined text-sm">chevron_right</span>
|
|
<span class="font-label-md text-on-surface font-bold" id="merchant-breadcrumb-name">Kopi Kenangan - GI Mall</span>
|
|
</nav>
|
|
<div class="flex justify-between items-start">
|
|
<div>
|
|
<h2 class="font-headline-lg text-headline-lg text-on-surface mb-2" id="merchant-title">Kopi Kenangan - GI Mall</h2>
|
|
<div class="flex items-center gap-3">
|
|
<span id="merchant-status-badge" class="px-3 py-1 bg-success/10 text-success rounded-full text-label-md font-bold flex items-center gap-1">
|
|
<span class="w-1.5 h-1.5 bg-success rounded-full"></span> Active
|
|
</span>
|
|
<span id="merchant-fee-profile" class="px-3 py-1 bg-primary/10 text-primary rounded-full text-label-md font-bold">
|
|
Fee Profile: Flat 0.7%
|
|
</span>
|
|
<span class="text-slate-400 text-body-md" id="merchant-code">ID: MID-98234-KK</span>
|
|
</div>
|
|
</div>
|
|
<div class="flex items-center gap-3">
|
|
<button class="px-4 py-2 bg-surface-container-lowest border border-slate-200 text-on-surface font-bold rounded-lg hover:bg-slate-50 transition-colors flex items-center gap-2">
|
|
<span class="material-symbols-outlined text-[20px]">edit</span> Edit
|
|
</button>
|
|
<button class="px-4 py-2 bg-danger text-white font-bold rounded-lg hover:opacity-90 transition-all flex items-center gap-2">
|
|
<span class="material-symbols-outlined text-[20px]">block</span> Suspend
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!-- Summary Row -->
|
|
<div class="grid grid-cols-1 md:grid-cols-3 gap-gutter mb-8">
|
|
<div class="bg-surface-container-lowest p-card-padding border border-slate-200 rounded-xl">
|
|
<p class="font-label-md text-label-md text-slate-500 mb-1">GMV Today</p>
|
|
<div class="flex items-baseline gap-2">
|
|
<p class="font-metric-lg text-metric-lg text-on-surface" id="merchant-gmv">Rp12.500.000</p>
|
|
<p class="font-metric-sm text-metric-sm text-success" id="merchant-gmv-trend">+12.4%</p>
|
|
</div>
|
|
</div>
|
|
<div class="bg-surface-container-lowest p-card-padding border border-slate-200 rounded-xl">
|
|
<p class="font-label-md text-label-md text-slate-500 mb-1">Active Devices</p>
|
|
<div class="flex items-baseline gap-2">
|
|
<p class="font-metric-lg text-metric-lg text-on-surface" id="merchant-active-devices">4</p>
|
|
<p class="font-metric-sm text-metric-sm text-slate-400"><span id="merchant-registered-devices">5</span> Registered</p>
|
|
</div>
|
|
</div>
|
|
<div class="bg-surface-container-lowest p-card-padding border border-slate-200 rounded-xl">
|
|
<p class="font-label-md text-label-md text-slate-500 mb-1">Success Rate</p>
|
|
<div class="flex items-baseline gap-2">
|
|
<p class="font-metric-lg text-metric-lg text-on-surface" id="merchant-success-rate">100%</p>
|
|
<p class="font-metric-sm text-metric-sm text-success" id="merchant-success-rate-label">Optimal</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!-- Tabs Navigation -->
|
|
<div class="flex items-center border-b border-slate-200 mb-8 overflow-x-auto no-scrollbar">
|
|
<button class="px-6 py-4 text-primary font-bold border-b-2 border-primary whitespace-nowrap">Profile</button>
|
|
<button class="px-6 py-4 text-slate-500 font-medium hover:text-on-surface whitespace-nowrap transition-colors">Outlets</button>
|
|
<button class="px-6 py-4 text-slate-500 font-medium hover:text-on-surface whitespace-nowrap transition-colors">Devices</button>
|
|
<button class="px-6 py-4 text-slate-500 font-medium hover:text-on-surface whitespace-nowrap transition-colors">Transactions</button>
|
|
<button class="px-6 py-4 text-slate-500 font-medium hover:text-on-surface whitespace-nowrap transition-colors">Settlements</button>
|
|
</div>
|
|
<!-- Profile Content Grid (Bento Style) -->
|
|
<div class="grid grid-cols-12 gap-6 mb-8">
|
|
<!-- Business Info Card -->
|
|
<div class="col-span-12 lg:col-span-8 space-y-6">
|
|
<div class="bg-surface-container-lowest rounded-xl border border-slate-200 overflow-hidden">
|
|
<div class="px-6 py-4 border-b border-slate-100 bg-slate-50/50">
|
|
<h3 class="font-headline-md text-[16px] text-on-surface">Business Information</h3>
|
|
</div>
|
|
<div class="p-6 grid grid-cols-2 gap-y-6 gap-x-12">
|
|
<div>
|
|
<p class="font-label-md text-slate-500 mb-1">Legal Entity Name</p>
|
|
<p class="font-body-lg text-on-surface" id="merchant-legal-name">PT Bumi Berkah Boga</p>
|
|
</div>
|
|
<div>
|
|
<p class="font-label-md text-slate-500 mb-1">Tax ID (NPWP)</p>
|
|
<p class="font-body-lg text-on-surface font-mono" id="merchant-tax-id">01.234.567.8-012.000</p>
|
|
</div>
|
|
<div class="col-span-2">
|
|
<p class="font-label-md text-slate-500 mb-1">Registered Business Address</p>
|
|
<p class="font-body-lg text-on-surface" id="merchant-address">Grand Indonesia, East Mall, Level LG. Jl. M.H. Thamrin No.1, Jakarta Pusat, 10310</p>
|
|
</div>
|
|
<div>
|
|
<p class="font-label-md text-slate-500 mb-1">Business Category</p>
|
|
<p class="font-body-lg text-on-surface" id="merchant-category">Food & Beverage (Coffee Shop)</p>
|
|
</div>
|
|
<div>
|
|
<p class="font-label-md text-slate-500 mb-1">Onboarding Date</p>
|
|
<p class="font-body-lg text-on-surface" id="merchant-onboarding-date">Oct 12, 2023</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!-- Settlement Info -->
|
|
<div class="bg-surface-container-lowest rounded-xl border border-slate-200 overflow-hidden">
|
|
<div class="px-6 py-4 border-b border-slate-100 bg-slate-50/50">
|
|
<h3 class="font-headline-md text-[16px] text-on-surface">Settlement Bank Account</h3>
|
|
</div>
|
|
<div class="p-6 flex items-center gap-6">
|
|
<div class="w-16 h-16 bg-slate-100 rounded-lg flex items-center justify-center">
|
|
<span class="material-symbols-outlined text-3xl text-slate-400">account_balance</span>
|
|
</div>
|
|
<div class="flex-1">
|
|
<p class="font-body-lg text-on-surface font-bold" id="merchant-settlement-bank">Bank Central Asia (BCA)</p>
|
|
<p class="font-body-md text-slate-500">Account Number: <span class="text-on-surface font-mono" id="merchant-settlement-account">5020129481</span></p>
|
|
<p class="font-body-md text-slate-500">Beneficiary: <span id="merchant-settlement-holder">PT Bumi Berkah Boga</span></p>
|
|
</div>
|
|
<div class="px-3 py-1 bg-success/10 text-success rounded-lg text-label-md font-bold">
|
|
Verified
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!-- Side Cards: PIC & Location -->
|
|
<div class="col-span-12 lg:col-span-4 space-y-6">
|
|
<!-- PIC Contact Details -->
|
|
<div class="bg-surface-container-lowest rounded-xl border border-slate-200 overflow-hidden">
|
|
<div class="px-6 py-4 border-b border-slate-100 bg-slate-50/50 flex justify-between items-center">
|
|
<h3 class="font-headline-md text-[16px] text-on-surface">PIC Contact</h3>
|
|
<span class="material-symbols-outlined text-slate-400 text-[20px]">contact_page</span>
|
|
</div>
|
|
<div class="p-6 space-y-4">
|
|
<div class="flex items-center gap-4">
|
|
<div class="w-10 h-10 rounded-full bg-primary/10 flex items-center justify-center text-primary font-bold">JD</div>
|
|
<div>
|
|
<p class="font-body-md font-bold text-on-surface" id="merchant-pic-name">John Doe</p>
|
|
<p class="text-label-md text-slate-500" id="merchant-pic-role">Operations Manager</p>
|
|
</div>
|
|
</div>
|
|
<div class="space-y-3 pt-2">
|
|
<div class="flex items-center gap-3 text-body-md">
|
|
<span class="material-symbols-outlined text-slate-400 text-[18px]">mail</span>
|
|
<span class="text-on-surface" id="merchant-pic-email">j.doe@kopikenangan.id</span>
|
|
</div>
|
|
<div class="flex items-center gap-3 text-body-md">
|
|
<span class="material-symbols-outlined text-slate-400 text-[18px]">phone</span>
|
|
<span class="text-on-surface" id="merchant-pic-phone">+62 812-3456-7890</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!-- Location Map Placeholder -->
|
|
<div class="bg-surface-container-lowest rounded-xl border border-slate-200 overflow-hidden">
|
|
<div class="h-48 bg-slate-200 relative">
|
|
<img class="w-full h-full object-cover" data-alt="A stylized satellite view map showing a commercial district in Jakarta with a central pin pointing at a large shopping mall. The map colors are muted in shades of light gray and slate blue to match the corporate dashboard aesthetic. Sharp grid lines and minimal labels highlight the precise geolocation of the merchant." data-location="Jakarta" src="https://lh3.googleusercontent.com/aida-public/AB6AXuDlt6GRr2iaPzTt0VsXntnT_1W2IOM8IHEZqrYEDlV4GK-AkuB97tjXHR0isXd1l9OXIaGTH_NvCxO14JYmB7WrO-ib0Cv4nAm7JhfvQFVqsrzqKHoof1c9Evj_XmyQ6pN3JMVEWCQtHWz2uuTscr3okdX99WpAgfHc-agtwflekGD4CxCKV17fjkdqn8JDr0FspXOsFSTblVfZviPFIjDf7SLdXjzpmKt-aMbgAwnK3lmpH6UsiodcYFG3o4Xemln28bhUDBvs8G8"/>
|
|
<div class="absolute inset-0 bg-gradient-to-t from-black/20 to-transparent"></div>
|
|
</div>
|
|
<div class="p-4 flex items-center justify-between">
|
|
<p class="text-label-md text-slate-500" id="merchant-gps">GPS: -6.1951, 106.8231</p>
|
|
<button class="text-primary font-bold text-label-md flex items-center gap-1">
|
|
Open in Maps <span class="material-symbols-outlined text-sm">open_in_new</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<!-- Audit History Block -->
|
|
<div class="mt-12 bg-slate-900 rounded-xl overflow-hidden shadow-xl">
|
|
<div class="px-6 py-4 border-b border-slate-800 flex justify-between items-center">
|
|
<div class="flex items-center gap-2">
|
|
<span class="material-symbols-outlined text-slate-400">terminal</span>
|
|
<h3 class="text-white font-headline-md text-[14px] uppercase tracking-widest">Audit Logs & Raw Payloads</h3>
|
|
</div>
|
|
<button class="text-slate-400 hover:text-white transition-colors flex items-center gap-1 text-label-md">
|
|
<span class="material-symbols-outlined text-[18px]">content_copy</span> Copy Log
|
|
</button>
|
|
</div>
|
|
<div class="p-6 font-mono text-[13px] leading-relaxed text-slate-300 max-h-64 overflow-y-auto custom-scrollbar" id="merchant-audit-logs"></div>
|
|
</div>
|
|
</main>
|
|
<!-- Interactive Layer: Notification Toast (Hidden by default) -->
|
|
<div class="fixed bottom-8 right-8 bg-surface-container-highest border border-slate-200 shadow-2xl rounded-xl p-4 translate-y-24 opacity-0 transition-all duration-300 z-[100] flex items-center gap-4" id="toast">
|
|
<div class="w-10 h-10 bg-success rounded-full flex items-center justify-center text-white">
|
|
<span class="material-symbols-outlined">check_circle</span>
|
|
</div>
|
|
<div>
|
|
<p class="font-bold text-on-surface">Update Successful</p>
|
|
<p class="text-body-md text-slate-500">Merchant details have been synced.</p>
|
|
</div>
|
|
</div>
|
|
<script src="/ui/shared/admin-api.js"></script>
|
|
<script>
|
|
const MerchantDetail = (() => {
|
|
const api = window.AdminUIAPI;
|
|
if (!api) {
|
|
return;
|
|
}
|
|
|
|
const qs = new URLSearchParams(window.location.search);
|
|
const merchantId = qs.get("merchant_id") || "";
|
|
const toast = document.getElementById("toast");
|
|
|
|
const els = {
|
|
breadcrumbName: document.getElementById("merchant-breadcrumb-name"),
|
|
title: document.getElementById("merchant-title"),
|
|
statusBadge: document.getElementById("merchant-status-badge"),
|
|
feeProfile: document.getElementById("merchant-fee-profile"),
|
|
code: document.getElementById("merchant-code"),
|
|
gmv: document.getElementById("merchant-gmv"),
|
|
gmvTrend: document.getElementById("merchant-gmv-trend"),
|
|
activeDevices: document.getElementById("merchant-active-devices"),
|
|
registeredDevices: document.getElementById("merchant-registered-devices"),
|
|
successRate: document.getElementById("merchant-success-rate"),
|
|
successRateLabel: document.getElementById("merchant-success-rate-label"),
|
|
legalName: document.getElementById("merchant-legal-name"),
|
|
taxId: document.getElementById("merchant-tax-id"),
|
|
address: document.getElementById("merchant-address"),
|
|
category: document.getElementById("merchant-category"),
|
|
onboardingDate: document.getElementById("merchant-onboarding-date"),
|
|
settlementBank: document.getElementById("merchant-settlement-bank"),
|
|
settlementAccount: document.getElementById("merchant-settlement-account"),
|
|
settlementHolder: document.getElementById("merchant-settlement-holder"),
|
|
picName: document.getElementById("merchant-pic-name"),
|
|
picRole: document.getElementById("merchant-pic-role"),
|
|
picEmail: document.getElementById("merchant-pic-email"),
|
|
picPhone: document.getElementById("merchant-pic-phone"),
|
|
gps: document.getElementById("merchant-gps"),
|
|
auditLogs: document.getElementById("merchant-audit-logs")
|
|
};
|
|
|
|
const setText = (el, value, fallback = "-") => {
|
|
if (el) {
|
|
el.textContent = value || fallback;
|
|
}
|
|
};
|
|
|
|
const showToast = (text) => {
|
|
if (!toast) {
|
|
return;
|
|
}
|
|
toast.querySelector("p.font-bold") && (toast.querySelector("p.font-bold").textContent = text);
|
|
toast.classList.remove("translate-y-24", "opacity-0");
|
|
setTimeout(() => toast.classList.add("translate-y-24", "opacity-0"), 1800);
|
|
};
|
|
|
|
const formatDate = (value, fallback = "-") => {
|
|
if (!value) {
|
|
return fallback;
|
|
}
|
|
const d = new Date(value);
|
|
if (Number.isNaN(d.getTime())) {
|
|
return value;
|
|
}
|
|
return d.toLocaleDateString("en-GB", {
|
|
year: "numeric",
|
|
month: "short",
|
|
day: "numeric"
|
|
});
|
|
};
|
|
|
|
const setStatusBadge = (status) => {
|
|
if (!els.statusBadge) {
|
|
return;
|
|
}
|
|
const s = String(status || "active").toLowerCase();
|
|
if (s === "inactive" || s === "rejected" || s === "suspended" || s === "disabled") {
|
|
els.statusBadge.textContent = "INACTIVE";
|
|
els.statusBadge.className =
|
|
"px-3 py-1 bg-danger/10 text-danger rounded-full text-label-md font-bold flex items-center gap-1";
|
|
} else if (s === "pending") {
|
|
els.statusBadge.textContent = s.toUpperCase();
|
|
els.statusBadge.className =
|
|
"px-3 py-1 bg-warning/10 text-warning rounded-full text-label-md font-bold flex items-center gap-1";
|
|
} else if (s === "approved") {
|
|
els.statusBadge.textContent = "ACTIVE";
|
|
els.statusBadge.className =
|
|
"px-3 py-1 bg-success/10 text-success rounded-full text-label-md font-bold flex items-center gap-1";
|
|
} else {
|
|
els.statusBadge.textContent = "ACTIVE";
|
|
els.statusBadge.className =
|
|
"px-3 py-1 bg-success/10 text-success rounded-full text-label-md font-bold flex items-center gap-1";
|
|
}
|
|
};
|
|
|
|
const renderAudit = (logs) => {
|
|
if (!els.auditLogs) {
|
|
return;
|
|
}
|
|
if (!Array.isArray(logs) || !logs.length) {
|
|
els.auditLogs.textContent = "No audit events available for this merchant.";
|
|
return;
|
|
}
|
|
els.auditLogs.innerHTML = logs
|
|
.map((entry) => {
|
|
const when = entry.timestamp
|
|
? new Date(entry.timestamp).toLocaleString("en-GB")
|
|
: "-";
|
|
const title = entry.type || "INFO";
|
|
return `<div class=\"mb-3\"><span class=\"text-slate-500\">[${when}]</span> <span class=\"text-primary-fixed\">${title}:</span> ${entry.message || ""}</div>`;
|
|
})
|
|
.join("");
|
|
};
|
|
|
|
const loadMerchant = async () => {
|
|
try {
|
|
api.requireToken();
|
|
let selectedMerchantId = merchantId;
|
|
if (!selectedMerchantId) {
|
|
const merchants = await api.listMerchants();
|
|
selectedMerchantId = Array.isArray(merchants) && merchants.length ? merchants[0].id : "";
|
|
}
|
|
|
|
if (!selectedMerchantId) {
|
|
showToast("Missing merchant_id in URL");
|
|
return;
|
|
}
|
|
|
|
const [merchant, outlets, devices, txs] = await Promise.all([
|
|
api.getMerchant(selectedMerchantId),
|
|
api.listOutlets({ merchant_id: selectedMerchantId }),
|
|
api.listDevices({ merchant_id: selectedMerchantId }),
|
|
api.listTransactions({ merchant_id: selectedMerchantId })
|
|
]);
|
|
|
|
const name = merchant.legal_name || merchant.brand_name || "Merchant";
|
|
setText(els.breadcrumbName, name);
|
|
setText(els.title, name);
|
|
setText(
|
|
els.code,
|
|
`ID: ${merchant.merchant_code || merchant.id || "—"}`
|
|
);
|
|
setStatusBadge(merchant.onboarding_status || merchant.status);
|
|
setText(els.feeProfile, `Fee Profile: ${merchant.fee_profile_id || "Default"}`);
|
|
setText(els.legalName, merchant.legal_name || merchant.brand_name || name);
|
|
setText(els.taxId, merchant.settlement_account_reference || "Not provided");
|
|
setText(els.address, merchant.address || "-");
|
|
setText(els.category, merchant.category || "General");
|
|
setText(els.onboardingDate, formatDate(merchant.created_at, "-"));
|
|
setText(els.settlementBank, merchant.settlement_account_type || "merchant_bank_account");
|
|
setText(els.settlementAccount, merchant.settlement_account_reference || "Not provided");
|
|
setText(els.settlementHolder, merchant.legal_name || name);
|
|
setText(els.picName, merchant.pic_name || "Not provided");
|
|
setText(els.picRole, merchant.pic_role || "Not provided");
|
|
setText(els.picEmail, merchant.pic_email || "Not provided");
|
|
setText(els.picPhone, merchant.pic_phone || "Not provided");
|
|
setText(els.gps, "GPS: " + (merchant.gps || "Unavailable"));
|
|
|
|
const paid = Array.isArray(txs)
|
|
? txs.filter((item) => String(item.status).toLowerCase() === "paid")
|
|
: [];
|
|
const total = Array.isArray(txs) ? txs.length : 0;
|
|
const volume = paid.reduce((sum, item) => sum + Number(item.amount || 0), 0);
|
|
const successRate = total ? Math.round((paid.length / total) * 100) : 0;
|
|
setText(els.gmv, api.formatMoney(volume));
|
|
setText(els.gmvTrend, total ? `${total} transactions` : "No transactions");
|
|
setText(els.activeDevices, devices?.filter((item) => item.status === "active").length || 0);
|
|
setText(els.registeredDevices, devices?.length || 0);
|
|
setText(els.successRate, `${successRate}%`);
|
|
setText(els.successRateLabel, successRate >= 95 ? "Healthy" : successRate >= 80 ? "Good" : "Review Required");
|
|
|
|
const outletsCount = Array.isArray(outlets) ? outlets.length : 0;
|
|
setText(
|
|
els.auditLogs,
|
|
JSON.stringify(
|
|
{
|
|
merchant,
|
|
summary: {
|
|
outlets_count: outletsCount,
|
|
devices_count: devices?.length || 0,
|
|
total_transactions: total,
|
|
paid_transactions: paid.length,
|
|
loaded_at: new Date().toISOString()
|
|
}
|
|
},
|
|
null,
|
|
2
|
|
)
|
|
);
|
|
showToast("Merchant data loaded");
|
|
} catch (error) {
|
|
console.error("[merchant detail] failed loading", error);
|
|
showToast("Unable to load merchant");
|
|
renderAudit();
|
|
}
|
|
};
|
|
|
|
// Keep existing button micro-interactions
|
|
document.querySelectorAll("button").forEach((btn) => {
|
|
btn.addEventListener("mousedown", () => {
|
|
btn.style.transform = "scale(0.96)";
|
|
btn.style.opacity = "0.9";
|
|
});
|
|
btn.addEventListener("mouseup", () => {
|
|
btn.style.transform = "scale(1)";
|
|
btn.style.opacity = "1";
|
|
});
|
|
btn.addEventListener("mouseleave", () => {
|
|
btn.style.transform = "scale(1)";
|
|
btn.style.opacity = "1";
|
|
});
|
|
});
|
|
|
|
loadMerchant();
|
|
})();
|
|
</script>
|
|
<!-- ui-nav -->
|
|
<div id="__sb_nav" style="position:fixed;left:16px;bottom:16px;z-index:9999;background:#fff;border:1px solid #e2e8f0;padding:8px 10px;border-radius:8px;box-shadow:0 6px 24px rgba(15,23,42,0.12);font-family:Inter,Arial,sans-serif;font-size:12px;line-height:1.4">
|
|
<a href="/ui" style="margin-right:8px;color:#2563eb;text-decoration:none;font-weight:600">UI Catalog</a>
|
|
<a href="/ui/hub" style="margin-right:8px;color:#2563eb;text-decoration:none;font-weight:600">Hub</a>
|
|
<a href="/ui/admin-login" style="margin-right:8px;color:#2563eb;text-decoration:none;font-weight:600">Admin Login</a>
|
|
<a href="/ui/merchant-login" style="margin-right:8px;color:#2563eb;text-decoration:none;font-weight:600">Merchant Login</a>
|
|
<a href="/ui/admin-dashboard-overview" style="margin-right:0;color:#2563eb;text-decoration:none;font-weight:600">Dashboard</a>
|
|
</div>
|
|
'
|
|
</body></html>
|