Spaces:
Running
Running
Download index.html from 7Ali/globall-cloud: direct link, hf CLI and curl.
- Browser
- Download file 66.6 kB
-
https://huggingface.co/spaces/7Ali/globall-cloud/resolve/main/index.html
- Command line
-
hf download hf://spaces/7Ali/globall-cloud/index.html
-
curl -L -o index.html https://huggingface.co/spaces/7Ali/globall-cloud/resolve/main/index.html
66.6 kB
| <html lang="ckb" dir="rtl" data-theme="dark"> | |
| <head> | |
| <meta charset="utf-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1"> | |
| <title>Globall Cloud — پلاتفۆرمی گواستنەوە (چین · دوبەی · عێراق)</title> | |
| <link rel="preconnect" href="https://fonts.googleapis.com"> | |
| <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> | |
| <link href="https://fonts.googleapis.com/css2?family=Vazirmatn:wght@300;400;500;600;700;800&display=swap" rel="stylesheet"> | |
| <style> | |
| :root{ | |
| --bg:#070a14; --bg2:#0b1120; --card:rgba(255,255,255,.045); --card2:rgba(255,255,255,.07); | |
| --line:rgba(255,255,255,.10); --txt:#eaf0ff; --mut:#94a3c4; | |
| --pri:#3b82f6; --pri2:#22d3ee; --ok:#22c55e; --warn:#f59e0b; --bad:#ef4444; | |
| --r:16px; --shadow:0 22px 55px rgba(0,0,0,.45); | |
| } | |
| [data-theme="light"]{ | |
| --bg:#f4f7fd; --bg2:#ffffff; --card:#ffffff; --card2:#f8faff; --line:#e2e8f6; | |
| --txt:#0d1630; --mut:#5a678a; --shadow:0 18px 40px rgba(14,30,70,.10); | |
| } | |
| *{box-sizing:border-box} | |
| html{scroll-behavior:smooth} | |
| body{ | |
| margin:0;font-family:'Vazirmatn',system-ui,-apple-system,'Segoe UI',Tahoma,sans-serif; | |
| background:var(--bg);color:var(--txt);line-height:1.8;-webkit-font-smoothing:antialiased; | |
| background-image:radial-gradient(900px 520px at 88% -12%,rgba(59,130,246,.20),transparent 62%), | |
| radial-gradient(700px 420px at 6% 4%,rgba(34,211,238,.12),transparent 60%); | |
| background-attachment:fixed; | |
| } | |
| .wrap{max-width:1180px;margin:0 auto;padding:0 18px} | |
| h1,h2,h3,h4{margin:0 0 .4rem;line-height:1.45;letter-spacing:-.2px} | |
| p{margin:0 0 .6rem} | |
| small{font-size:.8rem} | |
| .hidden{display:none!important} | |
| .muted{color:var(--mut)} | |
| .center{text-align:center} | |
| .row{display:flex;gap:10px;align-items:center;flex-wrap:wrap} | |
| .spread{display:flex;justify-content:space-between;align-items:center;gap:12px;flex-wrap:wrap} | |
| .nav{position:sticky;top:0;z-index:60;backdrop-filter:blur(14px); | |
| background:color-mix(in srgb,var(--bg) 78%,transparent);border-bottom:1px solid var(--line)} | |
| .nav-in{display:flex;align-items:center;gap:14px;justify-content:space-between;min-height:66px} | |
| .brand{display:flex;align-items:center;gap:10px;cursor:pointer;background:none;border:0;color:inherit;font:inherit} | |
| .brand .logo{width:38px;height:38px;border-radius:12px;display:grid;place-items:center;font-weight:800; | |
| background:linear-gradient(135deg,var(--pri),var(--pri2));color:#04101f;box-shadow:0 8px 22px rgba(59,130,246,.35)} | |
| .brand b{display:block;font-size:.98rem} | |
| .brand small{display:block;color:var(--mut);font-size:.7rem;letter-spacing:.4px} | |
| .links{display:flex;gap:4px;overflow-x:auto;scrollbar-width:none} | |
| .links::-webkit-scrollbar{display:none} | |
| .links button{background:none;border:0;color:var(--mut);font:inherit;font-size:.9rem;padding:8px 12px;border-radius:10px;cursor:pointer;white-space:nowrap} | |
| .links button:hover{color:var(--txt);background:var(--card)} | |
| .links button.on{color:var(--txt);background:var(--card2);box-shadow:inset 0 0 0 1px var(--line)} | |
| .nav-act{display:flex;align-items:center;gap:8px} | |
| .btn{border:1px solid var(--line);background:var(--card);color:var(--txt);font:inherit;font-size:.9rem; | |
| padding:10px 16px;border-radius:12px;cursor:pointer;transition:.18s;display:inline-flex;align-items:center;gap:8px;text-decoration:none} | |
| .btn:hover{transform:translateY(-1px);background:var(--card2)} | |
| .btn.primary{border-color:transparent;background:linear-gradient(135deg,var(--pri),var(--pri2));color:#04101f;font-weight:700; | |
| box-shadow:0 10px 26px rgba(59,130,246,.30)} | |
| .btn.ok{background:linear-gradient(135deg,#22c55e,#16a34a);color:#05150b;border-color:transparent;font-weight:700} | |
| .btn.danger{background:rgba(239,68,68,.14);border-color:rgba(239,68,68,.4);color:#ffb4b4} | |
| .btn.sm{padding:7px 11px;font-size:.82rem;border-radius:10px} | |
| .btn.icon{padding:9px 11px} | |
| .btn:disabled{opacity:.5;cursor:not-allowed;transform:none} | |
| .badge{font-size:.72rem;padding:5px 10px;border-radius:999px;border:1px solid var(--line);background:var(--card);color:var(--mut);white-space:nowrap} | |
| .badge.live{color:#8ff0c4;border-color:rgba(34,197,94,.35);background:rgba(34,197,94,.12)} | |
| .badge.demo{color:#ffd894;border-color:rgba(245,158,11,.35);background:rgba(245,158,11,.12)} | |
| section.view{padding:34px 0 10px;animation:fade .35s ease} | |
| @keyframes fade{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}} | |
| .card{background:var(--card);border:1px solid var(--line);border-radius:var(--r);padding:20px;box-shadow:var(--shadow)} | |
| .grid{display:grid;gap:16px} | |
| .g3{grid-template-columns:repeat(3,1fr)} | |
| .g4{grid-template-columns:repeat(4,1fr)} | |
| .g2{grid-template-columns:repeat(2,1fr)} | |
| @media(max-width:980px){.g3,.g4{grid-template-columns:repeat(2,1fr)}} | |
| @media(max-width:680px){.g2,.g3,.g4{grid-template-columns:1fr}.nav-in{min-height:auto;padding:10px 0;flex-wrap:wrap}} | |
| .eyebrow{font-size:.74rem;letter-spacing:2px;color:var(--pri2);font-weight:700;text-transform:uppercase} | |
| .hero{padding:26px 0 6px} | |
| .hero h1{font-size:clamp(1.5rem,3.6vw,2.6rem);font-weight:800} | |
| .hero h1 em{font-style:normal;background:linear-gradient(120deg,var(--pri2),var(--pri));-webkit-background-clip:text;background-clip:text;color:transparent} | |
| .hero p.lead{color:var(--mut);max-width:70ch;font-size:1rem} | |
| .route{display:grid;grid-template-columns:1fr auto 1fr auto 1fr;align-items:center;gap:10px;margin:18px 0 6px} | |
| .node{background:var(--card);border:1px solid var(--line);border-radius:14px;padding:12px 14px;text-align:center} | |
| .node b{display:block;font-size:.72rem;letter-spacing:1.4px;color:var(--mut)} | |
| .node span{font-weight:700} | |
| .track-line{height:3px;border-radius:3px;background:linear-gradient(90deg,var(--pri),var(--pri2));position:relative;min-width:50px} | |
| .track-line::after{content:"";position:absolute;top:-4px;width:11px;height:11px;border-radius:50%; | |
| background:#fff;box-shadow:0 0 14px var(--pri2);animation:move 3.4s linear infinite} | |
| @keyframes move{0%{right:0}100%{right:calc(100% - 11px)}} | |
| @media(max-width:680px){.route{grid-template-columns:1fr;text-align:center}.track-line{width:3px;height:34px;margin:0 auto} | |
| .track-line::after{animation:movev 3.4s linear infinite}@keyframes movev{0%{top:0;right:-4px}100%{top:calc(100% - 11px);right:-4px}}} | |
| label.fl{display:block;font-size:.8rem;color:var(--mut);margin:10px 0 5px;font-weight:600} | |
| input,select,textarea{width:100%;background:var(--bg2);border:1px solid var(--line);color:var(--txt); | |
| border-radius:12px;padding:11px 13px;font:inherit;font-size:.92rem;transition:.18s} | |
| input:focus,select:focus,textarea:focus{outline:0;border-color:var(--pri);box-shadow:0 0 0 3px rgba(59,130,246,.18)} | |
| input::placeholder{color:color-mix(in srgb,var(--mut) 75%,transparent)} | |
| .hint{background:rgba(34,211,238,.09);border:1px dashed rgba(34,211,238,.35);border-radius:12px;padding:10px 12px;font-size:.82rem;color:var(--mut)} | |
| .err{color:#ffb4b4;font-size:.85rem;min-height:1.2em} | |
| .tw{overflow:auto;border-radius:14px;border:1px solid var(--line)} | |
| table{width:100%;border-collapse:collapse;font-size:.88rem;min-width:640px} | |
| th,td{padding:11px 13px;text-align:right;border-bottom:1px solid var(--line);white-space:nowrap} | |
| th{background:var(--card2);color:var(--mut);font-size:.78rem;letter-spacing:.4px;font-weight:700;position:sticky;top:0} | |
| tbody tr:hover{background:var(--card)} | |
| tbody tr:last-child td{border-bottom:0} | |
| .st{font-size:.75rem;padding:4px 10px;border-radius:999px;font-weight:700;display:inline-block} | |
| .st.picked{background:rgba(148,163,196,.16);color:#c8d3ea} | |
| .st.transit{background:rgba(59,130,246,.16);color:#9dc0ff} | |
| .st.dubai{background:rgba(34,211,238,.16);color:#8ff0f6} | |
| .st.customs{background:rgba(245,158,11,.16);color:#ffd894} | |
| .st.delivered{background:rgba(34,197,94,.16);color:#8ff0c4} | |
| .tabs{display:flex;gap:6px;flex-wrap:wrap;margin:14px 0 18px} | |
| .tabs button{background:var(--card);border:1px solid var(--line);color:var(--mut);font:inherit;font-size:.86rem; | |
| padding:9px 14px;border-radius:11px;cursor:pointer} | |
| .tabs button.on{color:#04101f;background:linear-gradient(135deg,var(--pri),var(--pri2));border-color:transparent;font-weight:700} | |
| .stat{background:var(--card);border:1px solid var(--line);border-radius:14px;padding:14px 16px} | |
| .stat b{display:block;font-size:1.5rem;font-weight:800;line-height:1.3} | |
| .stat small{color:var(--mut)} | |
| .tl{list-style:none;margin:0;padding:0} | |
| .tl li{display:flex;gap:12px;padding:10px 0;border-bottom:1px dashed var(--line)} | |
| .tl li:last-child{border-bottom:0} | |
| .tl .dot{width:26px;height:26px;border-radius:50%;display:grid;place-items:center;font-size:.72rem;flex:0 0 26px; | |
| border:1px solid var(--line);color:var(--mut);background:var(--card)} | |
| .tl li.done .dot{background:linear-gradient(135deg,var(--ok),#16a34a);color:#04150a;border-color:transparent;font-weight:800} | |
| .tl li.now .dot{background:linear-gradient(135deg,var(--pri),var(--pri2));color:#04101f;border-color:transparent;font-weight:800} | |
| .tl .tt b{display:block} | |
| .tl .tt small{color:var(--mut)} | |
| .mb{position:fixed;inset:0;background:rgba(4,8,18,.68);backdrop-filter:blur(5px);display:grid;place-items:center;z-index:120;padding:18px} | |
| .modal{background:var(--bg2);border:1px solid var(--line);border-radius:18px;max-width:620px;width:100%; | |
| max-height:88vh;overflow:auto;padding:22px;box-shadow:var(--shadow);animation:pop .22s ease} | |
| @keyframes pop{from{transform:scale(.96);opacity:0}to{transform:scale(1);opacity:1}} | |
| #toasts{position:fixed;bottom:18px;left:18px;z-index:200;display:flex;flex-direction:column;gap:8px} | |
| .toast{background:var(--bg2);border:1px solid var(--line);border-inline-start:4px solid var(--pri); | |
| border-radius:12px;padding:11px 15px;font-size:.87rem;box-shadow:var(--shadow);animation:fade .25s ease;max-width:340px} | |
| .toast.ok{border-inline-start-color:var(--ok)} | |
| .toast.bad{border-inline-start-color:var(--bad)} | |
| .toast.warn{border-inline-start-color:var(--warn)} | |
| .feat{display:flex;gap:11px;align-items:flex-start} | |
| .feat .ic{width:40px;height:40px;flex:0 0 40px;border-radius:12px;display:grid;place-items:center;font-size:1.15rem; | |
| background:var(--card2);border:1px solid var(--line)} | |
| .kv{display:flex;justify-content:space-between;gap:12px;padding:8px 0;border-bottom:1px dashed var(--line);font-size:.9rem} | |
| .kv:last-child{border-bottom:0} | |
| .kv span{color:var(--mut)} | |
| code{background:var(--card2);border:1px solid var(--line);border-radius:7px;padding:2px 7px;font-size:.82rem; | |
| font-family:ui-monospace,SFMono-Regular,Menlo,monospace;direction:ltr;display:inline-block} | |
| footer{border-top:1px solid var(--line);margin-top:44px;padding:26px 0;color:var(--mut);font-size:.86rem} | |
| a{color:var(--pri2)} | |
| .skel{height:12px;border-radius:6px;background:linear-gradient(90deg,var(--card),var(--card2),var(--card)); | |
| background-size:200% 100%;animation:sk 1.3s linear infinite} | |
| @keyframes sk{from{background-position:200% 0}to{background-position:-200% 0}} | |
| </style> | |
| </head> | |
| <body> | |
| <header class="nav"> | |
| <div class="wrap nav-in"> | |
| <button class="brand" data-go="home" aria-label="سەرەتا"> | |
| <span class="logo">GC</span> | |
| <span><b>Globall Cloud</b><small>CHINA · UAE · IRAQ LOGISTICS</small></span> | |
| </button> | |
| <nav class="links" id="navLinks"> | |
| <button data-go="home" class="on">سەرەتا</button> | |
| <button data-go="track">شوێنکەوتنی بار</button> | |
| <button data-go="customer">هەژماری کڕیار</button> | |
| <button data-go="staff">کۆنسۆڵی ستاف</button> | |
| </nav> | |
| <div class="nav-act"> | |
| <span class="badge" id="modeBadge">Demo</span> | |
| <button class="btn icon" id="themeBtn" title="گۆڕینی ڕووکار" aria-label="گۆڕینی ڕووکار">🌙</button> | |
| <button class="btn primary" data-go="customer" id="navCta">چوونەژوورەوە</button> | |
| </div> | |
| </div> | |
| </header> | |
| <main> | |
| <section class="view wrap" id="view-home"> | |
| <div class="hero"> | |
| <p class="eyebrow">NETWORK ONLINE · AIR · SEA · LAND</p> | |
| <h1>بارەکەت، هەژمارەکەت،<br><em>هەموو گواستنەوەکە لە یەک شوێن.</em></h1> | |
| <p class="lead">Globall Cloud پلاتفۆرمێکی یەکگرتووە بۆ گواستنەوەی Air، Sea و Land لە چین و دوبەی بۆ هەولێر و هەموو عێراق — بە tracking، payment، بەڵگە و پشتیوانیی زیندوو.</p> | |
| <div class="route" aria-hidden="true"> | |
| <div class="node"><b>ORIGIN HUB</b><span>گوانگژۆ · چین</span></div> | |
| <div class="track-line"></div> | |
| <div class="node"><b>TRANSIT HUB</b><span>دوبەی · UAE</span></div> | |
| <div class="track-line"></div> | |
| <div class="node"><b>DELIVERY HUB</b><span>هەولێر · عێراق</span></div> | |
| </div> | |
| </div> | |
| <div class="card" id="track" style="margin-top:26px"> | |
| <div class="spread"> | |
| <div> | |
| <h3>📦 ژمارەی بارەکەت هەیە؟ لێرە بگەڕێ</h3> | |
| <p class="muted" style="margin:0;font-size:.9rem">بێ چوونەژوورەوەش دۆخی گشتیی shipment ـەکەت ببینە.</p> | |
| </div> | |
| <span class="badge live">● سیستم چالاکە</span> | |
| </div> | |
| <div class="row" style="margin-top:14px"> | |
| <input id="trkInput" placeholder="نموونە: GC-2026-1042" style="flex:1;min-width:220px" aria-label="ژمارەی بار"> | |
| <button class="btn primary" id="trkBtn">گەڕان</button> | |
| </div> | |
| <div id="trkOut" style="margin-top:16px"></div> | |
| </div> | |
| <h2 style="margin-top:38px">خزمەتگوزارییەکان</h2> | |
| <p class="muted">هەموو ئەو شتانەی بەکارت دێت — لە وەرگرتنەوە تا دەرگای کڕیار.</p> | |
| <div class="grid g3" style="margin-top:14px" id="svcGrid"></div> | |
| <h2 style="margin-top:38px">لە داواکاری تا Door-to-Door</h2> | |
| <div class="grid g3" style="margin-top:14px"> | |
| <div class="card"> | |
| <p class="eyebrow">01 · PICKUP & QC</p> | |
| <p style="margin:0">بار وەردەگیرێت، وێنە و QC تۆمار دەکرێت و packet chain بەردەوام دەبێت.</p> | |
| <div class="row" style="margin-top:10px"><span class="badge">SCAN</span><span class="badge">PHOTO</span><span class="badge">QC</span></div> | |
| </div> | |
| <div class="card"> | |
| <p class="eyebrow">02 · TRANSIT</p> | |
| <p style="margin:0">Air، Sea یان Land بە status و route leg ـەکان بەدواداچوون دەکرێت.</p> | |
| <div class="row" style="margin-top:10px"><span class="badge">AIR</span><span class="badge">SEA</span><span class="badge">LAND</span></div> | |
| </div> | |
| <div class="card"> | |
| <p class="eyebrow">03 · CUSTOMS & DELIVERY</p> | |
| <p style="margin:0">بەڵگە، گومرگ، invoice و گەیاندنی کۆتایی لە هەمان timeline ـدا.</p> | |
| <div class="row" style="margin-top:10px"><span class="badge">DOCS</span><span class="badge">CUSTOMS</span><span class="badge">D2D</span></div> | |
| </div> | |
| </div> | |
| <h2 style="margin-top:38px">یەک سایت، دوو جۆری بەکارهێنەر</h2> | |
| <div class="grid g2" style="margin-top:14px"> | |
| <div class="card"> | |
| <div class="feat"><div class="ic">👤</div><div> | |
| <h3>هەژماری کڕیار</h3> | |
| <p class="muted">تۆماربوون یان چوونەژوورەوە بۆ بینینی بارەکان، invoice، payment، بەڵگە و ئاگاداری.</p> | |
| </div></div> | |
| <div class="row" style="margin-top:12px"><span class="badge">MY SHIPMENTS</span><span class="badge">PAYMENTS</span><span class="badge">DOCUMENTS</span></div> | |
| <button class="btn primary" style="margin-top:14px" data-go="customer">کردنەوەی هەژمار ←</button> | |
| </div> | |
| <div class="card"> | |
| <div class="feat"><div class="ic">🛡️</div><div> | |
| <h3>کۆنسۆڵی ستاف</h3> | |
| <p class="muted">بۆ کارمەندان، بە role-based access، 2FA و audit trail.</p> | |
| </div></div> | |
| <div class="row" style="margin-top:12px"><span class="badge">SUPER ADMIN</span><span class="badge">ADMIN</span><span class="badge">ACCOUNTANT</span><span class="badge">2FA</span></div> | |
| <button class="btn" style="margin-top:14px" data-go="staff">چوونە ناو کۆنسۆڵ ←</button> | |
| </div> | |
| </div> | |
| </section> | |
| <section class="view wrap hidden" id="view-track"></section> | |
| <section class="view wrap hidden" id="view-customer"> | |
| <div id="custAuth"> | |
| <div class="card" style="max-width:520px;margin:20px auto"> | |
| <div class="spread"> | |
| <h2 id="custAuthTitle">چوونەژوورەوە</h2> | |
| <span class="badge" id="custStep">1 / 2</span> | |
| </div> | |
| <p class="muted" style="font-size:.9rem">هەژماری کڕیار — بینینی بار، پارەدان و بەڵگەکان.</p> | |
| <div id="custForm"> | |
| <label class="fl" for="cuEmail">ئیمەیل</label> | |
| <input id="cuEmail" type="email" placeholder="you@example.com" autocomplete="email"> | |
| <label class="fl" for="cuPass">وشەی نهێنی</label> | |
| <input id="cuPass" type="password" placeholder="••••••••" autocomplete="current-password"> | |
| <p class="err" id="cuErr"></p> | |
| <div class="row" style="margin-top:6px"> | |
| <button class="btn primary" id="cuLogin">چوونەژوورەوە</button> | |
| <button class="btn" id="cuSignup">دروستکردنی هەژمار</button> | |
| </div> | |
| </div> | |
| <div id="custOtp" class="hidden"> | |
| <p style="margin-top:10px">کۆدێکی ٦ ژمارەیی نێردرا بۆ <b id="cuOtpMail"></b>. تکایە بینووسە بۆ پشتڕاستکردنەوە (سەیری <b>Spam/Junk</b>ـیش بکە).</p> | |
| <label class="fl" for="cuCode">کۆدی پشتڕاستکردنەوە</label> | |
| <input id="cuCode" inputmode="numeric" maxlength="6" placeholder="______" style="letter-spacing:8px;text-align:center;font-size:1.2rem"> | |
| <p class="err" id="cuOtpErr"></p> | |
| <div class="row" style="margin-top:6px"> | |
| <button class="btn primary" id="cuVerify">پشتڕاستکردنەوە</button> | |
| <button class="btn" id="cuResend" disabled>دووبارە ناردن</button> | |
| <button class="btn" id="cuBack">گەڕانەوە</button> | |
| </div> | |
| </div> | |
| <div class="hint" style="margin-top:14px" id="cuHint"></div> | |
| </div> | |
| </div> | |
| <div id="custApp" class="hidden"> | |
| <div class="spread" style="margin-top:6px"> | |
| <div> | |
| <p class="eyebrow">CUSTOMER PORTAL</p> | |
| <h2 style="margin:0">بەخێربێی، <span id="cuName">کڕیار</span> 👋</h2> | |
| </div> | |
| <div class="row"> | |
| <span class="badge" id="cuRoleBadge">CUSTOMER</span> | |
| <button class="btn sm" id="cuLogout">چوونەدەرەوە</button> | |
| </div> | |
| </div> | |
| <div class="grid g4" style="margin-top:16px" id="cuStats"></div> | |
| <div class="tabs" id="cuTabs"></div> | |
| <div id="cuBody"></div> | |
| </div> | |
| </section> | |
| <section class="view wrap hidden" id="view-staff"> | |
| <div id="staffAuth"> | |
| <div class="card" style="max-width:520px;margin:20px auto"> | |
| <p class="eyebrow">GLOBALL CLOUD · SECURE ACCESS</p> | |
| <div class="spread"><h2 id="staffAuthTitle">کۆنسۆڵی بەڕێوەبردن</h2><span class="badge" id="staffStep">1 / 2</span></div> | |
| <p class="muted" style="font-size:.9rem">تەنها بۆ ئەندامانی ستاف — ئەدمین و ژمێریار.</p> | |
| <div id="staffForm"> | |
| <label class="fl" for="stEmail">ئیمەیل</label> | |
| <input id="stEmail" type="email" placeholder="admin@globall.iq" autocomplete="email"> | |
| <label class="fl" for="stPass">وشەی نهێنی</label> | |
| <input id="stPass" type="password" placeholder="••••••••" autocomplete="current-password"> | |
| <p class="err" id="stErr"></p> | |
| <button class="btn primary" id="stLogin" style="margin-top:6px">چوونەژوورەوە</button> | |
| <div class="row" style="margin-top:12px"><span class="badge">TLS</span><span class="badge">RLS</span><span class="badge">AUTO-LOCK</span></div> | |
| </div> | |
| <div id="staffOtp" class="hidden"> | |
| <h3 style="margin-top:12px">دووەم هەنگاوی پشتڕاستکردنەوە</h3> | |
| <p class="muted" style="font-size:.9rem">کۆدی ٦ ژمارەیی لە ئەپی Authenticator بنووسە.</p> | |
| <label class="fl" for="stCode">کۆدی پشتڕاستکردنەوە</label> | |
| <input id="stCode" inputmode="numeric" maxlength="6" placeholder="______" style="letter-spacing:8px;text-align:center;font-size:1.2rem"> | |
| <p class="err" id="stOtpErr"></p> | |
| <div class="row" style="margin-top:6px"> | |
| <button class="btn primary" id="stVerify">پشتڕاستکردنەوە</button> | |
| <button class="btn" id="stBack">گەڕانەوە</button> | |
| </div> | |
| </div> | |
| <div class="hint" style="margin-top:14px" id="stHint"></div> | |
| </div> | |
| </div> | |
| <div id="staffApp" class="hidden"> | |
| <div class="spread" style="margin-top:6px"> | |
| <div><p class="eyebrow">STAFF CONSOLE · SECURE</p><h2 style="margin:0" id="stWho">—</h2></div> | |
| <div class="row"> | |
| <span class="badge live" id="stRoleBadge">—</span> | |
| <button class="btn sm" id="stLogout">چوونەدەرەوە</button> | |
| </div> | |
| </div> | |
| <div class="grid g4" style="margin-top:16px" id="stStats"></div> | |
| <div class="tabs" id="stTabs"></div> | |
| <div id="stBody"></div> | |
| </div> | |
| </section> | |
| </main> | |
| <footer> | |
| <div class="wrap"> | |
| <div class="grid g3"> | |
| <div><h4>Globall Cloud</h4> | |
| <p class="muted">Air · Sea · Land · Customs · Warehousing · Door-to-Door<br>چین · دوبەی · عێراق</p></div> | |
| <div><h4>پەیوەندی</h4> | |
| <p style="margin:0"> | |
| <a href="https://wa.me/9647507577137">WhatsApp: +964 750 757 7137</a><br> | |
| <a href="mailto:tamanblbas271@gmail.com">tamanblbas271@gmail.com</a><br> | |
| هەولێر · عێراق | |
| </p></div> | |
| <div><h4>ناوەندەکان</h4> | |
| <p class="muted" style="margin:0">هەولێر · سلێمانی · دهۆک · بەغدا · کەرکووک · موسڵ · بەسرە</p></div> | |
| </div> | |
| <p class="muted" style="margin-top:18px;font-size:.8rem" id="footNote"></p> | |
| </div> | |
| </footer> | |
| <div id="toasts" aria-live="polite"></div> | |
| <div class="mb hidden" id="mb"><div class="modal" id="mbCard"></div></div> | |
| <script> | |
| /* ===== ١) کۆنفیگ — تەنها ئەم بەشە بگۆڕە ===== */ | |
| const CONFIG = { | |
| SUPABASE_URL: "", // https://xxxx.supabase.co | |
| SUPABASE_ANON_KEY: "", // anon / publishable key (هەرگیز service_role دانیە) | |
| SUPPORT_WHATSAPP: "9647507577137", | |
| OTP_DEMO_CODE: "123456", | |
| OTP_RESEND_SECONDS: 30 | |
| }; | |
| const LIVE = !!(CONFIG.SUPABASE_URL && CONFIG.SUPABASE_ANON_KEY); | |
| let supabase = null; | |
| if(LIVE){ | |
| const s = document.createElement("script"); | |
| s.src = "https://cdn.jsdelivr.net/npm/@supabase/supabase-js@2"; | |
| s.onload = () => { | |
| supabase = window.supabase.createClient(CONFIG.SUPABASE_URL, CONFIG.SUPABASE_ANON_KEY); | |
| toast("پەیوەست بە Supabase ✅","ok"); | |
| }; | |
| document.head.appendChild(s); | |
| } | |
| /* ===== ٢) داتای نموونە ===== */ | |
| const STAGES = [ | |
| { k:"picked", t:"وەرگیراوە لە گوانگژۆ", d:"SCAN · QC · PHOTO" }, | |
| { k:"transit", t:"لە ڕێگادا", d:"AIR / SEA / LAND LEG" }, | |
| { k:"dubai", t:"گەیشتووە بە دوبەی", d:"UAE TRANSIT HUB" }, | |
| { k:"customs", t:"گومرگ و بەڵگە", d:"CUSTOMS CLEARANCE" }, | |
| { k:"delivered", t:"گەیەندرا", d:"DOOR-TO-DOOR" } | |
| ]; | |
| const ST_LABEL = {picked:"وەرگیراوە",transit:"لە ڕێگادا",dubai:"لە دوبەی",customs:"گومرگ",delivered:"گەیەندرا"}; | |
| const ROLES = { super_admin:"سوپەر ئەدمین", admin:"ئەدمین", accountant:"ژمێریار" }; | |
| const PERMS = { | |
| super_admin:["overview","shipments","customers","staff","finance","audit","settings"], | |
| admin:["overview","shipments","customers"], | |
| accountant:["overview","finance"] | |
| }; | |
| const TABS = { overview:"پێداچوونەوە", shipments:"بارەکان", customers:"موشتەریان", | |
| staff:"ستاف", finance:"دارایی", audit:"Audit Trail", settings:"ڕێکخستن" }; | |
| const DB = { | |
| shipments: [ | |
| { code:"GC-2026-1042", cust:"C-1001", name:"ئاراس مارکێت", mode:"Air", from:"گوانگژۆ", to:"هەولێر", kg:180, usd:1450, st:2, eta:"2026-09-27", updated:"2026-09-21 14:20" }, | |
| { code:"GC-2026-1041", cust:"C-1002", name:"زێدان ئێلێکترۆنیک", mode:"Sea", from:"شێنژێن", to:"بەغدا", kg:5200, usd:8600, st:1, eta:"2026-10-14", updated:"2026-09-20 09:05" }, | |
| { code:"GC-2026-1038", cust:"C-1003", name:"هاوسازی گشتی", mode:"Land", from:"دوبەی", to:"سلێمانی", kg:980, usd:2300, st:3, eta:"2026-09-24", updated:"2026-09-22 08:10" }, | |
| { code:"GC-2026-1035", cust:"C-1001", name:"ئاراس مارکێت", mode:"Air", from:"گوانگژۆ", to:"هەولێر", kg:64, usd:520, st:4, eta:"2026-09-18", updated:"2026-09-18 17:40" }, | |
| { code:"GC-2026-1030", cust:"C-1004", name:"دەرمانخانەی نەورۆز", mode:"Air", from:"گوانگژۆ", to:"دهۆک", kg:210, usd:1980, st:0, eta:"2026-10-02", updated:"2026-09-22 11:15" }, | |
| { code:"GC-2026-1027", cust:"C-1005", name:"بەرگەی کوردستان", mode:"Sea", from:"نینگبۆ", to:"بەسرە", kg:7400, usd:11500, st:1, eta:"2026-10-20", updated:"2026-09-19 12:00" } | |
| ], | |
| customers: [ | |
| { code:"C-1001", name:"ئاراس مارکێت", p1:"+964 750 111 2233", p2:"+964 770 111 2233", city:"هەولێر", addr:"شەقامی ١٠٠ مەتری", note:"کڕیاری VIP" }, | |
| { code:"C-1002", name:"زێدان ئێلێکترۆنیک", p1:"+964 751 444 5566", p2:"", city:"بەغدا", addr:"شارەوانی", note:"" }, | |
| { code:"C-1003", name:"هاوسازی گشتی", p1:"+964 770 777 8899", p2:"+964 750 777 8899", city:"سلێمانی", addr:"سالم ستریت", note:"پارەدان دواخراوە" }, | |
| { code:"C-1004", name:"دەرمانخانەی نەورۆز", p1:"+964 771 222 3344", p2:"", city:"دهۆک", addr:"نزیک نەخۆشخانە", note:"" }, | |
| { code:"C-1005", name:"بەرگەی کوردستان", p1:"+964 780 555 6677", p2:"", city:"بەسرە", addr:"بەندەری ئوم قەسر", note:"بارە قورسەکان" } | |
| ], | |
| staff: [ | |
| { id:"S-1", name:"تامەن ئەلباس", email:"admin@globall.iq", role:"super_admin", active:true }, | |
| { id:"S-2", name:"کارمەند ئەدمین", email:"ops@globall.iq", role:"admin", active:true }, | |
| { id:"S-3", name:"ژمێریار", email:"finance@globall.iq", role:"accountant", active:true } | |
| ], | |
| invoices: [ | |
| { no:"INV-2026-331", cust:"C-1001", usd:1450, status:"پارەدراوە", date:"2026-09-15" }, | |
| { no:"INV-2026-330", cust:"C-1002", usd:8600, status:"کراوە", date:"2026-09-12" }, | |
| { no:"INV-2026-328", cust:"C-1003", usd:2300, status:"دواکەوتوو", date:"2026-09-02" }, | |
| { no:"INV-2026-325", cust:"C-1004", usd:1980, status:"پارەدراوە", date:"2026-08-28" } | |
| ], | |
| docs: [ | |
| { name:"Invoice-GC-2026-1042.pdf", ship:"GC-2026-1042", date:"2026-09-15", size:"184 KB" }, | |
| { name:"Packing-List-1042.pdf", ship:"GC-2026-1042", date:"2026-09-14", size:"96 KB" }, | |
| { name:"Customs-Dec-1038.pdf", ship:"GC-2026-1038", date:"2026-09-22", size:"212 KB" }, | |
| { name:"QC-Photos-1030.zip", ship:"GC-2026-1030", date:"2026-09-22", size:"3.4 MB" } | |
| ], | |
| audit: [ | |
| { at:"2026-09-22 11:15", who:"admin@globall.iq", act:"زیادکردنی shipment GC-2026-1030" }, | |
| { at:"2026-09-22 08:10", who:"ops@globall.iq", act:"گۆڕینی status بۆ GC-2026-1038" }, | |
| { at:"2026-09-21 17:02", who:"admin@globall.iq", act:"دروستکردنی هەژماری کارمەند ops@globall.iq" } | |
| ] | |
| }; | |
| /* ===== ٣) ئامرازەکان ===== */ | |
| const $ = s => document.querySelector(s); | |
| const money = n => "$" + Number(n).toLocaleString("en-US"); | |
| const kgf = n => Number(n).toLocaleString("en-US") + " kg"; | |
| const esc = s => String(s).replace(/[&<>"']/g, c => ({"&":"&","<":"<",">":">",'"':""","'":"'"}[c])); | |
| const nowStr = () => new Date().toISOString().slice(0,16).replace("T"," "); | |
| function toast(msg, type="ok"){ | |
| const el = document.createElement("div"); | |
| el.className = "toast " + type; el.textContent = msg; | |
| $("#toasts").appendChild(el); | |
| setTimeout(()=>{ el.style.opacity="0"; el.style.transform="translateY(6px)"; setTimeout(()=>el.remove(),250); }, 3600); | |
| } | |
| function openModal(html){ $("#mbCard").innerHTML = html; $("#mb").classList.remove("hidden"); } | |
| function closeModal(){ $("#mb").classList.add("hidden"); $("#mbCard").innerHTML=""; } | |
| function audit(act){ DB.audit.unshift({ at:nowStr(), who: state.staffEmail || "system", act }); if(state.view==="staff" && state.staffTab==="audit") renderStaff(); } | |
| function findCust(code){ return DB.customers.find(c=>c.code===code); } | |
| function custName(code){ const c = findCust(code); return c ? c.name : code; } | |
| function badgeFor(st){ return `<span class="st ${STAGES[st].k}">${ST_LABEL[STAGES[st].k]}</span>`; } | |
| function nextStage(st){ return st < STAGES.length-1 ? st+1 : st; } | |
| const state = { view:"home", tab:"shipments", staffTab:"overview", user:null, staffEmail:null, role:null, | |
| pendEmail:null, pendName:null, pendMode:"login", stPendEmail:null, stPendRole:null, | |
| trkFilter:"", stFilter:"all", resendLeft:0, resendTimer:null }; | |
| /* ===== ٤) ڕاوتن ===== */ | |
| function go(v){ | |
| state.view = v; | |
| ["home","track","customer","staff"].forEach(x=>{ | |
| const el = $("#view-"+x); if(el) el.classList.toggle("hidden", x!==v && !(v==="track" && x==="track")); | |
| }); | |
| $("#view-track").classList.add("hidden"); | |
| document.querySelectorAll("#navLinks button").forEach(b=>b.classList.toggle("on", b.dataset.go===v)); | |
| window.scrollTo({top:0}); | |
| } | |
| document.addEventListener("click", e=>{ | |
| const b = e.target.closest("[data-go]"); if(!b) return; | |
| const t = b.dataset.go; | |
| if(t==="track"){ go("home"); setTimeout(()=>{ $("#track").scrollIntoView({behavior:"smooth",block:"center"}); $("#trkInput").focus(); },60); return; } | |
| go(t); | |
| }); | |
| (function(){ | |
| const btn = $("#themeBtn"), root = document.documentElement; | |
| const set = m => { root.dataset.theme = m; btn.textContent = m==="dark" ? "🌙" : "☀️"; }; | |
| btn.addEventListener("click", ()=> set(root.dataset.theme==="dark"?"light":"dark")); | |
| set("dark"); | |
| })(); | |
| (function(){ | |
| const b = $("#modeBadge"); | |
| if(LIVE){ b.className="badge live"; b.textContent="Supabase · پەیوەست"; } | |
| else { b.className="badge demo"; b.textContent="Demo Mode"; b.title="URL و anon key لە CONFIG دابنێ"; } | |
| $("#footNote").textContent = LIVE ? "پەیوەست بە Supabase — داتاکان بە RLS پارێزراون." | |
| : "وەشانی نموونە (Demo): داتاکان لە بیرگەدا هەڵدەگیرێن و بە refresh دەسڕێنەوە. بۆ بەکارهێنانی ڕاستەقینە SUPABASE_URL و SUPABASE_ANON_KEY لە CONFIG دابنێ."; | |
| })(); | |
| /* ===== ٥) خزمەتگوزارییەکان ===== */ | |
| const SERVICES = [ | |
| { ic:"✈️", t:"Air Freight", d:"بۆ بارە پەلەکان و time-sensitive." }, | |
| { ic:"🚢", t:"Sea Freight", d:"بۆ بارە قورس و کۆکراوەکان." }, | |
| { ic:"🚛", t:"Land Freight", d:"گواستنەوەی وشکانی بۆ عێراق." }, | |
| { ic:"📑", t:"Customs", d:"ڕێکخستن و بەڕێوەبردنی بەڵگە." }, | |
| { ic:"🏬", t:"Warehousing", d:"وەرگرتن، QC و پاشەکەوتنی بەڵگە." }, | |
| { ic:"🚪", t:"Door-to-Door", d:"گەیاندن تا دەرگای کڕیار." } | |
| ]; | |
| $("#svcGrid").innerHTML = SERVICES.map(s=>`<div class="card"><div class="feat"><div class="ic">${s.ic}</div><div> | |
| <h3 style="margin-bottom:2px">${s.t}</h3><p class="muted" style="margin:0;font-size:.9rem">${s.d}</p></div></div></div>`).join(""); | |
| /* ===== ٦) Tracking ===== */ | |
| function timeline(st, updated){ | |
| return `<ul class="tl">${STAGES.map((s,i)=>`<li class="${i<st?"done":i===st?"now":""}"> | |
| <span class="dot">${i<st?"✓":i+1}</span> | |
| <span class="tt"><b>${s.t}</b><small>${s.d}${i===st?" · نوێترین: "+updated:""}</small></span></li>`).join("")}</ul>`; | |
| } | |
| function doTrack(){ | |
| const q = $("#trkInput").value.trim().toUpperCase(); const out = $("#trkOut"); | |
| if(!q){ out.innerHTML = `<p class="err">تکایە ژمارەی بار بنووسە.</p>`; return; } | |
| out.innerHTML = `<div class="skel" style="width:60%"></div><div class="skel" style="width:35%;margin-top:8px"></div>`; | |
| setTimeout(()=>{ | |
| const s = DB.shipments.find(x=>x.code.toUpperCase()===q); | |
| if(!s){ | |
| out.innerHTML = `<div class="card" style="background:var(--card2)"><h3>❓ هیچ بارێک بەم ژمارەیە نەدۆزرایەوە</h3> | |
| <p class="muted">ژمارەکە بپشکنەوە، یان پەیوەندیمان پێوە بکە. نموونەی کارا: <code>GC-2026-1042</code></p> | |
| <a class="btn primary" href="https://wa.me/${CONFIG.SUPPORT_WHATSAPP}">پەیوەندی بە WhatsApp</a></div>`; | |
| toast("بار نەدۆزرایەوە","warn"); return; | |
| } | |
| out.innerHTML = `<div class="card" style="background:var(--card2)"> | |
| <div class="spread"><div><p class="eyebrow">SHIPMENT</p><h3 style="margin:0">${s.code}</h3> | |
| <p class="muted" style="margin:0;font-size:.88rem">${esc(s.from)} → ${esc(s.to)} · ${s.mode} Freight</p></div> | |
| <div style="text-align:left">${badgeFor(s.st)}<div class="muted" style="font-size:.8rem;margin-top:6px">ETA: ${s.eta}</div></div></div> | |
| <div class="grid g4" style="margin-top:14px"> | |
| <div class="stat"><b>${kgf(s.kg)}</b><small>کێش</small></div> | |
| <div class="stat"><b>${money(s.usd)}</b><small>نرخی کۆیی</small></div> | |
| <div class="stat"><b>${s.mode}</b><small>جۆری گواستنەوە</small></div> | |
| <div class="stat"><b>${5-s.st}/5</b><small>هەنگاوی ماوە</small></div></div> | |
| <h4 style="margin-top:18px">Timeline</h4>${timeline(s.st, s.updated)} | |
| <p class="muted" style="font-size:.82rem">بۆ بینینی invoice و بەڵگەکان بچۆ ژوورەوە بۆ <a href="#" data-go="customer">هەژماری کڕیار</a>.</p></div>`; | |
| toast("زانیاری بار بارکرا","ok"); | |
| }, 420); | |
| } | |
| $("#trkBtn").addEventListener("click", doTrack); | |
| $("#trkInput").addEventListener("keydown", e=>{ if(e.key==="Enter") doTrack(); }); | |
| /* ===== ٧) کڕیار ===== */ | |
| function startResend(){ | |
| state.resendLeft = CONFIG.OTP_RESEND_SECONDS; | |
| const btn = $("#cuResend"); btn.disabled = true; | |
| clearInterval(state.resendTimer); | |
| state.resendTimer = setInterval(()=>{ | |
| state.resendLeft--; | |
| btn.textContent = state.resendLeft>0 ? `دووبارە ناردن (${state.resendLeft})` : "دووبارە ناردن"; | |
| if(state.resendLeft<=0){ clearInterval(state.resendTimer); btn.disabled=false; btn.textContent="دووبارە ناردن"; } | |
| },1000); | |
| } | |
| function custGoOtp(mode){ | |
| const email = $("#cuEmail").value.trim(), pass = $("#cuPass").value, err = $("#cuErr"); err.textContent = ""; | |
| if(!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)){ err.textContent="ئیمەیلی دروست بنووسە."; return; } | |
| if(pass.length < 6){ err.textContent="وشەی نهێنی دەبێت لانیکەم ٦ پیت بێت."; return; } | |
| state.pendEmail = email; state.pendMode = mode; state.pendName = email.split("@")[0]; | |
| $("#cuOtpMail").textContent = email; | |
| $("#custForm").classList.add("hidden"); $("#custOtp").classList.remove("hidden"); | |
| $("#custAuthTitle").textContent = mode==="signup" ? "دروستکردنی هەژمار" : "پشتڕاستکردنەوە"; | |
| $("#custStep").textContent = "2 / 2"; startResend(); | |
| toast("کۆدی پشتڕاستکردنەوە نێردرا","ok"); | |
| setTimeout(()=>$("#cuCode").focus(),100); | |
| } | |
| $("#cuLogin").addEventListener("click", ()=>custGoOtp("login")); | |
| $("#cuSignup").addEventListener("click", ()=>custGoOtp("signup")); | |
| $("#cuBack").addEventListener("click", ()=>{ | |
| $("#custForm").classList.remove("hidden"); $("#custOtp").classList.add("hidden"); | |
| $("#custAuthTitle").textContent="چوونەژوورەوە"; $("#custStep").textContent="1 / 2"; | |
| clearInterval(state.resendTimer); $("#cuResend").disabled=false; $("#cuResend").textContent="دووبارە ناردن"; | |
| }); | |
| $("#cuResend").addEventListener("click", ()=>{ startResend(); toast("کۆد دووبارە نێردرا","ok"); }); | |
| $("#cuCode").addEventListener("input", e=>{ e.target.value = e.target.value.replace(/\D/g,"").slice(0,6); }); | |
| $("#cuVerify").addEventListener("click", async ()=>{ | |
| const code = $("#cuCode").value.trim(), err = $("#cuOtpErr"); err.textContent=""; | |
| if(code.length!==6){ err.textContent="کۆد دەبێت ٦ ژمارە بێت."; return; } | |
| $("#cuVerify").disabled = true; $("#cuVerify").textContent = "پشتڕاستکردنەوە..."; | |
| if(LIVE && supabase){ | |
| try{ | |
| if(state.pendMode==="signup"){ const { error } = await supabase.auth.signUp({ email:state.pendEmail, password:$("#cuPass").value }); if(error) throw error; } | |
| const { error } = await supabase.auth.verifyOtp({ email:state.pendEmail, token:code, type:"email" }); if(error) throw error; | |
| }catch(ex){ err.textContent = "کۆدەکە دروست نییە یان بەسەرچووە. " + (ex.message||""); | |
| $("#cuVerify").disabled=false; $("#cuVerify").textContent="پشتڕاستکردنەوە"; return; } | |
| } else if(code !== CONFIG.OTP_DEMO_CODE){ | |
| err.textContent = "کۆدی نموونە 123456 ـە."; $("#cuVerify").disabled=false; $("#cuVerify").textContent="پشتڕاستکردنەوە"; return; | |
| } | |
| state.user = { email:state.pendEmail, name:state.pendName }; | |
| $("#cuName").textContent = state.user.name; | |
| $("#custAuth").classList.add("hidden"); $("#custApp").classList.remove("hidden"); | |
| $("#navCta").textContent = "هەژمارەکەم"; | |
| $("#cuVerify").disabled=false; $("#cuVerify").textContent="پشتڕاستکردنەوە"; clearInterval(state.resendTimer); | |
| state.tab = "shipments"; renderCustomer(); toast("بەخێربێی " + state.user.name + " 👋","ok"); | |
| }); | |
| $("#cuLogout").addEventListener("click", ()=>{ | |
| state.user=null; $("#custApp").classList.add("hidden"); $("#custAuth").classList.remove("hidden"); | |
| $("#custForm").classList.remove("hidden"); $("#custOtp").classList.add("hidden"); | |
| $("#custAuthTitle").textContent="چوونەژوورەوە"; $("#custStep").textContent="1 / 2"; | |
| $("#cuPass").value=""; $("#cuCode").value=""; $("#navCta").textContent="چوونەژوورەوە"; | |
| toast("چوویتەدەرەوە","warn"); | |
| }); | |
| $("#cuHint").innerHTML = LIVE ? "پەیوەست بە Supabase Auth — OTP ی ئیمەیل چالاکە." | |
| : `وەشانی نموونە: هەر ئیمەیلێکی دروست + وشەی نهێنی ≥٦ پیت، و کۆدی <code>${CONFIG.OTP_DEMO_CODE}</code>.`; | |
| const CU_TABS = [["shipments","📦 بارەکان"],["invoices","💳 پارەدان"],["docs","📄 بەڵگەکان"],["notify","🔔 ئاگاداری"]]; | |
| function findCustByEmail(email){ | |
| const map = { "customer@globall.iq":"C-1001", "aras@globall.iq":"C-1001", "zedan@globall.iq":"C-1002", "hawsaz@globall.iq":"C-1003" }; | |
| return findCust(map[email] || "C-1001"); | |
| } | |
| function myShips(){ const c = state.user ? (findCustByEmail(state.user.email)||{}).code : null; return c ? DB.shipments.filter(s=>s.cust===c) : DB.shipments.slice(0,3); } | |
| function renderCustomer(){ | |
| const ships = myShips(); const total = ships.reduce((a,s)=>a+s.usd,0); | |
| $("#cuRoleBadge").textContent = "CUSTOMER"; | |
| $("#cuStats").innerHTML = ` | |
| <div class="stat"><b>${ships.length}</b><small>بارە چالاکەکان</small></div> | |
| <div class="stat"><b>${ships.filter(s=>s.st===4).length}</b><small>گەیەندرا</small></div> | |
| <div class="stat"><b>${money(total)}</b><small>کۆی بەها</small></div> | |
| <div class="stat"><b>${DB.docs.filter(d=>ships.some(s=>s.code===d.ship)).length}</b><small>بەڵگە</small></div>`; | |
| $("#cuTabs").innerHTML = CU_TABS.map(([k,t])=>`<button data-tab="${k}" class="${state.tab===k?"on":""}">${t}</button>`).join(""); | |
| $("#cuTabs").querySelectorAll("button").forEach(b=>b.addEventListener("click",()=>{ state.tab=b.dataset.tab; renderCustomer(); })); | |
| const body = $("#cuBody"); | |
| if(state.tab==="shipments"){ | |
| body.innerHTML = `<div class="tw"><table><thead><tr><th>ژمارە</th><th>ڕێگا</th><th>جۆر</th><th>کێش</th><th>نرخ</th><th>دۆخ</th><th>ETA</th><th></th></tr></thead><tbody> | |
| ${ships.map((s,i)=>`<tr><td><b>${s.code}</b></td><td>${esc(s.from)} → ${esc(s.to)}</td><td>${s.mode}</td> | |
| <td>${kgf(s.kg)}</td><td>${money(s.usd)}</td><td>${badgeFor(s.st)}</td><td>${s.eta}</td> | |
| <td><button class="btn sm" data-detail="${i}">وردەکاری</button></td></tr>`).join("")}</tbody></table></div>`; | |
| body.querySelectorAll("[data-detail]").forEach(b=>b.addEventListener("click",()=>{ | |
| const s = ships[+b.dataset.detail]; | |
| openModal(`<div class="spread"><h3 style="margin:0">${s.code}</h3>${badgeFor(s.st)}</div> | |
| <p class="muted">${esc(s.from)} → ${esc(s.to)} · ${s.mode} · ${kgf(s.kg)}</p>${timeline(s.st, s.updated)} | |
| <div class="row" style="margin-top:14px"><button class="btn" onclick="closeModal()">داخستن</button> | |
| <a class="btn primary" href="https://wa.me/${CONFIG.SUPPORT_WHATSAPP}">پرسیار لە پشتیوانی</a></div>`); | |
| })); | |
| } | |
| if(state.tab==="invoices"){ | |
| body.innerHTML = `<div class="tw"><table><thead><tr><th>ژمارەی پسووڵە</th><th>بەروار</th><th>بڕ</th><th>دۆخ</th></tr></thead><tbody> | |
| ${DB.invoices.map(i=>`<tr><td><b>${i.no}</b></td><td>${i.date}</td><td>${money(i.usd)}</td> | |
| <td><span class="badge ${i.status==="پارەدراوە"?"live":""}">${i.status}</span></td></tr>`).join("")}</tbody></table></div> | |
| <p class="muted" style="margin-top:10px;font-size:.86rem">پارەدان بە WhatsApp یان حەواڵەی بانکی — پشتڕاستکردنەوە لەلایەن ژمێریار دەکرێت.</p>`; | |
| } | |
| if(state.tab==="docs"){ | |
| body.innerHTML = `<div class="tw"><table><thead><tr><th>ناوی فایل</th><th>بار</th><th>بەروار</th><th>قەبارە</th></tr></thead><tbody> | |
| ${DB.docs.map(d=>`<tr><td>📄 ${esc(d.name)}</td><td>${d.ship}</td><td>${d.date}</td><td>${d.size}</td></tr>`).join("")}</tbody></table></div> | |
| <p class="muted" style="margin-top:10px;font-size:.86rem">لە وەشانی ڕاستەقینە، لینکی پارێزراو بە Supabase Storage دەدرێت.</p>`; | |
| } | |
| if(state.tab==="notify"){ | |
| body.innerHTML = `<ul class="tl"> | |
| <li class="now"><span class="dot">🔔</span><span class="tt"><b>GC-2026-1038 گەیشتە گومرگ</b><small>ئەمڕۆ · پێویستە بەڵگەی ناسنامە بنێریت</small></span></li> | |
| <li class="done"><span class="dot">✓</span><span class="tt"><b>Invoice INV-2026-331 پارەدرا</b><small>2026-09-15</small></span></li> | |
| <li class="done"><span class="dot">✓</span><span class="tt"><b>GC-2026-1035 گەیەندرا</b><small>2026-09-18</small></span></li></ul> | |
| <div class="hint">چالاککردنی ئاگاداری بە WhatsApp و Email — لە وەشانی ڕاستەقینە بە Edge Function دەنێردرێت.</div>`; | |
| } | |
| } | |
| /* ===== ٨) ستاف + 2FA ===== */ | |
| $("#stLogin").addEventListener("click", async ()=>{ | |
| const email = $("#stEmail").value.trim().toLowerCase(), pass = $("#stPass").value, err = $("#stErr"); err.textContent = ""; | |
| const rec = DB.staff.find(s=>s.email.toLowerCase()===email); | |
| if(!rec){ err.textContent = "ئەم ئیمەیلە ئەندامی ستاف نییە."; return; } | |
| if(pass.length < 6){ err.textContent = "وشەی نهێنی دەبێت لانیکەم ٦ پیت بێت."; return; } | |
| if(LIVE && supabase){ | |
| $("#stLogin").disabled = true; $("#stLogin").textContent = "چاوەڕوان بە..."; | |
| const { error } = await supabase.auth.signInWithPassword({ email, password:pass }); | |
| $("#stLogin").disabled = false; $("#stLogin").textContent = "چوونەژوورەوە"; | |
| if(error){ err.textContent = "چوونەژوورەوە سەرکەوتوو نەبوو: " + error.message; return; } | |
| } | |
| state.stPendEmail = email; state.stPendRole = rec.role; | |
| $("#staffForm").classList.add("hidden"); $("#staffOtp").classList.remove("hidden"); | |
| $("#staffStep").textContent = "2 / 2"; $("#staffAuthTitle").textContent = "2FA"; | |
| toast("کۆدی 2FA بنووسە","ok"); setTimeout(()=>$("#stCode").focus(),100); | |
| }); | |
| $("#stBack").addEventListener("click", ()=>{ | |
| $("#staffForm").classList.remove("hidden"); $("#staffOtp").classList.add("hidden"); | |
| $("#staffStep").textContent="1 / 2"; $("#staffAuthTitle").textContent="کۆنسۆڵی بەڕێوەبردن"; | |
| $("#stCode").value=""; $("#stOtpErr").textContent=""; | |
| }); | |
| $("#stCode").addEventListener("input", e=>{ e.target.value = e.target.value.replace(/\D/g,"").slice(0,6); }); | |
| $("#stVerify").addEventListener("click", ()=>{ | |
| const code = $("#stCode").value.trim(), err = $("#stOtpErr"); err.textContent=""; | |
| if(code.length!==6){ err.textContent="کۆد دەبێت ٦ ژمارە بێت."; return; } | |
| if(code !== CONFIG.OTP_DEMO_CODE && !LIVE){ err.textContent = "کۆدی نموونە 123456 ـە."; return; } | |
| state.staffEmail = state.stPendEmail; state.role = state.stPendRole; | |
| $("#staffAuth").classList.add("hidden"); $("#staffApp").classList.remove("hidden"); | |
| $("#stWho").textContent = (DB.staff.find(s=>s.email===state.staffEmail)||{}).name || state.staffEmail; | |
| $("#stRoleBadge").textContent = state.staffEmail; | |
| state.staffTab = (PERMS[state.role]||[]).includes("overview") ? "overview" : PERMS[state.role][0]; | |
| renderStaff(); audit("چوونەژوورەوە بە 2FA"); toast("چوویتە ژوورەوە · " + ROLES[state.role],"ok"); | |
| }); | |
| $("#stLogout").addEventListener("click", ()=>{ | |
| state.staffEmail=null; state.role=null; | |
| $("#staffApp").classList.add("hidden"); $("#staffAuth").classList.remove("hidden"); | |
| $("#staffForm").classList.remove("hidden"); $("#staffOtp").classList.add("hidden"); | |
| $("#staffStep").textContent="1 / 2"; $("#staffAuthTitle").textContent="کۆنسۆڵی بەڕێوەبردن"; | |
| $("#stPass").value=""; $("#stCode").value=""; toast("چوویتەدەرەوە","warn"); | |
| }); | |
| $("#stHint").innerHTML = LIVE ? "پەیوەست بە Supabase Auth — 2FA بە TOTP چالاکە." | |
| : `وەشانی نموونە: <code>admin@globall.iq</code> (سوپەر ئەدمین) · <code>ops@globall.iq</code> (ئەدمین) · <code>finance@globall.iq</code> (ژمێریار) — هەر کام بە وشەی نهێنی ≥٦ پیت و کۆدی <code>${CONFIG.OTP_DEMO_CODE}</code>.`; | |
| /* ===== ٩) کۆنسۆڵ ===== */ | |
| function renderStaff(){ | |
| const allow = PERMS[state.role] || []; const ships = DB.shipments; | |
| $("#stStats").innerHTML = ` | |
| <div class="stat"><b>${ships.length}</b><small>کۆی بارەکان</small></div> | |
| <div class="stat"><b>${ships.filter(s=>s.st<4).length}</b><small>لە ڕێگادا</small></div> | |
| <div class="stat"><b>${DB.customers.length}</b><small>موشتەری</small></div> | |
| <div class="stat"><b>${money(DB.invoices.reduce((a,i)=>a+i.usd,0))}</b><small>کۆی پسووڵەکان</small></div>`; | |
| $("#stTabs").innerHTML = allow.map(k=>`<button data-tab="${k}" class="${state.staffTab===k?"on":""}">${TABS[k]}</button>`).join(""); | |
| $("#stTabs").querySelectorAll("button").forEach(b=>b.addEventListener("click",()=>{ state.staffTab=b.dataset.tab; renderStaff(); })); | |
| const body = $("#stBody"); const t = state.staffTab; | |
| if(t==="overview"){ | |
| const late = DB.invoices.filter(i=>i.status==="دواکەوتوو"); | |
| body.innerHTML = `<div class="grid g2"> | |
| <div class="card"><h3>دۆخی ئۆپەراسیۆن</h3> | |
| ${STAGES.map((s,i)=>{ const n = ships.filter(x=>x.st===i).length; return `<div class="kv"><span>${s.t}</span><b>${n}</b></div>`; }).join("")}</div> | |
| <div class="card"><h3>ئاگادارییەکان</h3> | |
| ${late.length ? late.map(i=>`<div class="kv"><span>پسووڵەی دواکەوتوو ${i.no} · ${custName(i.cust)}</span><b>${money(i.usd)}</b></div>`).join("") : `<p class="muted">هیچ دواکەوتوویەک نییە ✅</p>`} | |
| <div class="kv"><span>ڕۆڵی تۆ</span><b>${ROLES[state.role]}</b></div></div></div>`; | |
| } | |
| if(t==="shipments"){ | |
| const list = ships.filter(s => state.stFilter==="all" || STAGES[s.st].k===state.stFilter) | |
| .filter(s => !state.trkFilter || (s.code+s.name+s.from+s.to).toLowerCase().includes(state.trkFilter)); | |
| const canEdit = state.role!=="accountant"; | |
| body.innerHTML = `<div class="spread" style="margin-bottom:12px"> | |
| <div class="row"> | |
| <input id="stSearch" placeholder="گەڕان بە ژمارە یان ناو..." style="min-width:220px" value="${esc(state.trkFilter)}"> | |
| <select id="stFilter" style="min-width:170px"> | |
| <option value="all">هەموو دۆخەکان</option> | |
| ${STAGES.map(s=>`<option value="${s.k}" ${state.stFilter===s.k?"selected":""}>${s.t}</option>`).join("")}</select></div> | |
| <div class="row">${canEdit?`<button class="btn primary sm" id="addShip">+ بارێکی نوێ</button>`:`<span class="badge">تەنها خوێندنەوە</span>`}</div></div> | |
| <div class="tw"><table><thead><tr><th>ژمارە</th><th>موشتەری</th><th>ڕێگا</th><th>جۆر</th><th>کێش</th><th>نرخ</th><th>دۆخ</th><th>ETA</th><th>کردار</th></tr></thead><tbody> | |
| ${list.length? list.map(s=>`<tr><td><b>${s.code}</b></td><td>${esc(s.name)}</td><td>${esc(s.from)} → ${esc(s.to)}</td><td>${s.mode}</td> | |
| <td>${kgf(s.kg)}</td><td>${money(s.usd)}</td><td>${badgeFor(s.st)}</td><td>${s.eta}</td> | |
| <td>${canEdit && s.st<4 ? `<button class="btn sm ok" data-next="${s.code}">هەنگاوی داهاتوو</button>` : `<span class="muted" style="font-size:.8rem">تەواو</span>`}</td></tr>`).join("") | |
| : `<tr><td colspan="9" class="muted" style="text-align:center">هیچ ئەنجامێک نەدۆزرایەوە</td></tr>`}</tbody></table></div>`; | |
| const si = $("#stSearch"); if(si) si.addEventListener("input", e=>{ state.trkFilter = e.target.value.trim().toLowerCase(); renderStaff(); $("#stSearch").focus(); }); | |
| const sf = $("#stFilter"); if(sf) sf.addEventListener("change", e=>{ state.stFilter = e.target.value; renderStaff(); }); | |
| body.querySelectorAll("[data-next]").forEach(b=>b.addEventListener("click",()=>{ | |
| const s = DB.shipments.find(x=>x.code===b.dataset.next); | |
| s.st = nextStage(s.st); s.updated = nowStr(); | |
| audit(`گۆڕینی دۆخ بۆ ${s.code} → ${ST_LABEL[STAGES[s.st].k]}`); | |
| renderStaff(); toast(`${s.code} → ${ST_LABEL[STAGES[s.st].k]}`,"ok"); | |
| })); | |
| const as = $("#addShip"); if(as) as.addEventListener("click", ()=>shipModal()); | |
| } | |
| if(t==="customers"){ | |
| body.innerHTML = `<div class="spread" style="margin-bottom:12px"> | |
| <p class="muted" style="margin:0">کۆگای موشتەریان — ${DB.customers.length} تۆمار</p> | |
| <button class="btn primary sm" id="addCust">+ زیادکردنی موشتەری</button></div> | |
| <div class="tw"><table><thead><tr><th>کۆد</th><th>ناو</th><th>مۆبایل ١</th><th>مۆبایل ٢</th><th>شار</th><th>شوێنی ناردن</th><th>تێبینی</th><th>کردار</th></tr></thead><tbody> | |
| ${DB.customers.map(c=>`<tr><td><b>${c.code}</b></td><td>${esc(c.name)}</td><td dir="ltr">${esc(c.p1)}</td><td dir="ltr">${esc(c.p2||"—")}</td> | |
| <td>${esc(c.city)}</td><td>${esc(c.addr)}</td><td class="muted">${esc(c.note||"—")}</td> | |
| <td><button class="btn sm" data-edit="${c.code}">دەستکاری</button> | |
| ${state.role==="super_admin"?`<button class="btn sm danger" data-del="${c.code}">سڕینەوە</button>`:""}</td></tr>`).join("")}</tbody></table></div>`; | |
| $("#addCust").addEventListener("click", ()=>custModal()); | |
| body.querySelectorAll("[data-edit]").forEach(b=>b.addEventListener("click",()=>custModal(b.dataset.edit))); | |
| body.querySelectorAll("[data-del]").forEach(b=>b.addEventListener("click",()=>{ | |
| const c = findCust(b.dataset.del); | |
| openModal(`<h3>سڕینەوەی موشتەری</h3> | |
| <p>دڵنیایت لە سڕینەوەی <b>${esc(c.name)}</b>؟ ئەم کردارە گەڕانەوەی نییە، بەڵام بارەکانی پێشووی دەمێننەوە.</p> | |
| <div class="row" style="margin-top:14px"><button class="btn danger" id="yesDel">بەڵێ، بیسڕەوە</button> | |
| <button class="btn" onclick="closeModal()">پاشگەزبوونەوە</button></div>`); | |
| $("#yesDel").addEventListener("click", ()=>{ | |
| DB.customers = DB.customers.filter(x=>x.code!==c.code); | |
| audit(`سڕینەوەی موشتەری ${c.code}`); closeModal(); renderStaff(); toast("موشتەری سڕایەوە","warn"); | |
| }); | |
| })); | |
| } | |
| if(t==="staff"){ | |
| body.innerHTML = `<div class="spread" style="margin-bottom:12px"> | |
| <p class="muted" style="margin:0">ڕۆڵەکان: Super Admin (هەموو)، Admin (بار و موشتەری)، Accountant (دارایی).</p> | |
| <button class="btn primary sm" id="addStaff">+ کارمەندی نوێ</button></div> | |
| <div class="tw"><table><thead><tr><th>ID</th><th>ناو</th><th>ئیمەیل</th><th>ڕۆڵ</th><th>دۆخ</th><th>کردار</th></tr></thead><tbody> | |
| ${DB.staff.map(s=>`<tr><td>${s.id}</td><td>${esc(s.name)}</td><td dir="ltr">${esc(s.email)}</td> | |
| <td><span class="badge">${ROLES[s.role]}</span></td> | |
| <td><span class="badge ${s.active?"live":""}">${s.active?"چالاک":"ناچالاک"}</span></td> | |
| <td>${s.email===state.staffEmail?`<span class="muted" style="font-size:.8rem">خۆت</span>` | |
| :`<button class="btn sm danger" data-rm="${s.id}">سڕینەوە</button>`}</td></tr>`).join("")}</tbody></table></div>`; | |
| $("#addStaff").addEventListener("click", ()=>{ | |
| openModal(`<h3>زیادکردنی کارمەند</h3> | |
| <label class="fl">ناو</label><input id="nsName" placeholder="ناوی کارمەند"> | |
| <label class="fl">ئیمەیل</label><input id="nsMail" type="email" placeholder="name@globall.iq"> | |
| <label class="fl">ڕۆڵ</label><select id="nsRole">${Object.entries(ROLES).map(([k,v])=>`<option value="${k}">${v}</option>`).join("")}</select> | |
| <div class="row" style="margin-top:16px"><button class="btn primary" id="nsSave">پاشەکەوتکردن</button> | |
| <button class="btn" onclick="closeModal()">پاشگەزبوونەوە</button></div>`); | |
| $("#nsSave").addEventListener("click", ()=>{ | |
| const n = $("#nsName").value.trim(), e = $("#nsMail").value.trim().toLowerCase(), r = $("#nsRole").value; | |
| if(!n || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(e)){ toast("ناو و ئیمەیلی دروست پێویستە","bad"); return; } | |
| if(DB.staff.some(s=>s.email.toLowerCase()===e)){ toast("ئەم ئیمەیلە پێشتر هەیە","bad"); return; } | |
| DB.staff.push({ id:"S-"+(DB.staff.length+1), name:n, email:e, role:r, active:true }); | |
| audit(`دروستکردنی هەژماری کارمەند ${e} (${ROLES[r]})`); closeModal(); renderStaff(); toast("کارمەند زیادکرا","ok"); | |
| }); | |
| }); | |
| body.querySelectorAll("[data-rm]").forEach(b=>b.addEventListener("click",()=>{ | |
| const s = DB.staff.find(x=>x.id===b.dataset.rm); | |
| DB.staff = DB.staff.filter(x=>x.id!==s.id); | |
| audit(`سڕینەوەی کارمەند ${s.email}`); renderStaff(); toast("کارمەند سڕایەوە","warn"); | |
| })); | |
| } | |
| if(t==="finance"){ | |
| const paid = DB.invoices.filter(i=>i.status==="پارەدراوە").reduce((a,i)=>a+i.usd,0); | |
| const open = DB.invoices.filter(i=>i.status==="کراوە").reduce((a,i)=>a+i.usd,0); | |
| const late = DB.invoices.filter(i=>i.status==="دواکەوتوو").reduce((a,i)=>a+i.usd,0); | |
| body.innerHTML = `<div class="grid g3" style="margin-bottom:14px"> | |
| <div class="stat"><b>${money(paid)}</b><small>وەرگیراو</small></div> | |
| <div class="stat"><b>${money(open)}</b><small>کراوە</small></div> | |
| <div class="stat"><b>${money(late)}</b><small>دواکەوتوو</small></div></div> | |
| <div class="tw"><table><thead><tr><th>پسووڵە</th><th>موشتەری</th><th>بەروار</th><th>بڕ</th><th>دۆخ</th><th>کردار</th></tr></thead><tbody> | |
| ${DB.invoices.map(i=>`<tr><td><b>${i.no}</b></td><td>${esc(custName(i.cust))}</td><td>${i.date}</td><td>${money(i.usd)}</td> | |
| <td><span class="badge ${i.status==="پارەدراوە"?"live":i.status==="دواکەوتوو"?"demo":""}">${i.status}</span></td> | |
| <td>${i.status!=="پارەدراوە"?`<button class="btn sm ok" data-pay="${i.no}">تۆمارکردنی پارەدان</button>`:`<span class="muted" style="font-size:.8rem">—</span>`}</td></tr>`).join("")}</tbody></table></div>`; | |
| body.querySelectorAll("[data-pay]").forEach(b=>b.addEventListener("click",()=>{ | |
| const i = DB.invoices.find(x=>x.no===b.dataset.pay); i.status = "پارەدراوە"; | |
| audit(`تۆمارکردنی پارەدان بۆ ${i.no}`); renderStaff(); toast(`${i.no} پارەدرا`,"ok"); | |
| })); | |
| } | |
| if(t==="audit"){ | |
| body.innerHTML = `<div class="tw"><table><thead><tr><th>کات</th><th>بەکارهێنەر</th><th>کردار</th></tr></thead><tbody> | |
| ${DB.audit.map(a=>`<tr><td dir="ltr">${a.at}</td><td dir="ltr">${esc(a.who)}</td><td>${esc(a.act)}</td></tr>`).join("")}</tbody></table></div> | |
| <p class="muted" style="margin-top:10px;font-size:.86rem">Audit trail ی append-only — لە وەشانی ڕاستەقینە لە خشتەی <code>audit_log</code> لە Supabase تۆمار دەکرێت.</p>`; | |
| } | |
| if(t==="settings"){ | |
| body.innerHTML = `<div class="grid g2"> | |
| <div class="card"><h3>پەیوەستکردنی Supabase</h3> | |
| <p class="muted" style="font-size:.9rem">لە سەرەتای <code><script></code> ی فایلەکەدا:</p> | |
| <div class="kv"><span>SUPABASE_URL</span><code>${LIVE?CONFIG.SUPABASE_URL:"— بەتاڵ —"}</code></div> | |
| <div class="kv"><span>SUPABASE_ANON_KEY</span><code>${LIVE?"•••• (دانراوە)":"— بەتاڵ —"}</code></div> | |
| <div class="kv"><span>دۆخ</span><b>${LIVE?"پەیوەست ✅":"Demo ⚠️"}</b></div> | |
| <p class="muted" style="margin-top:12px;font-size:.86rem">تەنها <b>anon key</b> لە frontend دابنێ — هیچ کات <b>service_role</b> دانیە.</p></div> | |
| <div class="card"><h3>ئاسایش</h3> | |
| <div class="kv"><span>2FA بۆ ستاف</span><b>چالاک</b></div> | |
| <div class="kv"><span>Role-Based Access</span><b>چالاک</b></div> | |
| <div class="kv"><span>Audit Trail</span><b>چالاک</b></div> | |
| <div class="kv"><span>پەیوەندی TLS</span><b>چالاک</b></div> | |
| <div class="kv"><span>داخستنی خۆکار</span><b>پێشنیار: 5 هەوڵ / 15 خولەک</b></div></div></div>`; | |
| } | |
| } | |
| function custModal(code){ | |
| const c = code ? findCust(code) : { code:"", name:"", p1:"", p2:"", city:"هەولێر", addr:"", note:"" }; | |
| openModal(`<div class="spread"><h3 style="margin:0">${code?"دەستکاری موشتەری":"زیادکردنی موشتەری"}</h3>${code?`<span class="badge">${c.code}</span>`:""}</div> | |
| <div class="grid g2" style="margin-top:10px"> | |
| <div><label class="fl">ناو *</label><input id="cmName" value="${esc(c.name)}"></div> | |
| <div><label class="fl">شار</label><input id="cmCity" value="${esc(c.city)}"></div> | |
| <div><label class="fl">مۆبایل ١</label><input id="cmP1" dir="ltr" value="${esc(c.p1)}"></div> | |
| <div><label class="fl">مۆبایل ٢</label><input id="cmP2" dir="ltr" value="${esc(c.p2)}"></div> | |
| <div><label class="fl">شوێنی ناردن</label><input id="cmAddr" value="${esc(c.addr)}"></div> | |
| <div><label class="fl">تێبینی</label><input id="cmNote" value="${esc(c.note)}"></div></div> | |
| <div class="row" style="margin-top:16px"><button class="btn primary" id="cmSave">پاشەکەوتکردن</button> | |
| <button class="btn" onclick="closeModal()">پاشگەزبوونەوە</button></div>`); | |
| $("#cmSave").addEventListener("click", ()=>{ | |
| const name = $("#cmName").value.trim(); if(!name){ toast("ناو پێویستە","bad"); return; } | |
| const data = { name, city:$("#cmCity").value.trim(), p1:$("#cmP1").value.trim(), p2:$("#cmP2").value.trim(), | |
| addr:$("#cmAddr").value.trim(), note:$("#cmNote").value.trim() }; | |
| if(code){ Object.assign(findCust(code), data); audit(`دەستکاری موشتەری ${code}`); toast("نوێکرایەوە","ok"); } | |
| else { const n = "C-"+(1000+DB.customers.length+1); DB.customers.push({ code:n, ...data }); audit(`زیادکردنی موشتەری ${n}`); toast("موشتەری زیادکرا","ok"); } | |
| closeModal(); renderStaff(); | |
| }); | |
| } | |
| function shipModal(){ | |
| openModal(`<h3>بارێکی نوێ</h3> | |
| <label class="fl">موشتەری</label><select id="ns0">${DB.customers.map(c=>`<option value="${c.code}">${esc(c.name)}</option>`).join("")}</select> | |
| <div class="grid g2"> | |
| <div><label class="fl">لە</label><input id="nf" value="گوانگژۆ"></div> | |
| <div><label class="fl">بۆ</label><input id="nt" value="هەولێر"></div> | |
| <div><label class="fl">جۆر</label><select id="nm"><option>Air</option><option>Sea</option><option>Land</option></select></div> | |
| <div><label class="fl">کێش (kg)</label><input id="nk" type="number" value="100"></div> | |
| <div><label class="fl">نرخ (USD)</label><input id="np" type="number" value="800"></div> | |
| <div><label class="fl">ETA</label><input id="ne" type="date"></div></div> | |
| <div class="row" style="margin-top:16px"><button class="btn primary" id="nfSave">پاشەکەوتکردن</button> | |
| <button class="btn" onclick="closeModal()">پاشگەزبوونەوە</button></div>`); | |
| $("#nfSave").addEventListener("click", ()=>{ | |
| const cust = $("#ns0").value, c = findCust(cust); | |
| const code = "GC-2026-" + (1043 + DB.shipments.length); | |
| DB.shipments.unshift({ code, cust, name:c.name, mode:$("#nm").value, from:$("#nf").value, to:$("#nt").value, | |
| kg:+$("#nk").value||0, usd:+$("#np").value||0, st:0, eta:$("#ne").value || "—", updated:nowStr() }); | |
| audit(`زیادکردنی بار ${code}`); closeModal(); renderStaff(); toast("بار زیادکرا","ok"); | |
| }); | |
| } | |
| $("#mb").addEventListener("click", e=>{ if(e.target.id==="mb") closeModal(); }); | |
| document.addEventListener("keydown", e=>{ if(e.key==="Escape") closeModal(); }); | |
| go("home"); | |
| </script> | |
| </body> | |
| </html> | |
| <script data-huggingchat-badge="script"> | |
| (function () { | |
| try { | |
| if (document.querySelector("[data-huggingchat-badge]:not(script)")) return; | |
| var host = document.createElement("div"); | |
| host.setAttribute("data-huggingchat-badge", ""); | |
| var styles = [["all","initial"],["position","fixed"],["right","12px"],["bottom","12px"],["z-index","2147483647"],["display","block"],["visibility","visible"],["opacity","1"],["pointer-events","auto"],["width","auto"],["height","auto"],["margin","0"],["padding","0"],["max-width","none"],["max-height","none"],["transform","none"],["filter","none"],["clip-path","none"],["color-scheme","light"]]; | |
| for (var i = 0; i < styles.length; i++) { | |
| host.style.setProperty(styles[i][0], styles[i][1], "important"); | |
| } | |
| host.attachShadow({ mode: "closed" }).innerHTML = "<style>\n:host { all: initial; }\na {\n display: flex;\n align-items: center;\n gap: 5px;\n box-sizing: border-box;\n height: 26px;\n padding: 0 9px 0 7px;\n border-radius: 999px;\n border: 1px solid rgba(0, 0, 0, 0.07);\n background: rgba(255, 255, 255, 0.92);\n -webkit-backdrop-filter: saturate(180%) blur(8px);\n backdrop-filter: saturate(180%) blur(8px);\n box-shadow: 0 1px 2px rgba(0, 0, 0, 0.08), 0 6px 16px rgba(0, 0, 0, 0.06);\n color: #111827;\n font: 500 11px/1 ui-sans-serif, system-ui, -apple-system, \"Segoe UI\", sans-serif;\n text-decoration: none;\n white-space: nowrap;\n opacity: 0.72;\n transition: opacity 0.15s ease, transform 0.15s ease;\n}\na:hover, a:focus-visible { opacity: 1; transform: translateY(-1px); }\nsvg { width: 14px; height: 14px; flex: none; }\n@media print { a { display: none; } }\n<\/style><a href=\"https://huggingface.co/chat\" target=\"_blank\" rel=\"noopener noreferrer nofollow\"><svg viewBox=\"0 0 32 32\" fill=\"none\" aria-hidden=\"true\"><path d=\"M16.0006 25.9992C13.8266 25.999 11.7118 25.2901 9.97686 23.9799C8.2419 22.6698 6.98127 20.8298 6.38599 18.7388C5.79071 16.6478 5.89323 14.4198 6.678 12.3923C7.46278 10.3648 8.88705 8.64837 10.735 7.50308C12.5829 6.35779 14.7538 5.84606 16.9187 6.04544C19.0837 6.24481 21.1246 7.14442 22.7323 8.60795C24.34 10.0715 25.4268 12.0192 25.8281 14.1559C26.2293 16.2926 25.9232 18.5019 24.9561 20.449C24.7703 20.8042 24.7223 21.2155 24.8211 21.604L25.4211 23.8316C25.4803 24.0518 25.4805 24.2837 25.4216 24.5039C25.3627 24.7242 25.2468 24.925 25.0856 25.0862C24.9244 25.2474 24.7235 25.3633 24.5033 25.4222C24.283 25.4811 24.0512 25.4809 23.831 25.4217L21.6034 24.8217C21.2172 24.7248 20.809 24.7729 20.4558 24.9567C19.0683 25.6467 17.5457 26.0068 16.0006 26.0068V25.9992Z\" fill=\"currentColor\"/><path d=\"M9.62598 16.0013C9.62598 15.3799 10.1294 14.8765 10.7508 14.8765C11.3721 14.8765 11.8756 15.3799 11.8756 16.0013C11.8756 17.0953 12.3102 18.1448 13.0838 18.9184C13.8574 19.692 14.9069 20.1266 16.001 20.1267C17.095 20.1267 18.1445 19.692 18.9181 18.9184C19.6918 18.1448 20.1264 17.0953 20.1264 16.0013C20.1264 15.3799 20.6299 14.8765 21.2512 14.8765C21.8725 14.8765 22.3759 15.3799 22.3759 16.0013C22.3759 17.6921 21.7046 19.3137 20.509 20.5093C19.3134 21.7049 17.6918 22.3762 16.001 22.3762C14.3102 22.3762 12.6885 21.7049 11.4929 20.5093C10.2974 19.3137 9.62598 17.6921 9.62598 16.0013Z\" fill=\"#fff\"/><\/svg><span>Made with HuggingChat<\/span><\/a>"; | |
| function attach() { | |
| var parent = document.body || document.documentElement; | |
| if (parent && host.parentNode !== parent) parent.appendChild(host); | |
| } | |
| attach(); | |
| // Artifacts that rewrite document.body wholesale would drop the badge. | |
| if (document.body && typeof MutationObserver === "function") { | |
| new MutationObserver(attach).observe(document.body, { childList: true }); | |
| } else { | |
| document.addEventListener("DOMContentLoaded", attach); | |
| } | |
| } catch (e) {} | |
| })(); | |
| </script> | |