@agenticmail/enterprise 0.5.295 → 0.5.296

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -210,6 +210,88 @@ function PermissionEditor({ userId, userName, currentPerms, pageRegistry, onSave
210
210
  );
211
211
  }
212
212
 
213
+ // ─── Inline Permission Picker (for create modal) ──
214
+
215
+ function InlinePermissionPicker({ permissions, pageRegistry, onChange }) {
216
+ var [expandedPage, setExpandedPage] = useState(null);
217
+
218
+ // Resolve grants from permissions
219
+ var grants = permissions === '*' ? (function() { var a = {}; Object.keys(pageRegistry).forEach(function(p) { a[p] = true; }); return a; })() : (permissions || {});
220
+
221
+ var togglePage = function(pid) {
222
+ var next = Object.assign({}, grants);
223
+ if (next[pid]) { delete next[pid]; if (expandedPage === pid) setExpandedPage(null); }
224
+ else { next[pid] = true; }
225
+ onChange(Object.keys(next).length === Object.keys(pageRegistry).length ? '*' : next);
226
+ };
227
+
228
+ var toggleTab = function(pid, tabId) {
229
+ var next = Object.assign({}, grants);
230
+ var current = next[pid];
231
+ var allTabs = Object.keys(pageRegistry[pid].tabs || {});
232
+ if (current === true) {
233
+ var remaining = allTabs.filter(function(t) { return t !== tabId; });
234
+ next[pid] = remaining.length > 0 ? remaining : true;
235
+ } else if (Array.isArray(current)) {
236
+ var idx = current.indexOf(tabId);
237
+ if (idx >= 0) {
238
+ var arr = current.filter(function(t) { return t !== tabId; });
239
+ if (arr.length === 0) delete next[pid];
240
+ else next[pid] = arr;
241
+ } else {
242
+ var newArr = current.concat([tabId]);
243
+ next[pid] = newArr.length === allTabs.length ? true : newArr;
244
+ }
245
+ }
246
+ onChange(Object.keys(next).length === Object.keys(pageRegistry).length && Object.values(next).every(function(v) { return v === true; }) ? '*' : next);
247
+ };
248
+
249
+ var isTabChecked = function(pid, tabId) {
250
+ var g = grants[pid];
251
+ if (!g) return false;
252
+ if (g === true) return true;
253
+ return Array.isArray(g) && g.indexOf(tabId) >= 0;
254
+ };
255
+
256
+ var sections = { overview: 'Overview', management: 'Management', administration: 'Administration' };
257
+ var grouped = {};
258
+ Object.keys(pageRegistry).forEach(function(pid) { var s = pageRegistry[pid].section; if (!grouped[s]) grouped[s] = []; grouped[s].push(pid); });
259
+
260
+ return h('div', { style: { maxHeight: 250, overflowY: 'auto', border: '1px solid var(--border)', borderRadius: 6, marginTop: 8 } },
261
+ Object.keys(sections).map(function(sKey) {
262
+ var pids = grouped[sKey] || [];
263
+ if (!pids.length) return null;
264
+ return h(Fragment, { key: sKey },
265
+ h('div', { style: { fontSize: 10, fontWeight: 600, textTransform: 'uppercase', letterSpacing: '0.05em', color: 'var(--text-muted)', padding: '8px 10px 2px' } }, sections[sKey]),
266
+ pids.map(function(pid) {
267
+ var page = pageRegistry[pid];
268
+ var checked = !!grants[pid];
269
+ var hasTabs = page.tabs && Object.keys(page.tabs).length > 0;
270
+ var isExpanded = expandedPage === pid;
271
+ return h(Fragment, { key: pid },
272
+ h('div', { style: { display: 'flex', alignItems: 'center', gap: 8, padding: '4px 10px', fontSize: 12, cursor: 'pointer', background: checked ? 'var(--bg-tertiary)' : 'transparent' }, onClick: function(e) {
273
+ if (e.target.closest && e.target.closest('[data-expand]')) return;
274
+ togglePage(pid);
275
+ } },
276
+ h('input', { type: 'checkbox', checked: checked, readOnly: true, style: { width: 14, height: 14, accentColor: 'var(--primary)', cursor: 'pointer' } }),
277
+ h('span', { style: { flex: 1 } }, page.label),
278
+ hasTabs && checked && h('button', { 'data-expand': true, className: 'btn btn-ghost', style: { padding: '0 4px', minWidth: 0, fontSize: 10, lineHeight: 1 }, onClick: function(e) { e.stopPropagation(); setExpandedPage(isExpanded ? null : pid); } },
279
+ isExpanded ? I.chevronDown() : I.chevronRight()
280
+ )
281
+ ),
282
+ hasTabs && isExpanded && checked && Object.keys(page.tabs).map(function(tabId) {
283
+ return h('div', { key: tabId, style: { display: 'flex', alignItems: 'center', gap: 8, padding: '3px 10px 3px 34px', fontSize: 11, color: 'var(--text-secondary)', cursor: 'pointer' }, onClick: function() { toggleTab(pid, tabId); } },
284
+ h('input', { type: 'checkbox', checked: isTabChecked(pid, tabId), readOnly: true, style: { width: 13, height: 13, accentColor: 'var(--primary)', cursor: 'pointer' } }),
285
+ h('span', null, page.tabs[tabId])
286
+ );
287
+ })
288
+ );
289
+ })
290
+ );
291
+ })
292
+ );
293
+ }
294
+
213
295
  // ─── Users Page ────────────────────────────────────
