-
-
- Whitelisted Shops
-Shops listed here bypass subscription checks and get full app access.
+App Users
+All shops that have installed Data4Autos. Toggle free access directly from this list.
-
-
- ➕
-
-
- Grant Free Access
-Add a shop domain and optional expiry date.
-
-
-
-
-
-
+
+
+
'; return; }
+ wrap.innerHTML = ''; return; }
+ allUsers = r.data.users || [];
+ document.getElementById('shops-count').textContent = allUsers.length + ' user' + (allUsers.length !== 1 ? 's' : '');
+ renderUsers(allUsers);
+}
- const shops = r.data.shops || [];
- const countLabel = shops.length + ' shop' + (shops.length !== 1 ? 's' : '');
- document.getElementById('shops-count').textContent = countLabel;
- document.getElementById('shops-count-table').textContent = countLabel;
+function filterUsers() {
+ const q = document.getElementById('search-inp').value.toLowerCase();
+ renderUsers(q ? allUsers.filter(u => u.shop.toLowerCase().includes(q)) : allUsers);
+}
- if (shops.length === 0) {
- wrap.innerHTML = '';
+function renderUsers(users) {
+ const wrap = document.getElementById('shops-table-wrap');
+ document.getElementById('shops-count-table').textContent = users.length + ' shop' + (users.length !== 1 ? 's' : '');
+
+ if (users.length === 0) {
+ wrap.innerHTML = '';
return;
}
- const rows = shops.map(s => {
- const statusTag = s.expired
- ? 'Expired'
- : s.permanent
- ? 'Permanent'
- : 'Active';
-
- const expiryDisplay = s.expiresAt
- ? '' + new Date(s.expiresAt).toLocaleDateString(undefined,{year:'numeric',month:'short',day:'numeric'}) + ''
- : 'Never';
-
- const grantedDisplay = s.grantedAt
- ? '' + new Date(s.grantedAt).toLocaleDateString(undefined,{year:'numeric',month:'short',day:'numeric'}) + ''
+ const rows = users.map(u => {
+ const lastSeen = u.savedAt
+ ? new Date(u.savedAt).toLocaleDateString(undefined,{year:'numeric',month:'short',day:'numeric'})
: '—';
- return \`
-
+ let statusTag, actionBtn;
+ if (u.freeAccess) {
+ const fa = u.freeEntry;
+ const expLabel = fa?.expiresAt
+ ? 'Until ' + new Date(fa.expiresAt).toLocaleDateString(undefined,{month:'short',day:'numeric',year:'numeric'})
+ : 'Permanent';
+ statusTag = \`Free Access · \${expLabel}\`;
+ actionBtn = \`\`;
+ } else {
+ statusTag = 'Subscribed';
+ actionBtn = \`\`;
+ }
+
+ return \`
+
+
\${statusTag}
- \${grantedDisplay}
- \${expiryDisplay}
- \${escHtml(s.note || '—')}
-
+ \${escHtml(u.freeEntry?.note || '—')}
+ \${actionBtn}
\`;
}).join('');
wrap.innerHTML = \`
\`;
}
-// ── ADD SHOP ─────────────────────────────────────────────────────────────────
-async function doAddShop() {
- const shop = document.getElementById('inp-shop').value.trim();
- const expires = document.getElementById('inp-expires').value || null;
- const note = document.getElementById('inp-note').value.trim();
- const ok = document.getElementById('add-success');
- const err = document.getElementById('add-err');
- ok.style.display = 'none';
- err.style.display = 'none';
+// ── MODAL ────────────────────────────────────────────────────────────────────
+let _modalShop = null;
- if (!shop) { err.textContent = '⚠️ Shop domain is required.'; err.style.display = 'block'; return; }
-
- const expiresAt = expires ? new Date(expires + 'T23:59:59.000Z').toISOString() : null;
- const r = await api('POST', '/api/shops', { shop, expiresAt, note });
- if (r.ok) {
- ok.style.display = 'block';
- document.getElementById('inp-shop').value = '';
- document.getElementById('inp-expires').value = '';
- document.getElementById('inp-note').value = '';
- loadShops();
- } else {
- err.textContent = r.data.error || 'Failed to add shop';
- err.style.display = 'block';
- }
+function openModal(shop) {
+ _modalShop = shop;
+ document.getElementById('modal-shop-name').textContent = shop;
+ document.getElementById('modal-expires').value = '';
+ document.getElementById('modal-note').value = '';
+ const m = document.getElementById('expiry-modal');
+ m.style.display = 'flex';
}
-// ── REMOVE SHOP ───────────────────────────────────────────────────────────────
-async function doRemoveShop(shop) {
- if (!confirm('Remove free access for ' + shop + '?')) return;
+function closeModal() {
+ document.getElementById('expiry-modal').style.display = 'none';
+ _modalShop = null;
+}
+
+async function confirmGrant() {
+ if (!_modalShop) return;
+ const expires = document.getElementById('modal-expires').value || null;
+ const note = document.getElementById('modal-note').value.trim();
+ const expiresAt = expires ? new Date(expires + 'T23:59:59.000Z').toISOString() : null;
+ const r = await api('POST', '/api/shops', { shop: _modalShop, expiresAt, note });
+ if (r.ok) { closeModal(); loadUsers(); }
+ else alert('Failed to grant access: ' + (r.data.error || 'Unknown error'));
+}
+
+// ── REVOKE ───────────────────────────────────────────────────────────────────
+async function doRevoke(shop) {
+ if (!confirm('Revoke free access for ' + shop + '? They will need an active subscription.')) return;
const r = await api('DELETE', '/api/shops/' + encodeURIComponent(shop));
- if (r.ok) loadShops();
- else alert('Failed to remove shop');
+ if (r.ok) loadUsers();
+ else alert('Failed to revoke access');
}
// ── UTIL ──────────────────────────────────────────────────────────────────────
+
-
+
Grant Free Access
+
+
-
+
-
+
-
-
+
+
+
+
+
-
✅ Shop added successfully!
-
-
Active Whitelist
- 0 shops +Installed Shops
+
+
+ 0 shops
+
@@ -549,90 +571,109 @@ async function doLogout() {
function showDashboard() {
document.getElementById('login-screen').style.display = 'none';
document.getElementById('dashboard').style.display = 'block';
- loadShops();
+ loadUsers();
}
-// ── LOAD SHOPS ──────────────────────────────────────────────────────────────
-async function loadShops() {
+// ── ALL USERS DATA ───────────────────────────────────────────────────────────
+let allUsers = [];
+
+async function loadUsers() {
const wrap = document.getElementById('shops-table-wrap');
- const r = await api('GET', '/api/shops');
- if (!r.ok) { wrap.innerHTML = '
Failed to load shops
';
+ const r = await api('GET', '/api/users');
+ if (!r.ok) { wrap.innerHTML = '
Failed to load users.
🏪
No shops added yet.
Use the form above to grant free access to a shop.🏪
No shops found.
\${escHtml(s.shop)}
\${escHtml(u.shop)}
+ Last auth: \${lastSeen}
+ | Shop Domain | Status | Granted | Expires | Note | + | Shop Domain | +Access Status | +Note | +
|---|