// Add-User modal (#10). Creates a user account directly with an admin-set
// password and assigns them to a workspace + role (admin-provisioning for
// instances with no outbound email). Two open modes, ONE shared form:
//
// openAddUserModal({ id, name }, opts) -> fixed-workspace mode (members view).
// No picker; assigns into that workspace.
// openAddUserModal(null, opts) -> picker mode (platform Users admin page).
// Shows an Org/Workspace picker; the admin
// chooses the target workspace.
//
// opts.onSuccess: (result) => void - fires on 201 (server response body)
// opts.mapError: (err) => string - translates server error to display text
import { api } from '../api.js';
import { t } from '../i18n.js';
const EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
// Roles the picker offers. This is the SET POST /api/admin/users accepts
// (server: routes/admin.js WORKSPACE_ROLES) - keep them in sync so we never
// offer a value the endpoint 400s (the platform_operator dropdown/endpoint
// mismatch we already hit). Order here is display order (least-privilege first
// = the default selection); the server validates set membership, not order.
const WORKSPACE_ROLES = ['workspace_viewer', 'workspace_editor', 'workspace_admin'];
// Crockford-ish readable random password: avoids ambiguous chars (0/O, 1/l/I).
function generatePassword(len = 16) {
const alphabet = 'ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz23456789';
const arr = new Uint32Array(len);
crypto.getRandomValues(arr);
let out = '';
for (let i = 0; i < len; i++) out += alphabet[arr[i] % alphabet.length];
return out;
}
function wsLabel(w) {
return `${w.organization_name || '—'} / ${w.name}`;
}
export function openAddUserModal(workspace, opts = {}) {
const { onSuccess, mapError } = opts;
// Picker mode whenever no concrete target workspace was supplied.
const pickerMode = !(workspace && workspace.id);
const title = pickerMode
? t('members.modal.add_user_title_generic')
: t('members.modal.add_user_title', { workspace: esc(workspace.name) });
const roleOptions = WORKSPACE_ROLES
.map(r => ``)
.join('');
// Workspace picker block — only rendered in picker mode. A filter input above
// a