@astratra/saas-kit-ui 0.1.0 → 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.
package/package.json
CHANGED
|
@@ -4,8 +4,8 @@ import styles from './Layout.module.css';
|
|
|
4
4
|
|
|
5
5
|
const NAV_ITEMS = [
|
|
6
6
|
{ id: 'dashboard', index: '01', label: 'Dashboard' },
|
|
7
|
-
{ id: 'users', index: '02', label: '
|
|
8
|
-
{ id: 'settings', index: '03', label: '
|
|
7
|
+
{ id: 'users', index: '02', label: 'Utilisateurs' },
|
|
8
|
+
{ id: 'settings', index: '03', label: 'Parametres' }
|
|
9
9
|
];
|
|
10
10
|
|
|
11
11
|
export default function Layout({ activeView, children, onNavigate }) {
|
|
@@ -18,7 +18,7 @@ export default function Layout({ activeView, children, onNavigate }) {
|
|
|
18
18
|
<img src={logoUrl} alt="Astratra" className={styles.brandMark} />
|
|
19
19
|
<span>
|
|
20
20
|
<strong>Astratra</strong>
|
|
21
|
-
<small>saas_starter@
|
|
21
|
+
<small>saas_starter@v1.0.0</small>
|
|
22
22
|
</span>
|
|
23
23
|
</div>
|
|
24
24
|
|
|
@@ -43,8 +43,8 @@ export default function Layout({ activeView, children, onNavigate }) {
|
|
|
43
43
|
|
|
44
44
|
<div className={styles.account}>
|
|
45
45
|
<span>{user?.email}</span>
|
|
46
|
-
<small>role: {user?.role}</small>
|
|
47
|
-
<button type="button" onClick={logout}>[
|
|
46
|
+
<small>role : {user?.role}</small>
|
|
47
|
+
<button type="button" onClick={logout}>[ deconnexion ]</button>
|
|
48
48
|
</div>
|
|
49
49
|
</aside>
|
|
50
50
|
<main className={styles.content}>{children}</main>
|
|
@@ -28,19 +28,19 @@ export default function Dashboard() {
|
|
|
28
28
|
return (
|
|
29
29
|
<section className={styles.screen}>
|
|
30
30
|
<header className={styles.header}>
|
|
31
|
-
<p>
|
|
31
|
+
<p>Vue d'ensemble</p>
|
|
32
32
|
<h1>Dashboard</h1>
|
|
33
33
|
</header>
|
|
34
34
|
<Notice tone="error">{error}</Notice>
|
|
35
|
-
{!summary && !error ? <p className={styles.muted}>
|
|
35
|
+
{!summary && !error ? <p className={styles.muted}>Chargement du dashboard...</p> : null}
|
|
36
36
|
{summary ? (
|
|
37
37
|
<div className={styles.grid}>
|
|
38
38
|
<article className={styles.statCard}>
|
|
39
|
-
<span>
|
|
39
|
+
<span>Utilisateurs</span>
|
|
40
40
|
<strong>{summary.userCount}</strong>
|
|
41
41
|
</article>
|
|
42
42
|
<article className={styles.wideCard}>
|
|
43
|
-
<span>
|
|
43
|
+
<span>Repartition des roles</span>
|
|
44
44
|
<div className={styles.bars}>
|
|
45
45
|
{roles.map(([role, count]) => (
|
|
46
46
|
<div key={role} className={styles.barRow}>
|
package/src/screens/Login.jsx
CHANGED
|
@@ -30,14 +30,14 @@ export default function Login({ onLoggedIn }) {
|
|
|
30
30
|
<div className={styles.frame} aria-hidden="true" />
|
|
31
31
|
<div className={styles.identity}>
|
|
32
32
|
<span className={styles.tag}>astratra/saas-kit</span>
|
|
33
|
-
<span className={styles.tagMuted}>
|
|
33
|
+
<span className={styles.tagMuted}>v1.0.0</span>
|
|
34
34
|
</div>
|
|
35
35
|
<h1 id="login-title">
|
|
36
|
-
<span className={styles.prompt}>></span>
|
|
36
|
+
<span className={styles.prompt}>></span> connexion
|
|
37
37
|
<span className={styles.cursor} aria-hidden="true" />
|
|
38
38
|
</h1>
|
|
39
39
|
<p className={styles.copy}>
|
|
40
|
-
//
|
|
40
|
+
// connectez-vous avec le compte owner de demo pour explorer l'API SaaS generique
|
|
41
41
|
</p>
|
|
42
42
|
<Notice tone="error">{error}</Notice>
|
|
43
43
|
<form className={styles.form} onSubmit={handleSubmit}>
|
|
@@ -46,10 +46,10 @@ export default function Login({ onLoggedIn }) {
|
|
|
46
46
|
<input value={email} onChange={(event) => setEmail(event.target.value)} type="email" required />
|
|
47
47
|
</label>
|
|
48
48
|
<label>
|
|
49
|
-
|
|
49
|
+
mot de passe
|
|
50
50
|
<input value={password} onChange={(event) => setPassword(event.target.value)} type="password" required />
|
|
51
51
|
</label>
|
|
52
|
-
<button type="submit" disabled={loading}>{loading ? '
|
|
52
|
+
<button type="submit" disabled={loading}>{loading ? 'connexion...' : '[ se connecter ]'}</button>
|
|
53
53
|
</form>
|
|
54
54
|
</section>
|
|
55
55
|
</main>
|
package/src/screens/Settings.jsx
CHANGED
|
@@ -17,7 +17,7 @@ export default function Settings() {
|
|
|
17
17
|
setSettings(data || {});
|
|
18
18
|
setDrafts(Object.fromEntries(Object.entries(data || {}).map(([key, value]) => [key, JSON.stringify(value)])));
|
|
19
19
|
} catch (apiError) {
|
|
20
|
-
setError(apiError.status === 403 ? '
|
|
20
|
+
setError(apiError.status === 403 ? 'Vous n etes pas autorise a gerer les parametres.' : apiError.message);
|
|
21
21
|
}
|
|
22
22
|
}
|
|
23
23
|
|
|
@@ -42,9 +42,9 @@ export default function Settings() {
|
|
|
42
42
|
});
|
|
43
43
|
setSettings({ ...settings, [updated.key]: updated.value });
|
|
44
44
|
setDrafts({ ...drafts, [updated.key]: JSON.stringify(updated.value) });
|
|
45
|
-
setSuccess(`${updated.key}
|
|
45
|
+
setSuccess(`${updated.key} enregistre.`);
|
|
46
46
|
} catch (apiError) {
|
|
47
|
-
setError(apiError.status === 403 ? '
|
|
47
|
+
setError(apiError.status === 403 ? 'Vous n etes pas autorise a modifier les parametres.' : apiError.message);
|
|
48
48
|
}
|
|
49
49
|
}
|
|
50
50
|
|
|
@@ -54,11 +54,11 @@ export default function Settings() {
|
|
|
54
54
|
<section className={styles.screen}>
|
|
55
55
|
<header className={styles.header}>
|
|
56
56
|
<p>Configuration</p>
|
|
57
|
-
<h1>
|
|
57
|
+
<h1>Parametres</h1>
|
|
58
58
|
</header>
|
|
59
59
|
<Notice tone="error">{error}</Notice>
|
|
60
60
|
<Notice tone="success">{success}</Notice>
|
|
61
|
-
{rows.length === 0 && !error ? <p className={styles.muted}>
|
|
61
|
+
{rows.length === 0 && !error ? <p className={styles.muted}>Aucun parametre pour le moment.</p> : null}
|
|
62
62
|
<div className={styles.settingsList}>
|
|
63
63
|
{rows.map(([key]) => (
|
|
64
64
|
<div className={styles.settingRow} key={key}>
|
|
@@ -69,7 +69,7 @@ export default function Settings() {
|
|
|
69
69
|
onChange={(event) => setDrafts({ ...drafts, [key]: event.target.value })}
|
|
70
70
|
/>
|
|
71
71
|
</label>
|
|
72
|
-
<button type="button" onClick={() => saveSetting(key)}>
|
|
72
|
+
<button type="button" onClick={() => saveSetting(key)}>Enregistrer</button>
|
|
73
73
|
</div>
|
|
74
74
|
))}
|
|
75
75
|
</div>
|
package/src/screens/Users.jsx
CHANGED
|
@@ -10,6 +10,7 @@ export default function Users() {
|
|
|
10
10
|
const [success, setSuccess] = useState('');
|
|
11
11
|
const [form, setForm] = useState({ email: '', role: 'member', password: 'password' });
|
|
12
12
|
const [loading, setLoading] = useState(true);
|
|
13
|
+
const [authorized, setAuthorized] = useState(true);
|
|
13
14
|
|
|
14
15
|
async function loadUsers() {
|
|
15
16
|
setLoading(true);
|
|
@@ -17,8 +18,14 @@ export default function Users() {
|
|
|
17
18
|
try {
|
|
18
19
|
const data = await request('/users?limit=50&offset=0');
|
|
19
20
|
setUsers(data.items || []);
|
|
21
|
+
setAuthorized(true);
|
|
20
22
|
} catch (apiError) {
|
|
21
|
-
|
|
23
|
+
if (apiError.status === 403) {
|
|
24
|
+
setAuthorized(false);
|
|
25
|
+
setError('Vous n etes pas autorise a gerer les utilisateurs.');
|
|
26
|
+
} else {
|
|
27
|
+
setError(apiError.message);
|
|
28
|
+
}
|
|
22
29
|
} finally {
|
|
23
30
|
setLoading(false);
|
|
24
31
|
}
|
|
@@ -35,10 +42,10 @@ export default function Users() {
|
|
|
35
42
|
try {
|
|
36
43
|
await request('/users', { method: 'POST', body: form });
|
|
37
44
|
setForm({ email: '', role: 'member', password: 'password' });
|
|
38
|
-
setSuccess('
|
|
45
|
+
setSuccess('Utilisateur cree.');
|
|
39
46
|
await loadUsers();
|
|
40
47
|
} catch (apiError) {
|
|
41
|
-
setError(apiError.status === 403 ? '
|
|
48
|
+
setError(apiError.status === 403 ? 'Vous n etes pas autorise a creer des utilisateurs.' : apiError.message);
|
|
42
49
|
}
|
|
43
50
|
}
|
|
44
51
|
|
|
@@ -46,55 +53,59 @@ export default function Users() {
|
|
|
46
53
|
<section className={styles.screen}>
|
|
47
54
|
<header className={styles.header}>
|
|
48
55
|
<p>Administration</p>
|
|
49
|
-
<h1>
|
|
56
|
+
<h1>Utilisateurs</h1>
|
|
50
57
|
</header>
|
|
51
58
|
<Notice tone="error">{error}</Notice>
|
|
52
59
|
<Notice tone="success">{success}</Notice>
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
<
|
|
83
|
-
<
|
|
84
|
-
<
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
<
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
60
|
+
{authorized ? (
|
|
61
|
+
<>
|
|
62
|
+
<form className={styles.inlineForm} onSubmit={createUser}>
|
|
63
|
+
<input
|
|
64
|
+
aria-label="Email"
|
|
65
|
+
placeholder="email@example.test"
|
|
66
|
+
type="email"
|
|
67
|
+
value={form.email}
|
|
68
|
+
onChange={(event) => setForm({ ...form, email: event.target.value })}
|
|
69
|
+
required
|
|
70
|
+
/>
|
|
71
|
+
<select
|
|
72
|
+
aria-label="Role"
|
|
73
|
+
value={form.role}
|
|
74
|
+
onChange={(event) => setForm({ ...form, role: event.target.value })}
|
|
75
|
+
>
|
|
76
|
+
<option value="member">member</option>
|
|
77
|
+
<option value="admin">admin</option>
|
|
78
|
+
<option value="owner">owner</option>
|
|
79
|
+
</select>
|
|
80
|
+
<input
|
|
81
|
+
aria-label="Mot de passe"
|
|
82
|
+
value={form.password}
|
|
83
|
+
onChange={(event) => setForm({ ...form, password: event.target.value })}
|
|
84
|
+
required
|
|
85
|
+
/>
|
|
86
|
+
<button type="submit">Creer un utilisateur</button>
|
|
87
|
+
</form>
|
|
88
|
+
{loading ? <p className={styles.muted}>Chargement des utilisateurs...</p> : null}
|
|
89
|
+
<div className={styles.tableWrap}>
|
|
90
|
+
<table>
|
|
91
|
+
<thead>
|
|
92
|
+
<tr>
|
|
93
|
+
<th>Email</th>
|
|
94
|
+
<th>Role</th>
|
|
95
|
+
</tr>
|
|
96
|
+
</thead>
|
|
97
|
+
<tbody>
|
|
98
|
+
{users.map((user) => (
|
|
99
|
+
<tr key={user.id}>
|
|
100
|
+
<td>{user.email}</td>
|
|
101
|
+
<td>{user.role || 'inconnu'}</td>
|
|
102
|
+
</tr>
|
|
103
|
+
))}
|
|
104
|
+
</tbody>
|
|
105
|
+
</table>
|
|
106
|
+
</div>
|
|
107
|
+
</>
|
|
108
|
+
) : null}
|
|
98
109
|
</section>
|
|
99
110
|
);
|
|
100
111
|
}
|