@xeplr/ui-account 1.0.0

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.
Files changed (42) hide show
  1. package/LICENSE +21 -0
  2. package/package.json +17 -0
  3. package/src/AccessContext.jsx +125 -0
  4. package/src/AccessGuard.jsx +35 -0
  5. package/src/ProtectedRoute.jsx +55 -0
  6. package/src/ThemeContext.jsx +84 -0
  7. package/src/adminApi.js +68 -0
  8. package/src/api.js +196 -0
  9. package/src/designs/AccessMatrixSample.jsx +199 -0
  10. package/src/designs/ActivateSample.jsx +38 -0
  11. package/src/designs/ChangePasswordSample.jsx +31 -0
  12. package/src/designs/ForgotPasswordSample.jsx +22 -0
  13. package/src/designs/LoginSample.jsx +26 -0
  14. package/src/designs/MasterSettingsSample.jsx +206 -0
  15. package/src/designs/NotActivatedSample.jsx +17 -0
  16. package/src/designs/ProfileSample.jsx +36 -0
  17. package/src/designs/RegisterSample.jsx +34 -0
  18. package/src/designs/ResetPasswordSample.jsx +42 -0
  19. package/src/designs/TenantPickerSample.jsx +39 -0
  20. package/src/designs/TenantSample.jsx +182 -0
  21. package/src/designs/UserRolesMatrixSample.jsx +77 -0
  22. package/src/designs/admin.css +525 -0
  23. package/src/designs/auth.css +160 -0
  24. package/src/designs/index.js +13 -0
  25. package/src/designs/theme.css +231 -0
  26. package/src/index.js +41 -0
  27. package/src/masterApi.js +28 -0
  28. package/src/pages.jsx +92 -0
  29. package/src/token.js +38 -0
  30. package/src/useAccessMatrixController.js +244 -0
  31. package/src/useActivateController.js +37 -0
  32. package/src/useChangePasswordController.js +48 -0
  33. package/src/useForgotPasswordController.js +32 -0
  34. package/src/useLoginController.js +53 -0
  35. package/src/useMasterSettingsController.js +199 -0
  36. package/src/useProfileController.js +62 -0
  37. package/src/useRegisterController.js +41 -0
  38. package/src/useResetPasswordController.js +36 -0
  39. package/src/useTenantController.js +146 -0
  40. package/src/useTenantPickerController.js +97 -0
  41. package/src/useUserRolesController.js +81 -0
  42. package/src/validateDesign.js +101 -0
