482 lines
17 KiB
JavaScript
482 lines
17 KiB
JavaScript
|
|
/**
|
|||
|
|
* pc-live | Bleeter-Verwaltung
|
|||
|
|
*
|
|||
|
|
* Was das Bleeter-Fenster selbst nicht kann: Unternehmensprofile einrichten und
|
|||
|
|
* Mitarbeiter freigeben. Es hängt an derselben Anmeldung wie das Webhosting –
|
|||
|
|
* wer als admin@liveinvader.ls angemeldet ist, ist auch hier der Anbieter.
|
|||
|
|
*
|
|||
|
|
* Die Anmeldung sitzt unten links in der Seitenleiste, wie in der
|
|||
|
|
* Webhosting-App. Beide teilen sich die Sitzung: einmal anmelden genügt.
|
|||
|
|
*
|
|||
|
|
* Inhalte kommen von Spielern (Handles, Anzeigenamen). Sie werden über
|
|||
|
|
* textContent gesetzt, nie über innerHTML.
|
|||
|
|
*/
|
|||
|
|
const BleeterAdminApp = (() => {
|
|||
|
|
const WIN_ID = 'app-bleeteradmin';
|
|||
|
|
const el = ICWebRender.el;
|
|||
|
|
|
|||
|
|
/* Muss zu BUSINESS_TYPES in bleeter/server/management.lua passen. */
|
|||
|
|
const PROFILE_TYPES = [
|
|||
|
|
['company', 'Unternehmen'],
|
|||
|
|
['small_business', 'Kleingewerbe'],
|
|||
|
|
['authority', 'Behörde'],
|
|||
|
|
['lifeinvader', 'Lifeinvader'],
|
|||
|
|
];
|
|||
|
|
|
|||
|
|
const typeLabel = (t) => (PROFILE_TYPES.find(p => p[0] === t) || [t, t])[1];
|
|||
|
|
|
|||
|
|
let session = null; // ic-web-Anmeldung
|
|||
|
|
let profiles = [];
|
|||
|
|
let provider = false;
|
|||
|
|
let winEl = null;
|
|||
|
|
let openId = null; // Profil, dessen Mitarbeiter gerade angezeigt werden
|
|||
|
|
|
|||
|
|
/* ── Transport ────────────────────────────────────────── */
|
|||
|
|
/* Bleeter antwortet über ein gemeinsames Ereignis, nicht über Anfrage-Ids.
|
|||
|
|
Deshalb wird je Aktion auf die nächste Antwort gewartet. */
|
|||
|
|
const waiting = new Map();
|
|||
|
|
|
|||
|
|
function call(op, payload) {
|
|||
|
|
return new Promise((resolve) => {
|
|||
|
|
const timer = setTimeout(() => {
|
|||
|
|
if (waiting.get(op) === resolve) waiting.delete(op);
|
|||
|
|
resolve({ ok: false, error: 'Keine Antwort vom Server.' });
|
|||
|
|
}, 10000);
|
|||
|
|
|
|||
|
|
waiting.set(op, (data) => { clearTimeout(timer); resolve(data); });
|
|||
|
|
fetchNui('bleeter', { op, payload: payload || {} });
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function onResponse(data) {
|
|||
|
|
if (!data || !data.action) return;
|
|||
|
|
const fn = waiting.get(data.action);
|
|||
|
|
if (!fn) return;
|
|||
|
|
waiting.delete(data.action);
|
|||
|
|
fn(data);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* ── Gerüst ───────────────────────────────────────────── */
|
|||
|
|
async function open() {
|
|||
|
|
const win = WindowManager.create({
|
|||
|
|
id: WIN_ID, title: 'Bleeter-Verwaltung', icon: '🐦', width: 940, height: 640,
|
|||
|
|
content: `
|
|||
|
|
<div class="wh-root">
|
|||
|
|
<div class="wh-side" id="ba-side"></div>
|
|||
|
|
<div class="wh-main" id="ba-main"></div>
|
|||
|
|
</div>`,
|
|||
|
|
});
|
|||
|
|
if (!win) return;
|
|||
|
|
|
|||
|
|
winEl = win;
|
|||
|
|
await refresh();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function root(id) {
|
|||
|
|
if (!winEl || !winEl.isConnected) {
|
|||
|
|
winEl = document.querySelector('[data-wid="' + WIN_ID + '"]');
|
|||
|
|
}
|
|||
|
|
return winEl ? winEl.querySelector('#' + id) : null;
|
|||
|
|
}
|
|||
|
|
function setMain(node) {
|
|||
|
|
const m = root('ba-main');
|
|||
|
|
if (m) m.replaceChildren(node);
|
|||
|
|
}
|
|||
|
|
function info(icon, text) {
|
|||
|
|
const d = el('div', 'wh-empty');
|
|||
|
|
d.appendChild(el('div', 'wh-empty-icon', icon));
|
|||
|
|
d.appendChild(el('div', null, text));
|
|||
|
|
return d;
|
|||
|
|
}
|
|||
|
|
function notify(res, okText) {
|
|||
|
|
Desktop.showNotification(res && res.ok ? '✔ ' + okText
|
|||
|
|
: '⚠ ' + ((res && res.error) || 'Fehlgeschlagen.'));
|
|||
|
|
return res && res.ok;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
async function refresh() {
|
|||
|
|
setMain(info('⏳', 'Lade…'));
|
|||
|
|
|
|||
|
|
const s = await ICWebNet.call('session');
|
|||
|
|
session = (s.ok && s.data && s.data.session) || null;
|
|||
|
|
|
|||
|
|
const res = await call('listBusinessProfiles');
|
|||
|
|
profiles = (res.ok && res.payload && res.payload.profiles) || [];
|
|||
|
|
provider = !!(res.ok && res.payload && res.payload.provider);
|
|||
|
|
|
|||
|
|
renderSide();
|
|||
|
|
showProfiles();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* ── Seitenleiste mit Anmeldung unten links ───────────── */
|
|||
|
|
function renderSide() {
|
|||
|
|
const side = root('ba-side');
|
|||
|
|
if (!side) return;
|
|||
|
|
side.replaceChildren();
|
|||
|
|
|
|||
|
|
side.appendChild(el('div', 'wh-side-title', 'Bleeter'));
|
|||
|
|
|
|||
|
|
const nav = el('div', 'wh-nav active');
|
|||
|
|
nav.appendChild(el('span', 'wh-nav-icon', '🏢'));
|
|||
|
|
nav.appendChild(el('span', null, provider ? 'Alle Profile' : 'Meine Profile'));
|
|||
|
|
nav.addEventListener('click', () => showProfiles());
|
|||
|
|
side.appendChild(nav);
|
|||
|
|
|
|||
|
|
if (provider) {
|
|||
|
|
const add = el('div', 'wh-nav');
|
|||
|
|
add.appendChild(el('span', 'wh-nav-icon', '+'));
|
|||
|
|
add.appendChild(el('span', null, 'Profil einrichten'));
|
|||
|
|
add.addEventListener('click', () => showCreate());
|
|||
|
|
side.appendChild(add);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* Anmeldung – unten links, wie im Webhosting. */
|
|||
|
|
const foot = el('div', 'wh-side-user');
|
|||
|
|
|
|||
|
|
if (session) {
|
|||
|
|
foot.appendChild(el('div', 'wh-side-username', session.username));
|
|||
|
|
foot.appendChild(el('div', 'wh-side-role',
|
|||
|
|
session.role === 'superadmin' ? 'Anbieter (Lifeinvader)'
|
|||
|
|
: { admin: 'Domänenadmin', editor: 'Redakteur' }[session.role] || session.role));
|
|||
|
|
|
|||
|
|
const out = el('a', 'wh-side-logout', 'Abmelden');
|
|||
|
|
out.addEventListener('click', async () => {
|
|||
|
|
await ICWebNet.call('logout');
|
|||
|
|
Desktop.showNotification('Abgemeldet.');
|
|||
|
|
await refresh();
|
|||
|
|
});
|
|||
|
|
foot.appendChild(out);
|
|||
|
|
} else {
|
|||
|
|
foot.appendChild(el('div', 'wh-side-role', 'Nicht angemeldet'));
|
|||
|
|
const go = el('a', 'wh-side-logout', '🔑 Als Anbieter anmelden');
|
|||
|
|
go.style.color = 'var(--accent)';
|
|||
|
|
go.addEventListener('click', showLogin);
|
|||
|
|
foot.appendChild(go);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
side.appendChild(foot);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* ── Anmeldung ────────────────────────────────────────── */
|
|||
|
|
function showLogin() {
|
|||
|
|
ICWebRender.modal('Anmelden', (box, close) => {
|
|||
|
|
box.appendChild(el('div', 'icweb-modal-sub',
|
|||
|
|
'Zugangsdaten der Lifeinvader-Verwaltung. Es ist dieselbe Anmeldung wie '
|
|||
|
|
+ 'beim Webhosting – einmal anmelden genügt für beides.'));
|
|||
|
|
|
|||
|
|
const mk = (label, type, value, placeholder) => {
|
|||
|
|
const row = el('div', 'wh-field');
|
|||
|
|
row.appendChild(el('label', 'wh-label', label));
|
|||
|
|
const input = el('input', 'icweb-input');
|
|||
|
|
input.setAttribute('type', type);
|
|||
|
|
if (placeholder) input.setAttribute('placeholder', placeholder);
|
|||
|
|
input.value = value || '';
|
|||
|
|
row.appendChild(input);
|
|||
|
|
box.appendChild(row);
|
|||
|
|
return input;
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const user = mk('Benutzername', 'text', 'admin@liveinvader.ls', 'name@domäne.ls');
|
|||
|
|
const pass = mk('Passwort', 'password', '');
|
|||
|
|
const err = el('div', 'wh-login-error');
|
|||
|
|
box.appendChild(err);
|
|||
|
|
|
|||
|
|
const row = el('div', 'icweb-modal-actions');
|
|||
|
|
const cancel = el('button', 'icweb-btn-ghost', 'Abbrechen');
|
|||
|
|
cancel.addEventListener('click', close);
|
|||
|
|
|
|||
|
|
const go = el('button', 'icweb-btn-primary', 'Anmelden');
|
|||
|
|
const submit = async () => {
|
|||
|
|
err.textContent = '';
|
|||
|
|
if (!user.value || !pass.value) {
|
|||
|
|
err.textContent = 'Bitte Benutzername und Passwort eingeben.';
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
go.disabled = true;
|
|||
|
|
const res = await ICWebNet.call('login', user.value, pass.value);
|
|||
|
|
go.disabled = false;
|
|||
|
|
pass.value = '';
|
|||
|
|
|
|||
|
|
if (!res.ok) { err.textContent = res.error || 'Anmeldung fehlgeschlagen.'; return; }
|
|||
|
|
close();
|
|||
|
|
await refresh();
|
|||
|
|
};
|
|||
|
|
go.addEventListener('click', submit);
|
|||
|
|
[user, pass].forEach(i =>
|
|||
|
|
i.addEventListener('keydown', e => { if (e.key === 'Enter') submit(); }));
|
|||
|
|
|
|||
|
|
row.append(cancel, go);
|
|||
|
|
box.appendChild(row);
|
|||
|
|
setTimeout(() => pass.focus(), 0);
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* ── Profilliste ──────────────────────────────────────── */
|
|||
|
|
function header(title, sub) {
|
|||
|
|
const h = el('div', 'wh-head');
|
|||
|
|
h.appendChild(el('div', 'wh-head-title', title));
|
|||
|
|
if (sub) h.appendChild(el('div', 'wh-head-sub', sub));
|
|||
|
|
return h;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
function showProfiles() {
|
|||
|
|
const wrap = el('div', 'wh-page');
|
|||
|
|
wrap.appendChild(header(provider ? 'Unternehmensprofile' : 'Meine Profile',
|
|||
|
|
provider
|
|||
|
|
? 'Alle Profile auf Bleeter, die keiner Privatperson gehören.'
|
|||
|
|
: 'Profile, für die du Mitarbeiter freigeben darfst.'));
|
|||
|
|
|
|||
|
|
if (!session) {
|
|||
|
|
wrap.appendChild(info('🔑',
|
|||
|
|
'Melde dich unten links an, um Bleeter zu verwalten.'));
|
|||
|
|
return setMain(wrap);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (!profiles.length) {
|
|||
|
|
wrap.appendChild(info('🐦', provider
|
|||
|
|
? 'Noch kein Unternehmensprofil eingerichtet.'
|
|||
|
|
: 'Du verwaltest kein Bleeter-Profil.'));
|
|||
|
|
return setMain(wrap);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const table = el('div', 'wh-table');
|
|||
|
|
profiles.forEach(p => {
|
|||
|
|
const row = el('div', 'wh-row');
|
|||
|
|
|
|||
|
|
const col = el('div');
|
|||
|
|
col.appendChild(el('div', 'wh-row-title', '@' + p.handle));
|
|||
|
|
col.appendChild(el('div', 'wh-row-sub',
|
|||
|
|
(p.display_name || '—') + ' · ' + typeLabel(p.profile_type)
|
|||
|
|
+ ' · ' + (p.member_count || 0) + ' Mitarbeiter'));
|
|||
|
|
row.appendChild(col);
|
|||
|
|
|
|||
|
|
const right = el('div', 'wh-row-right');
|
|||
|
|
if (p.is_verified) right.appendChild(el('span', 'wh-tag ok', 'verifiziert'));
|
|||
|
|
if (p.is_locked) right.appendChild(el('span', 'wh-tag bad', 'gesperrt'));
|
|||
|
|
|
|||
|
|
const members = el('button', 'icweb-btn-primary', 'Mitarbeiter');
|
|||
|
|
members.addEventListener('click', () => showMembers(p));
|
|||
|
|
right.appendChild(members);
|
|||
|
|
|
|||
|
|
row.appendChild(right);
|
|||
|
|
table.appendChild(row);
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
wrap.appendChild(table);
|
|||
|
|
setMain(wrap);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* ── Profil einrichten (nur Anbieter) ─────────────────── */
|
|||
|
|
function showCreate() {
|
|||
|
|
const wrap = el('div', 'wh-page');
|
|||
|
|
wrap.appendChild(header('Profil einrichten',
|
|||
|
|
'Das Profil gehört keinem Konto – bedient wird es über die freigegebenen '
|
|||
|
|
+ 'Mitarbeiter. Deshalb gehört gleich ein Verantwortlicher dazu.'));
|
|||
|
|
|
|||
|
|
const form = el('div', 'wh-form');
|
|||
|
|
|
|||
|
|
const mk = (label, placeholder) => {
|
|||
|
|
const row = el('div', 'wh-field');
|
|||
|
|
row.appendChild(el('label', 'wh-label', label));
|
|||
|
|
const input = el('input', 'icweb-input');
|
|||
|
|
if (placeholder) input.setAttribute('placeholder', placeholder);
|
|||
|
|
row.appendChild(input);
|
|||
|
|
form.appendChild(row);
|
|||
|
|
return input;
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const handleRow = el('div', 'wh-field');
|
|||
|
|
handleRow.appendChild(el('label', 'wh-label', 'Handle'));
|
|||
|
|
const handleWrap = el('div', 'wh-inline');
|
|||
|
|
handleWrap.appendChild(el('span', 'wh-inline-suffix', '@'));
|
|||
|
|
const handle = el('input', 'icweb-input');
|
|||
|
|
handle.setAttribute('placeholder', 'weazelnews');
|
|||
|
|
handleWrap.appendChild(handle);
|
|||
|
|
handleRow.appendChild(handleWrap);
|
|||
|
|
form.appendChild(handleRow);
|
|||
|
|
|
|||
|
|
const display = mk('Anzeigename', 'Weazel News');
|
|||
|
|
const mail = mk('Kontaktadresse (optional)', 'presse@weazel-news.ls');
|
|||
|
|
|
|||
|
|
const typeRow = el('div', 'wh-field');
|
|||
|
|
typeRow.appendChild(el('label', 'wh-label', 'Art'));
|
|||
|
|
const type = el('select', 'icweb-input');
|
|||
|
|
PROFILE_TYPES.forEach(([v, l]) => {
|
|||
|
|
const o = el('option', null, l);
|
|||
|
|
o.value = v;
|
|||
|
|
type.appendChild(o);
|
|||
|
|
});
|
|||
|
|
typeRow.appendChild(type);
|
|||
|
|
form.appendChild(typeRow);
|
|||
|
|
|
|||
|
|
const ownerRow = el('div', 'wh-field');
|
|||
|
|
ownerRow.appendChild(el('label', 'wh-label', 'Verantwortlich (muss online sein)'));
|
|||
|
|
const owner = el('select', 'icweb-input');
|
|||
|
|
// Ohne ausdrücklichen Wert liefert select.value den Text der Option.
|
|||
|
|
const noOwner = el('option', null, '— später festlegen —');
|
|||
|
|
noOwner.value = '';
|
|||
|
|
owner.appendChild(noOwner);
|
|||
|
|
ownerRow.appendChild(owner);
|
|||
|
|
form.appendChild(ownerRow);
|
|||
|
|
|
|||
|
|
call('listCandidates', {}).then(res => {
|
|||
|
|
const list = (res.ok && res.payload && res.payload.candidates) || [];
|
|||
|
|
list.forEach(c => {
|
|||
|
|
const o = el('option', null, c.name);
|
|||
|
|
o.value = String(c.source);
|
|||
|
|
owner.appendChild(o);
|
|||
|
|
});
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
const go = el('button', 'icweb-btn-primary', 'Einrichten');
|
|||
|
|
go.addEventListener('click', async () => {
|
|||
|
|
go.disabled = true;
|
|||
|
|
const res = await call('createBusinessProfile', {
|
|||
|
|
handle: handle.value,
|
|||
|
|
displayName: display.value,
|
|||
|
|
emailContact: mail.value,
|
|||
|
|
profileType: type.value,
|
|||
|
|
ownerSource: owner.value ? Number(owner.value) : null,
|
|||
|
|
});
|
|||
|
|
go.disabled = false;
|
|||
|
|
if (notify(res, 'Profil eingerichtet.')) refresh();
|
|||
|
|
});
|
|||
|
|
form.appendChild(go);
|
|||
|
|
|
|||
|
|
form.appendChild(el('div', 'wh-hint',
|
|||
|
|
'Erlaubt sind Kleinbuchstaben, Ziffern, Punkt, Unterstrich und Bindestrich. '
|
|||
|
|
+ 'Behörden- und Systemnamen sind reserviert.'));
|
|||
|
|
|
|||
|
|
wrap.appendChild(form);
|
|||
|
|
setMain(wrap);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/* ── Mitarbeiter ──────────────────────────────────────── */
|
|||
|
|
async function showMembers(profile) {
|
|||
|
|
openId = profile.id;
|
|||
|
|
setMain(info('⏳', 'Lade Mitarbeiter…'));
|
|||
|
|
|
|||
|
|
const [membersRes, candRes] = await Promise.all([
|
|||
|
|
call('listProfileMembers', { profileId: profile.id }),
|
|||
|
|
call('listCandidates', { profileId: profile.id }),
|
|||
|
|
]);
|
|||
|
|
|
|||
|
|
const wrap = el('div', 'wh-page');
|
|||
|
|
|
|||
|
|
const head = el('div', 'wh-domainhead');
|
|||
|
|
const left = el('div');
|
|||
|
|
left.appendChild(el('div', 'wh-card-domain', '@' + profile.handle));
|
|||
|
|
left.appendChild(el('div', 'wh-card-title',
|
|||
|
|
(profile.display_name || '') + ' · ' + typeLabel(profile.profile_type)));
|
|||
|
|
head.appendChild(left);
|
|||
|
|
|
|||
|
|
const back = el('div', 'wh-tab-back', '← Übersicht');
|
|||
|
|
back.addEventListener('click', () => showProfiles());
|
|||
|
|
head.appendChild(back);
|
|||
|
|
wrap.appendChild(head);
|
|||
|
|
|
|||
|
|
if (!membersRes.ok) {
|
|||
|
|
wrap.appendChild(el('div', 'wh-hint', membersRes.error || 'Keine Berechtigung.'));
|
|||
|
|
return setMain(wrap);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
wrap.appendChild(el('div', 'wh-hint',
|
|||
|
|
'Freigegebene schreiben unter diesem Profil. „Verwalten" gibt zusätzlich das '
|
|||
|
|
+ 'Recht, weitere Leute freizugeben – wer nur schreiben darf, kann sich '
|
|||
|
|
+ 'nicht selbst hochstufen.'));
|
|||
|
|
|
|||
|
|
const members = (membersRes.payload && membersRes.payload.members) || [];
|
|||
|
|
const table = el('div', 'wh-table');
|
|||
|
|
if (!members.length) table.appendChild(el('div', 'wh-hint', 'Noch niemand freigegeben.'));
|
|||
|
|
|
|||
|
|
members.forEach(m => {
|
|||
|
|
const row = el('div', 'wh-row');
|
|||
|
|
|
|||
|
|
const col = el('div');
|
|||
|
|
col.appendChild(el('div', 'wh-row-title', m.name || m.char_id));
|
|||
|
|
col.appendChild(el('div', 'wh-row-sub', m.role || 'member'));
|
|||
|
|
row.appendChild(col);
|
|||
|
|
|
|||
|
|
const right = el('div', 'wh-row-right');
|
|||
|
|
right.appendChild(el('span', 'wh-tag ' + (m.online ? 'ok' : ''),
|
|||
|
|
m.online ? 'online' : 'offline'));
|
|||
|
|
if (m.can_post) right.appendChild(el('span', 'wh-tag', 'schreibt'));
|
|||
|
|
if (m.can_edit_profile) right.appendChild(el('span', 'wh-tag', 'Profil'));
|
|||
|
|
if (m.can_manage_members) right.appendChild(el('span', 'wh-tag warn', 'verwaltet'));
|
|||
|
|
|
|||
|
|
const rm = el('button', 'icweb-btn-danger', 'Entfernen');
|
|||
|
|
rm.addEventListener('click', () => {
|
|||
|
|
ICWebRender.confirmBox('Freigabe entfernen',
|
|||
|
|
(m.name || m.char_id) + ' schreibt dann nicht mehr unter @' + profile.handle + '.',
|
|||
|
|
async () => {
|
|||
|
|
const res = await call('setProfileMember', {
|
|||
|
|
profileId: profile.id, charId: m.char_id, remove: true,
|
|||
|
|
});
|
|||
|
|
if (notify(res, 'Freigabe entfernt.')) showMembers(profile);
|
|||
|
|
}, { danger: true, okLabel: 'Entfernen' });
|
|||
|
|
});
|
|||
|
|
right.appendChild(rm);
|
|||
|
|
|
|||
|
|
row.appendChild(right);
|
|||
|
|
table.appendChild(row);
|
|||
|
|
});
|
|||
|
|
wrap.appendChild(table);
|
|||
|
|
|
|||
|
|
/* Freigeben */
|
|||
|
|
wrap.appendChild(el('div', 'wh-subhead', 'Mitarbeiter freigeben'));
|
|||
|
|
|
|||
|
|
const candidates = (candRes.ok && candRes.payload && candRes.payload.candidates) || [];
|
|||
|
|
if (!candidates.length) {
|
|||
|
|
wrap.appendChild(el('div', 'wh-hint',
|
|||
|
|
'Niemand verfügbar. Die Person muss online sein, um freigegeben zu werden.'));
|
|||
|
|
return setMain(wrap);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const form = el('div', 'wh-form wh-form-inline');
|
|||
|
|
|
|||
|
|
const pick = el('div', 'wh-field');
|
|||
|
|
pick.appendChild(el('label', 'wh-label', 'Person'));
|
|||
|
|
const sel = el('select', 'icweb-input');
|
|||
|
|
candidates.forEach(c => {
|
|||
|
|
const o = el('option', null, c.name);
|
|||
|
|
o.value = String(c.source);
|
|||
|
|
sel.appendChild(o);
|
|||
|
|
});
|
|||
|
|
pick.appendChild(sel);
|
|||
|
|
form.appendChild(pick);
|
|||
|
|
|
|||
|
|
const roleRow = el('div', 'wh-field');
|
|||
|
|
roleRow.appendChild(el('label', 'wh-label', 'Rechte'));
|
|||
|
|
const rights = el('select', 'icweb-input');
|
|||
|
|
[['post', 'Schreiben'],
|
|||
|
|
['edit', 'Schreiben und Profil bearbeiten'],
|
|||
|
|
['manage', 'Schreiben, Profil bearbeiten und Leute freigeben']].forEach(([v, l]) => {
|
|||
|
|
const o = el('option', null, l);
|
|||
|
|
o.value = v;
|
|||
|
|
rights.appendChild(o);
|
|||
|
|
});
|
|||
|
|
roleRow.appendChild(rights);
|
|||
|
|
form.appendChild(roleRow);
|
|||
|
|
|
|||
|
|
const add = el('button', 'icweb-btn-primary', 'Freigeben');
|
|||
|
|
add.addEventListener('click', async () => {
|
|||
|
|
const level = rights.value;
|
|||
|
|
const res = await call('setProfileMember', {
|
|||
|
|
profileId: profile.id,
|
|||
|
|
targetSource: Number(sel.value),
|
|||
|
|
role: level === 'manage' ? 'manager' : 'member',
|
|||
|
|
canPost: true,
|
|||
|
|
canEditProfile: level === 'edit' || level === 'manage',
|
|||
|
|
canManageMembers: level === 'manage',
|
|||
|
|
});
|
|||
|
|
if (notify(res, 'Freigabe gespeichert.')) showMembers(profile);
|
|||
|
|
});
|
|||
|
|
form.appendChild(add);
|
|||
|
|
|
|||
|
|
wrap.appendChild(form);
|
|||
|
|
setMain(wrap);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return { open, onResponse };
|
|||
|
|
})();
|