214
296
 
215
297
  export function UsersPage() {
@@ -357,24 +439,11 @@ export function UsersPage() {
357
439
  h('button', { type: 'button', className: 'btn btn-ghost btn-sm', onClick: function() { setShowCreatePerms(!showCreatePerms); }, style: { fontSize: 11 } }, showCreatePerms ? 'Hide' : 'Customize')
358
440
  ),
359
441
  !showCreatePerms && h('div', { style: { fontSize: 12, color: 'var(--text-muted)', marginTop: 4 } }, 'Full access (default). Click "Customize" to restrict.'),
360
- showCreatePerms && pageRegistry && h('div', { style: { maxHeight: 200, overflowY: 'auto', border: '1px solid var(--border)', borderRadius: 6, marginTop: 8 } },
361
- Object.keys(pageRegistry).map(function(pid) {
362
- var page = pageRegistry[pid];
363
- var grants = form.permissions === '*' ? null : form.permissions;
364
- var checked = !grants || (grants && grants[pid]);
365
- return h('div', { key: pid, style: { display: 'flex', alignItems: 'center', gap: 8, padding: '4px 10px', fontSize: 12, cursor: 'pointer' }, onClick: function() {
366
- setForm(function(f) {
367
- var current = f.permissions === '*' ? (function() { var a = {}; Object.keys(pageRegistry).forEach(function(p) { a[p] = true; }); return a; })() : Object.assign({}, f.permissions);
368
- if (current[pid]) { delete current[pid]; } else { current[pid] = true; }
369
- if (Object.keys(current).length === Object.keys(pageRegistry).length) return Object.assign({}, f, { permissions: '*' });
370
- return Object.assign({}, f, { permissions: current });
371
- });
372
- } },
373
- h('input', { type: 'checkbox', checked: checked, readOnly: true, style: { width: 14, height: 14, accentColor: 'var(--primary)' } }),
374
- h('span', null, page.label)
375
- );
376
- })
377
- )
442
+ showCreatePerms && pageRegistry && h(InlinePermissionPicker, {
443
+ permissions: form.permissions,
444
+ pageRegistry: pageRegistry,
445
+ onChange: function(p) { setForm(function(f) { return Object.assign({}, f, { permissions: p }); }); }
446
+ })
378
447
  ),
