let _qCtx = null; function openQuotaModal(type, name, path, validUsers) { _qCtx = { type, name, path, users: validUsers ? validUsers.split(/[\s,]+/).filter(Boolean) : [] }; document.getElementById('quotaModalTitle').textContent = 'Quota — ' + (type === 'smb' ? esc(name) : esc(path)); let body = ''; if (type === 'smb' && _qCtx.users.length > 0) { body = _qCtx.users.map(u => `
GB
`).join(''); } else { const note = type === 'nfs' ? `

Path: ${esc(path)}

` : `

No valid users on this share — enter a username manually.

`; body = note + `
GB
`; } document.getElementById('quotaModalBody').innerHTML = body; new bootstrap.Modal(document.getElementById('quotaModal')).show(); } async function submitQuota() { if (!_qCtx) return; const calls = []; if (_qCtx.type === 'smb' && _qCtx.users.length > 0) { for (const inp of document.querySelectorAll('.quota-inp')) { const gb = parseInt(inp.value); if (!gb || gb <= 0) continue; calls.push({ type: 'smb', user: inp.dataset.user, quota: gb }); } } else { const user = document.getElementById('qManualUser')?.value.trim(); const gb = parseInt(document.getElementById('qManualGB')?.value); if (!user) return showToast('Username is required.', 'danger'); if (!gb || gb <= 0) return showToast('Quota must be a positive number.', 'danger'); calls.push({ type: _qCtx.type, user, path: _qCtx.path, quota: gb }); } if (!calls.length) return showToast('No quota values entered.', 'warning'); bootstrap.Modal.getInstance(document.getElementById('quotaModal')).hide(); const results = await Promise.all(calls.map(body => apiFetch('/api/quota', { method: 'POST', headers: {'Content-Type': 'application/json'}, body: JSON.stringify(body) }) )); const failed = results.filter(r => r && !r.ok).length; if (failed) showToast(`${failed} quota(s) failed to apply.`, 'danger'); else showToast(`Quota${calls.length > 1 ? 's' : ''} applied.`); loadDashboard(); }