feat: Attribution de rôles multiples par utilisateur
Les utilisateurs Classeo étaient limités à un seul rôle, alors que dans la réalité scolaire un directeur peut aussi être enseignant, ou un parent peut avoir un rôle vie scolaire. Cette limitation obligeait à créer des comptes distincts par fonction. Le modèle User supporte désormais plusieurs rôles simultanés avec basculement via le header. L'admin peut attribuer/retirer des rôles depuis l'interface de gestion, avec des garde-fous : pas d'auto- destitution, pas d'escalade de privilèges (seul SUPER_ADMIN peut attribuer SUPER_ADMIN), vérification du statut actif pour le switch de rôle, et TTL explicite sur le cache de rôle actif.
This commit is contained in:
@@ -0,0 +1,113 @@
|
||||
<script lang="ts">
|
||||
import {
|
||||
getRoles,
|
||||
getActiveRole,
|
||||
hasMultipleRoles,
|
||||
switchTo,
|
||||
getIsSwitching
|
||||
} from '$features/roles/roleContext.svelte';
|
||||
|
||||
let selectedRole = $state(getActiveRole() ?? '');
|
||||
let switchError = $state<string | null>(null);
|
||||
|
||||
// Sync selected role when active role changes externally
|
||||
$effect(() => {
|
||||
const active = getActiveRole();
|
||||
if (active) {
|
||||
selectedRole = active;
|
||||
}
|
||||
});
|
||||
|
||||
async function handleSwitch() {
|
||||
if (selectedRole === getActiveRole()) return;
|
||||
|
||||
switchError = null;
|
||||
const success = await switchTo(selectedRole);
|
||||
|
||||
if (!success) {
|
||||
switchError = 'Erreur lors du basculement';
|
||||
selectedRole = getActiveRole() ?? '';
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if hasMultipleRoles()}
|
||||
<div class="role-switcher">
|
||||
<label for="role-switcher" class="role-switcher-label">Vue :</label>
|
||||
<select
|
||||
id="role-switcher"
|
||||
bind:value={selectedRole}
|
||||
onchange={handleSwitch}
|
||||
disabled={getIsSwitching()}
|
||||
class="role-switcher-select"
|
||||
>
|
||||
{#each getRoles() as role}
|
||||
<option value={role.value}>{role.label}</option>
|
||||
{/each}
|
||||
</select>
|
||||
{#if getIsSwitching()}
|
||||
<span class="role-switcher-spinner"></span>
|
||||
{/if}
|
||||
{#if switchError}
|
||||
<span class="role-switcher-error">{switchError}</span>
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
.role-switcher {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
.role-switcher-label {
|
||||
font-size: 0.75rem;
|
||||
font-weight: 500;
|
||||
color: var(--text-secondary, #64748b);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.role-switcher-select {
|
||||
padding: 0.375rem 0.75rem;
|
||||
font-size: 0.8125rem;
|
||||
font-weight: 500;
|
||||
color: var(--accent-primary, #0ea5e9);
|
||||
background: var(--accent-primary-light, #e0f2fe);
|
||||
border: 1px solid var(--accent-primary, #0ea5e9);
|
||||
border-radius: 0.5rem;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
appearance: auto;
|
||||
}
|
||||
|
||||
.role-switcher-select:hover:not(:disabled) {
|
||||
background: var(--accent-primary, #0ea5e9);
|
||||
color: white;
|
||||
}
|
||||
|
||||
.role-switcher-select:disabled {
|
||||
opacity: 0.6;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
|
||||
.role-switcher-spinner {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
border: 2px solid var(--border-subtle, #e2e8f0);
|
||||
border-top-color: var(--accent-primary, #0ea5e9);
|
||||
border-radius: 50%;
|
||||
animation: spin 0.8s linear infinite;
|
||||
}
|
||||
|
||||
.role-switcher-error {
|
||||
font-size: 0.75rem;
|
||||
color: var(--color-alert, #ef4444);
|
||||
}
|
||||
|
||||
@keyframes spin {
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user