379
448
  (form.role === 'owner' || form.role === 'admin') && h('div', { style: { marginTop: 8, padding: 8, background: 'var(--info-soft)', borderRadius: 'var(--radius)', fontSize: 11, color: 'var(--info)' } },
380
449
  'Owner and Admin roles always have full access to all pages.'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agenticmail/enterprise",
3
- "version": "0.5.295",
3
+ "version": "0.5.296",
4
4
  "description": "AgenticMail Enterprise — cloud-hosted AI agent identity, email, auth & compliance for organizations",
5
5
  "type": "module",
6
6
  "bin": {
@@ -210,6 +210,88 @@ function PermissionEditor({ userId, userName, currentPerms, pageRegistry, onSave
210
210
  );
211
211
  }
212
212
 
213
+ // ─── Inline Permission Picker (for create modal) ──
214
+
215
+ function InlinePermissionPicker({ permissions, pageRegistry, onChange }) {
216
+ var [expandedPage, setExpandedPage] = useState(null);
217
+
218
+ // Resolve grants from permissions
219
+ var grants = permissions === '*' ? (function() { var a = {}; Object.keys(pageRegistry).forEach(function(p) { a[p] = true; }); return a; })() : (permissions || {});
220
+
221
+ var togglePage = function(pid) {
222
+ var next = Object.assign({}, grants);
223
+ if (next[pid]) { delete next[pid]; if (expandedPage === pid) setExpandedPage(null); }
224
+ else { next[pid] = true; }
225
+ onChange(Object.keys(next).length === Object.keys(pageRegistry).length ? '*' : next);
226
+ };
227
+
228
+ var toggleTab = function(pid, tabId) {
229
+ var next = Object.assign({}, grants);
230
+ var current = next[pid];
231
+ var allTabs = Object.keys(pageRegistry[pid].tabs || {});
232
+ if (current === true) {
233
+ var remaining = allTabs.filter(function(t) { return t !== tabId; });
234
+ next[pid] = remaining.length > 0 ? remaining : true;
235
+ } else if (Array.isArray(current)) {
236
+ var idx = current.indexOf(tabId);
237
+ if (idx >= 0) {
238
+ var arr = current.filter(function(t) { return t !== tabId; });
239
+ if (arr.length === 0) delete next[pid];
240
+ else next[pid] = arr;
241
+ } else {
242
+ var newArr = current.concat([tabId]);
243
+ next[pid] = newArr.length === allTabs.length ? true : newArr;
244
+ }
245
+ }
246
+ onChange(Object.keys(next).length === Object.keys(pageRegistry).length && Object.values(next).every(function(v) { return v === true; }) ? '*' : next);
247
+ };
248
+
249
+ var isTabChecked = function(pid, tabId) {
250
+ var g = grants[pid];
251
+ if (!g) return false;
252
+ if (g === true) return true;
253
+ return Array.isArray(g) && g.indexOf(tabId) >= 0;
254
+ };
255
+
256
+ var sections = { overview: 'Overview', management: 'Management', administration: 'Administration' };
257
+ var grouped = {};
258
+ Object.keys(pageRegistry).forEach(function(pid) { var s = pageRegistry[pid].section; if (!grouped[s]) grouped[s] = []; grouped[s].push(pid); });
259
+
260
+ return h('div', { style: { maxHeight: 250, overflowY: 'auto', border: '1px solid var(--border)', borderRadius: 6, marginTop: 8 } },
261
+ Object.keys(sections).map(function(sKey) {
262
+ var pids = grouped[sKey] || [];
263
+ if (!pids.length) return null;
264
+ return h(Fragment, { key: sKey },
265
+ h('div', { style: { fontSize: 10, fontWeight: 600, textTransform: 'uppercase', letterSpacing: '0.05em', color: 'var(--text-muted)', padding: '8px 10px 2px' } }, sections[sKey]),
266
+ pids.map(function(pid) {
267
+ var page = pageRegistry[pid];
268
+ var checked = !!grants[pid];
269
+ var hasTabs = page.tabs && Object.keys(page.tabs).length > 0;
270
+ var isExpanded = expandedPage === pid;
271
+ return h(Fragment, { key: pid },
272
+ h('div', { style: { display: 'flex', alignItems: 'center', gap: 8, padding: '4px 10px', fontSize: 12, cursor: 'pointer', background: checked ? 'var(--bg-tertiary)' : 'transparent' }, onClick: function(e) {
273
+ if (e.target.closest && e.target.closest('[data-expand]')) return;
274
+ togglePage(pid);
275
+ } },
276
+ h('input', { type: 'checkbox', checked: checked, readOnly: true, style: { width: 14, height: 14, accentColor: 'var(--primary)', cursor: 'pointer' } }),
277
+ h('span', { style: { flex: 1 } }, page.label),
278
+ hasTabs && checked && h('button', { 'data-expand': true, className: 'btn btn-ghost', style: { padding: '0 4px', minWidth: 0, fontSize: 10, lineHeight: 1 }, onClick: function(e) { e.stopPropagation(); setExpandedPage(isExpanded ? null : pid); } },
279
+ isExpanded ? I.chevronDown() : I.chevronRight()
280
+ )
281
+ ),
282
+ hasTabs && isExpanded && checked && Object.keys(page.tabs).map(function(tabId) {
283
+ return h('div', { key: tabId, style: { display: 'flex', alignItems: 'center', gap: 8, padding: '3px 10px 3px 34px', fontSize: 11, color: 'var(--text-secondary)', cursor: 'pointer' }, onClick: function() { toggleTab(pid, tabId); } },
284
+ h('input', { type: 'checkbox', checked: isTabChecked(pid, tabId), readOnly: true, style: { width: 13, height: 13, accentColor: 'var(--primary)', cursor: 'pointer' } }),
285
+ h('span', null, page.tabs[tabId])
286
+ );
287
+ })
288
+ );
289
+ })
290
+ );
291
+ })
292
+ );
293
+ }
294
+
213
295
  // ─── Users Page ────────────────────────────────────
214
296
 
215
297
  export function UsersPage() {
@@ -357,24 +439,11 @@ export function UsersPage() {
357
439
  h('button', { type: 'button', className: 'btn btn-ghost btn-sm', onClick: function() { setShowCreatePerms(!showCreatePerms); }, style: { fontSize: 11 } }, showCreatePerms ? 'Hide' : 'Customize')
358
440
  ),
359
441
  !showCreatePerms && h('div', { style: { fontSize: 12, color: 'var(--text-muted)', marginTop: 4 } }, 'Full access (default). Click "Customize" to restrict.'),
360
- showCreatePerms && pageRegistry && h('div', { style: { maxHeight: 200, overflowY: 'auto', border: '1px solid var(--border)', borderRadius: 6, marginTop: 8 } },
361
- Object.keys(pageRegistry).map(function(pid) {
362
- var page = pageRegistry[pid];
363
- var grants = form.permissions === '*' ? null : form.permissions;
364
- var checked = !grants || (grants && grants[pid]);
365
- return h('div', { key: pid, style: { display: 'flex', alignItems: 'center', gap: 8, padding: '4px 10px', fontSize: 12, cursor: 'pointer' }, onClick: function() {
366
- setForm(function(f) {
367
- var current = f.permissions === '*' ? (function() { var a = {}; Object.keys(pageRegistry).forEach(function(p) { a[p] = true; }); return a; })() : Object.assign({}, f.permissions);
368
- if (current[pid]) { delete current[pid]; } else { current[pid] = true; }
369
- if (Object.keys(current).length === Object.keys(pageRegistry).length) return Object.assign({}, f, { permissions: '*' });
370
- return Object.assign({}, f, { permissions: current });
371
- });
372
- } },
373
- h('input', { type: 'checkbox', checked: checked, readOnly: true, style: { width: 14, height: 14, accentColor: 'var(--primary)' } }),
374
- h('span', null, page.label)
375
- );
376
- })
377
- )
442
+ showCreatePerms && pageRegistry && h(InlinePermissionPicker, {
443
+ permissions: form.permissions,
444
+ pageRegistry: pageRegistry,
445
+ onChange: function(p) { setForm(function(f) { return Object.assign({}, f, { permissions: p }); }); }
446
+ })
378
447
  ),
379
448
  (form.role === 'owner' || form.role === 'admin') && h('div', { style: { marginTop: 8, padding: 8, background: 'var(--info-soft)', borderRadius: 'var(--radius)', fontSize: 11, color: 'var(--info)' } },
380
449
  'Owner and Admin roles always have full access to all pages.'