@@ -0,0 +1,39 @@
1
+ import './auth.css';
2
+
3
+ export default function TenantPickerSample({ tenants, loading, error, label, handleSelect, onLogout }) {
4
+ return (
5
+ <div className="xeplr-auth-container">
6
+ <h1>Select {label}</h1>
7
+ {error && <div className="xeplr-auth-alert xeplr-auth-alert-error">{error}</div>}
8
+ {loading ? (
9
+ <p>Loading...</p>
10
+ ) : tenants.length === 0 ? (
11
+ <div className="xeplr-auth-alert xeplr-auth-alert-error">
12
+ No {label.toLowerCase()}s available. Contact your administrator.
13
+ </div>
14
+ ) : (
15
+ <div className="xeplr-tenant-picker">
16
+ {tenants.map(function(t) {
17
+ return (
18
+ <button
19
+ key={t.id}
20
+ type="button"
21
+ className="xeplr-tenant-option"
22
+ onClick={function() { handleSelect(t); }}
23
+ >
24
+ <span className="xeplr-tenant-name">{t.name}</span>
25
+ {t.code && <span className="xeplr-tenant-code">{t.code}</span>}
26
+ {t.description && <span className="xeplr-tenant-desc">{t.description}</span>}
27
+ </button>
28
+ );
29
+ })}
30
+ </div>
31
+ )}
32
+ {onLogout && (
33
+ <div className="xeplr-auth-links">
34
+ <p><button type="button" onClick={onLogout} style={{ background: 'none', border: 'none', color: 'var(--xeplr-text-muted, #777)', cursor: 'pointer', fontSize: '14px' }}>Sign out</button></p>
35
+ </div>
36
+ )}
37
+ </div>
38
+ );
39
+ }
@@ -0,0 +1,182 @@
1
+ import './admin.css';
2
+
3
+ export default function TenantSample({
4
+ tenants, users, loading, error, saving, editingItem, editForm,
5
+ activeTab, setActiveTab, search, setSearch, label, labels,
6
+ startAdd, startEdit, cancelEdit, updateField, handleSave, handleDelete,
7
+ handleAssignUser, isUserAssignedTenant, reload, level
8
+ }) {
9
+ return (
10
+ <div className="xeplr-admin-container">
11
+ <div className="xeplr-admin-header">
12
+ <h2>{labels} Management</h2>
13
+ <div style={{ display: 'flex', gap: '8px' }}>
14
+ {activeTab === 'tenants' && (
15
+ <button type="button" onClick={startAdd} className="xeplr-admin-btn-primary" disabled={saving || editingItem === '__new__'}>
16
+ + Add {label}
17
+ </button>
18
+ )}
19
+ <button type="button" onClick={reload} className="xeplr-admin-btn-secondary">Refresh</button>
20
+ </div>
21
+ </div>
22
+
23
+ <div className="xeplr-admin-tabs" role="tablist">
24
+ <button role="tab" className={'xeplr-admin-tab' + (activeTab === 'tenants' ? ' xeplr-admin-tab-active' : '')} onClick={function() { setActiveTab('tenants'); setSearch(''); }}>
25
+ {labels}
26
+ </button>
27
+ <button role="tab" className={'xeplr-admin-tab' + (activeTab === 'users' ? ' xeplr-admin-tab-active' : '')} onClick={function() { setActiveTab('users'); setSearch(''); }}>
28
+ Assign Users
29
+ </button>
30
+ </div>
31
+
32
+ <div className="xeplr-admin-toolbar">
33
+ <input type="text" placeholder={'Search ' + (activeTab === 'tenants' ? labels.toLowerCase() : 'users') + '...'} value={search} onChange={function(e) { setSearch(e.target.value); }} className="xeplr-admin-search" />
34
+ </div>
35
+
36
+ {error && <div className="xeplr-admin-alert xeplr-admin-alert-error">{error}</div>}
37
+
38
+ {loading ? (
39
+ <div className="xeplr-admin-loading">Loading...</div>
40
+ ) : activeTab === 'tenants' ? (
41
+ renderTenantList(tenants, editingItem, editForm, saving, label, startEdit, cancelEdit, updateField, handleSave, handleDelete)
42
+ ) : (
43
+ renderUserAssignment(users, tenants, search, handleAssignUser, isUserAssignedTenant, label)
44
+ )}
45
+ </div>
46
+ );
47
+ }
48
+
49
+ function renderTenantList(tenants, editingItem, editForm, saving, label, startEdit, cancelEdit, updateField, handleSave, handleDelete) {
50
+ return (
51
+ <div className="xeplr-admin-matrix-wrapper">
52
+ <table className="xeplr-admin-matrix xeplr-admin-master-table">
53
+ <thead>
54
+ <tr>
55
+ <th className="xeplr-admin-sticky-col">Name</th>
56
+ <th>Code</th>
57
+ <th>Description</th>
58
+ <th className="xeplr-admin-actions-col">Actions</th>
59
+ </tr>
60
+ </thead>
61
+ <tbody>
62
+ {editingItem === '__new__' && (
63
+ <tr className="xeplr-admin-editing-row">
64
+ <td className="xeplr-admin-sticky-col">
65
+ <input type="text" className="xeplr-admin-inline-input" value={editForm.name || ''} onChange={function(e) { updateField('name', e.target.value); }} placeholder={label + ' name'} autoFocus />
66
+ </td>
67
+ <td>
68
+ <input type="text" className="xeplr-admin-inline-input xeplr-admin-group-input" value={editForm.code || ''} onChange={function(e) { updateField('code', e.target.value); }} placeholder="Short code" />
69
+ </td>
70
+ <td>
71
+ <input type="text" className="xeplr-admin-inline-input" value={editForm.description || ''} onChange={function(e) { updateField('description', e.target.value); }} placeholder="Description" />
72
+ </td>
73
+ <td className="xeplr-admin-actions-col">
74
+ <div className="xeplr-admin-action-btns">
75
+ <button onClick={handleSave} disabled={saving} className="xeplr-admin-btn-save" title="Save">{'\u2713'}</button>
76
+ <button onClick={cancelEdit} className="xeplr-admin-btn-cancel" title="Cancel">{'\u2715'}</button>
77
+ </div>
78
+ </td>
79
+ </tr>
80
+ )}
81
+ {tenants.length === 0 && editingItem !== '__new__' ? (
82
+ <tr><td colSpan="4" className="xeplr-admin-empty">No {label.toLowerCase()}s found</td></tr>
83
+ ) : (
84
+ tenants.map(function(t) {
85
+ var isEditing = editingItem === t.id;
86
+ return (
87
+ <tr key={t.id} className={isEditing ? 'xeplr-admin-editing-row' : ''}>
88
+ <td className="xeplr-admin-sticky-col">
89
+ {isEditing ? (
90
+ <input type="text" className="xeplr-admin-inline-input" value={editForm.name || ''} onChange={function(e) { updateField('name', e.target.value); }} />
91
+ ) : (
92
+ <span className="xeplr-admin-item-name">{t.name}</span>
93
+ )}
94
+ </td>
95
+ <td>
96
+ {isEditing ? (
97
+ <input type="text" className="xeplr-admin-inline-input xeplr-admin-group-input" value={editForm.code || ''} onChange={function(e) { updateField('code', e.target.value); }} />
98
+ ) : (
99
+ t.code ? <span className="xeplr-admin-group-tag">{t.code}</span> : '\u2014'
100
+ )}
101
+ </td>
102
+ <td>
103
+ {isEditing ? (
104
+ <input type="text" className="xeplr-admin-inline-input" value={editForm.description || ''} onChange={function(e) { updateField('description', e.target.value); }} />
105
+ ) : (
106
+ t.description || '\u2014'
107
+ )}
108
+ </td>
109
+ <td className="xeplr-admin-actions-col">
110
+ <div className="xeplr-admin-action-btns">
111
+ {isEditing ? (
112
+ <>
113
+ <button onClick={handleSave} className="xeplr-admin-btn-save" title="Save">{'\u2713'}</button>
114
+ <button onClick={cancelEdit} className="xeplr-admin-btn-cancel" title="Cancel">{'\u2715'}</button>
115
+ </>
116
+ ) : (
117
+ <>
118
+ <button onClick={function() { startEdit(t); }} className="xeplr-admin-btn-edit" title="Edit" disabled={editingItem !== null}>{'\u270E'}</button>
119
+ <button onClick={function() { if (confirm('Delete this ' + label.toLowerCase() + '?')) handleDelete(t.id); }} className="xeplr-admin-btn-delete" title="Delete" disabled={editingItem !== null}>{'\u2715'}</button>
120
+ </>
121
+ )}
122
+ </div>
123
+ </td>
124
+ </tr>
125
+ );
126
+ })
127
+ )}
128
+ </tbody>
129
+ </table>
130
+ </div>
131
+ );
132
+ }
133
+
134
+ function renderUserAssignment(users, tenants, search, handleAssignUser, isUserAssignedTenant, label) {
135
+ var q = search ? search.toLowerCase() : '';
136
+ var filtered = users.filter(function(u) {
137
+ if (!q) return true;
138
+ return (u.name && u.name.toLowerCase().includes(q)) || (u.email && u.email.toLowerCase().includes(q));
139
+ });
140
+
141
+ return (
142
+ <div className="xeplr-admin-matrix-wrapper">
143
+ <table className="xeplr-admin-matrix" role="grid">
144
+ <thead>
145
+ <tr>
146
+ <th className="xeplr-admin-sticky-col">User</th>
147
+ {tenants.map(function(t) {
148
+ return <th key={t.id} className="xeplr-admin-role-header">{t.name}</th>;
149
+ })}
150
+ </tr>
151
+ </thead>
152
+ <tbody>
153
+ {filtered.length === 0 ? (
154
+ <tr><td colSpan={tenants.length + 1} className="xeplr-admin-empty">No users found</td></tr>
155
+ ) : (
156
+ filtered.map(function(u) {
157
+ return (
158
+ <tr key={u.id}>
159
+ <td className="xeplr-admin-sticky-col" style={{ lineHeight: '1.3' }}>
160
+ <div className="xeplr-admin-user-name">{u.name || '\u2014'}</div>
161
+ <div className="xeplr-admin-user-email">{u.email}</div>
162
+ </td>
163
+ {tenants.map(function(t) {
164
+ var assigned = isUserAssignedTenant(u, t.id);
165
+ return (
166
+ <td key={t.id} className="xeplr-admin-cell">
167
+ <label className="xeplr-admin-toggle">
168
+ <input type="checkbox" checked={assigned} onChange={function() { handleAssignUser(u.id, t.id, assigned); }} />
169
+ <span className="xeplr-admin-checkmark" />
170
+ </label>
171
+ </td>
172
+ );
173
+ })}
174
+ </tr>
175
+ );
176
+ })
177
+ )}
178
+ </tbody>
179
+ </table>
180
+ </div>
181
+ );
182
+ }
@@ -0,0 +1,77 @@
1
+ import './admin.css';
2
+
3
+ export default function UserRolesMatrixSample({
4
+ users, roles, search, setSearch, loading, error, saving, handleToggle, isAssigned, reload
5
+ }) {
6
+ return (
7
+ <div className="xeplr-admin-container">
8
+ <div className="xeplr-admin-header">
9
+ <h2>User Roles</h2>
10
+ <div className="xeplr-admin-toolbar">
11
+ <input
12
+ id="xeplr-admin-user-search"
13
+ type="text"
14
+ placeholder="Search users by name or email..."
15
+ value={search}
16
+ onChange={function(e) { setSearch(e.target.value); }}
17
+ className="xeplr-admin-search"
18
+ />
19
+ <button type="button" onClick={reload} className="xeplr-admin-btn-secondary">Refresh</button>
20
+ </div>
21
+ </div>
22
+
23
+ {error && <div className="xeplr-admin-alert xeplr-admin-alert-error">{error}</div>}
24
+
25
+ {loading ? (
26
+ <div className="xeplr-admin-loading">Loading...</div>
27
+ ) : (
28
+ <div className="xeplr-admin-matrix-wrapper">
29
+ <table className="xeplr-admin-matrix" role="grid">
30
+ <thead>
31
+ <tr>
32
+ <th className="xeplr-admin-sticky-col">User</th>
33
+ {roles.map(function(role) {
34
+ return <th key={role.id} className="xeplr-admin-role-header">{role.name}</th>;
35
+ })}
36
+ </tr>
37
+ </thead>
38
+ <tbody>
39
+ {users.length === 0 ? (
40
+ <tr><td colSpan={roles.length + 1} className="xeplr-admin-empty">No users found</td></tr>
41
+ ) : (
42
+ users.map(function(user) {
43
+ return (
44
+ <tr key={user.id}>
45
+ <td className="xeplr-admin-sticky-col xeplr-admin-user-cell">
46
+ <div className="xeplr-admin-user-name">{user.name || '—'}</div>
47
+ <div className="xeplr-admin-user-email">{user.email}</div>
48
+ </td>
49
+ {roles.map(function(role) {
50
+ var assigned = isAssigned(user, role.id);
51
+ var key = user.id + ':' + role.id;
52
+ var isSaving = !!saving[key];
53
+ return (
54
+ <td key={role.id} className="xeplr-admin-cell">
55
+ <label className="xeplr-admin-toggle">
56
+ <input
57
+ type="checkbox"
58
+ checked={assigned}
59
+ disabled={isSaving}
60
+ onChange={function() { handleToggle(user.id, role.id, assigned); }}
61
+ />
62
+ <span className={'xeplr-admin-checkmark' + (isSaving ? ' xeplr-admin-saving' : '')} />
63
+ </label>
64
+ </td>
65
+ );
66
+ })}
67
+ </tr>
68
+ );
69
+ })
70
+ )}
71
+ </tbody>
72
+ </table>
73
+ </div>
74
+ )}
75
+ </div>
76
+ );
77
+ }