SiteโฏBuilder
Editing:
index6.php
writable 0666
<?php /***************************************************************** * BestDealOn โ Modular microsite template (2025-07-xx) *****************************************************************/ define('PAGES_ROOT', __DIR__); // /pages define('MODULE_ROOT', PAGES_ROOT.'/modules'); // /pages/modules define('CFG_FILE', PAGES_ROOT.'/config.json'); define('MOD_FILE', PAGES_ROOT.'/modules.json'); /* ---------- tiny helpers ------------------------------------ */ function read_json(string $f){ return is_file($f) ? json_decode(file_get_contents($f),true) : []; } function esc($s){ return htmlspecialchars($s, ENT_QUOTES,'UTF-8'); } /* ---------- 1) static catalogue written by admin interface -- */ $catalogue = []; foreach (read_json(MOD_FILE) as $row) $catalogue[$row['name']] = $row; /* ---------- 2) perโsite config ------------------------------ */ $cfg = read_json(CFG_FILE); $title = $cfg['title'] ?? 'Welcome!'; $slogan = $cfg['slogan'] ?? ''; $brandClr = $cfg['theme']['brand'] ?? '#0066ff'; $accentClr = $cfg['theme']['accent'] ?? '#ffb31c'; $activeSet = $cfg['active'] ?? array_keys($catalogue); // default: show all /* only keep modules that exist + are flagged active + tier โ premium/admin */ $modules = []; foreach ($activeSet as $name){ if (!isset($catalogue[$name])) continue; $row = $catalogue[$name]; if (!$row['active']) continue; // globally off if ($row['tier']==='premium') continue; // eg. free tier site $modules[$name] = $row; } if (!$modules) http_response_code(500) && exit('No modules enabled'); ?><!doctype html> <html lang="en"><head> <meta charset="utf-8"> <title><?=esc($title)?></title> <meta name="viewport" content="width=device-width,initial-scale=1"> <style> :root{ --brand: <?=$brandClr?>; --accent: <?=$accentClr?>; --bg:#f8fafe;--fg:#111;--fg2:#334; font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica,Arial,sans-serif; } *{box-sizing:border-box} body{margin:0;background:var(--bg);color:var(--fg);display:flex;flex-direction:column;min-height:100vh} /* ---------- hero ---------- */ header{background:var(--brand);color:#fff;text-align:center;padding:2.6rem .8rem 2rem} header h1{margin:0;font-size:2.1rem;text-wrap:balance} header p{margin:.4rem 0 0;font-weight:500;font-size:1.05rem;opacity:.9} /* ---------- nav ---------- */ .nav{display:flex;overflow-x:auto;border-bottom:3px solid var(--brand);scroll-snap-type:x mandatory} .nav button{ flex:0 0 auto;padding:1rem 1.2rem;font:inherit;border:none;background:#fff;cursor:pointer; min-width:110px;font-weight:600;color:var(--fg2);border-right:1px solid #e4e8f3; scroll-snap-align:start;white-space:nowrap } .nav button[aria-selected="true"]{ background:var(--brand);color:#fff;position:relative; } .nav button[aria-selected="true"]::after{ content:'';position:absolute;inset-inline:0;bottom:-3px;height:3px;background:var(--accent); } /* ---------- stage ---------- */ #stage{padding:2rem .8rem;max-width:960px;margin:auto;width:100%} .spinner{text-align:center;font-size:1.1rem;color:var(--accent)} /* ---------- misc ---------- */ @media(max-width:600px){header h1{font-size:1.6rem}} </style> </head><body> <header> <h1><?=esc($title)?></h1> <?php if($slogan): ?><p><?=esc($slogan)?></p><?php endif ?> </header> <nav class="nav" id="nav"> <?php $first=true; foreach($modules as $n=>$row): ?> <button role="tab" data-mod="<?=esc($n)?>" aria-selected="<?=$first?'true':'false'?>"> <?=ucwords(str_replace('-',' ',$n))?> </button> <?php $first=false; endforeach ?> </nav> <main id="stage" role="tabpanel"> <div class="spinner">โณ Loadingโฆ</div> </main> <script> const mods = <?=json_encode($modules)?>; // name โ row const cache = {}; // already fetched html /* fetch+display first tab immediately */ activate(document.querySelector('#nav button[aria-selected="true"]')); document.getElementById('nav').addEventListener('click', e=>{ const btn = e.target.closest('button[data-mod]'); if (!btn) return; activate(btn); }); /* dragโscroll on desktop */ let isDown=false,startX,scrollL,nav=document.getElementById('nav'); nav.addEventListener('mousedown',e=>{isDown=true;nav.classList.add('grab');startX=e.pageX;scrollL=nav.scrollLeft}); window.addEventListener('mousemove',e=>{if(!isDown)return;nav.scrollLeft=scrollL-(e.pageX-startX)}); window.addEventListener('mouseup',()=>{isDown=false;nav.classList.remove('grab')}); /* ---------- core ---------- */ async function activate(btn){ if(btn.getAttribute('aria-selected')==='true') return; document.querySelectorAll('#nav button').forEach(b=>b.setAttribute('aria-selected','false')); btn.setAttribute('aria-selected','true'); const name=btn.dataset.mod; const stage=document.getElementById('stage'); stage.innerHTML='<div class="spinner">โณ Loadingโฆ</div>'; if(cache[name]) { stage.innerHTML=cache[name]; return; } try{ const rsp = await fetch(mods[name].http_path, {headers:{'X-Module-Only':'1'}}); if(!rsp.ok) throw 0; const html = await rsp.text(); cache[name]=html; stage.innerHTML=html; /* run inline scripts safely */ stage.querySelectorAll('script').forEach(s=>{ const n=document.createElement('script'); n.textContent=s.textContent; document.body.appendChild(n); }); }catch(e){ stage.innerHTML='<div class="spinner">โ Unable to load module.</div>'; } } </script> </body></html>
Save changes
Create folder
writable 0777
Create
Cancel