Multi-tenancy : espaces de travail + fix auth reload (rate limiter OPTIONS)

- Modèles Organization + OrgMember, migration Alembic (SQLite compatible)
- organization_id nullable sur Document, Decision, Mandate, VotingProtocol
- Service, schéma, router /organizations + dependency get_active_org_id
- Seed : Duniter G1 + Axiom Team ; tout le contenu seed attaché à Duniter G1
- Backend : list/create filtrés par header X-Organization
- Frontend : store organizations, WorkspaceSelector réel, useApi injecte l'org
- Fix critique : rate_limiter exclut les requêtes OPTIONS (CORS preflight)
  → résout le bug "Failed to fetch /auth/me" au reload (429 sur preflight)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Yvv
2026-04-23 15:17:14 +02:00
parent 224e5b0f5e
commit 79e468b40f
31 changed files with 1296 additions and 159 deletions

View File

@@ -20,6 +20,8 @@ interface ToolSection {
tools: Tool[]
}
const expandSocio = ref(false)
const sections: ToolSection[] = [
{
key: 'documents',
@@ -149,6 +151,29 @@ const sections: ToolSection[] = [
</div>
</div>
</div>
<!-- Election sociocratique modalité d'élection, accessible depuis mandats -->
<div v-if="section.key === 'mandats'" class="socio-expand">
<button class="socio-expand__trigger" @click="expandSocio = !expandSocio">
<div class="socio-expand__icon">
<UIcon name="i-lucide-users" class="text-sm" />
</div>
<div class="socio-expand__info">
<span class="socio-expand__title">Élection sociocratique</span>
<span class="socio-expand__meta">6 étapes · clarification · consentement collectif</span>
</div>
<span class="socio-expand__tag">Modalité d'élection</span>
<UIcon
:name="expandSocio ? 'i-lucide-chevron-up' : 'i-lucide-chevron-down'"
class="socio-expand__toggle"
/>
</button>
<Transition name="socio-expand">
<div v-if="expandSocio" class="socio-expand__content">
<SocioElection />
</div>
</Transition>
</div>
</div>
</div>
</div>
@@ -329,4 +354,99 @@ const sections: ToolSection[] = [
opacity: 1;
color: var(--section-color);
}
/* --- Élection sociocratique expandable --- */
.socio-expand {
margin-top: 0.5rem;
border-radius: 16px;
overflow: hidden;
background: var(--mood-surface);
}
.socio-expand__trigger {
display: flex;
align-items: center;
gap: 0.75rem;
width: 100%;
padding: 1rem 1.125rem;
cursor: pointer;
background: none;
text-align: left;
transition: background 0.12s;
}
.socio-expand__trigger:hover { background: var(--mood-accent-soft); }
.socio-expand__icon {
width: 2rem;
height: 2rem;
flex-shrink: 0;
display: flex;
align-items: center;
justify-content: center;
border-radius: 10px;
background: color-mix(in srgb, var(--mood-success) 12%, transparent);
color: var(--mood-success);
}
.socio-expand__info {
flex: 1;
min-width: 0;
display: flex;
flex-direction: column;
gap: 0.0625rem;
}
.socio-expand__title {
font-size: 0.9375rem;
font-weight: 700;
color: var(--mood-text);
}
.socio-expand__meta {
font-size: 0.75rem;
color: var(--mood-text-muted);
}
.socio-expand__tag {
font-size: 0.625rem;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.05em;
padding: 3px 8px;
border-radius: 20px;
background: color-mix(in srgb, var(--mood-success) 12%, transparent);
color: var(--mood-success);
white-space: nowrap;
flex-shrink: 0;
}
@media (max-width: 480px) {
.socio-expand__tag { display: none; }
}
.socio-expand__toggle {
flex-shrink: 0;
color: var(--mood-text-muted);
font-size: 0.875rem;
}
.socio-expand__content {
padding: 0 1rem 1rem;
}
.socio-expand-enter-active,
.socio-expand-leave-active {
transition: all 0.25s ease;
overflow: hidden;
}
.socio-expand-enter-from,
.socio-expand-leave-to {
max-height: 0;
opacity: 0;
}
.socio-expand-enter-to,
.socio-expand-leave-from {
max-height: 2000px;
opacity: 1;
}
</style>