feat(codev): skip fiche + annuaire table sticky + page QR code
This commit is contained in:
@@ -9,12 +9,12 @@
|
|||||||
{{ fiches.length }} fiche{{ fiches.length !== 1 ? 's' : '' }} - clique sur un nom pour voir le detail
|
{{ fiches.length }} fiche{{ fiches.length !== 1 ? 's' : '' }} - clique sur un nom pour voir le detail
|
||||||
</template>
|
</template>
|
||||||
</p>
|
</p>
|
||||||
|
<NuxtLink to="/codev/qr" class="qr-link" title="QR Code">[ QR ]</NuxtLink>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<div class="codev-tabs">
|
<div class="codev-tabs">
|
||||||
<button :class="{ active: tab === 'carto' }" @click="tab = 'carto'" type="button">Carto</button>
|
<button :class="{ active: tab === 'carto' }" @click="tab = 'carto'" type="button">Carto</button>
|
||||||
<button :class="{ active: tab === 'besoins' }" @click="tab = 'besoins'" type="button">Besoins</button>
|
<button :class="{ active: tab === 'annuaire' }" @click="tab = 'annuaire'" type="button">Annuaire</button>
|
||||||
<button :class="{ active: tab === 'competences' }" @click="tab = 'competences'" type="button">Compétences</button>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="tab === 'carto'">
|
<div v-if="tab === 'carto'">
|
||||||
@@ -81,22 +81,32 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-else-if="tab === 'besoins'" class="list-view">
|
<div v-else-if="tab === 'annuaire'" class="annuaire-wrap">
|
||||||
<div v-for="f in fiches" :key="f.id" class="list-card">
|
|
||||||
<div class="list-card-name">{{ f.nom }}</div>
|
<div v-if="fiches.length === 0" class="list-empty">
|
||||||
<p class="list-card-text">{{ f.besoin }}</p>
|
Aucune fiche. <NuxtLink to="/codev/fiche">Ajouter la mienne</NuxtLink>
|
||||||
<NuxtLink :to="`/codev/fiche?id=${f.id}`" class="list-card-link">Modifier</NuxtLink>
|
|
||||||
</div>
|
|
||||||
<div v-if="fiches.length === 0" class="list-empty">Aucune fiche. <NuxtLink to="/codev/fiche">Ajouter la mienne</NuxtLink></div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-else-if="tab === 'competences'" class="list-view">
|
<div v-else class="annuaire-scroll">
|
||||||
<div v-for="f in fiches" :key="f.id" class="list-card">
|
<table class="annuaire-table">
|
||||||
<div class="list-card-name">{{ f.nom }}</div>
|
<thead>
|
||||||
<p class="list-card-text">{{ f.offre }}</p>
|
<tr>
|
||||||
<NuxtLink :to="`/codev/fiche?id=${f.id}`" class="list-card-link">Modifier</NuxtLink>
|
<th class="col-nom">Prénom</th>
|
||||||
|
<th class="col-besoin">Besoin</th>
|
||||||
|
<th class="col-offre">Ce que j'offre</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
<tr v-for="f in fiches" :key="f.id" @click="navigateTo(`/codev/fiche?id=${f.id}`)" class="annuaire-row">
|
||||||
|
<td class="col-nom">{{ f.nom }}</td>
|
||||||
|
<td class="col-besoin">{{ f.besoin }}</td>
|
||||||
|
<td class="col-offre">{{ f.offre }}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
</div>
|
</div>
|
||||||
<div v-if="fiches.length === 0" class="list-empty">Aucune fiche. <NuxtLink to="/codev/fiche">Ajouter la mienne</NuxtLink></div>
|
|
||||||
|
<p class="annuaire-hint">Clique sur une ligne pour modifier la fiche</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- FAB ajouter une fiche -->
|
<!-- FAB ajouter une fiche -->
|
||||||
@@ -141,7 +151,7 @@ const fiches = computed(() => data.value?.list ?? [])
|
|||||||
const matches = ref<CodevMatch[]>([])
|
const matches = ref<CodevMatch[]>([])
|
||||||
const mode = ref<'none' | 'solution' | 'alliance' | 'surprise'>('none')
|
const mode = ref<'none' | 'solution' | 'alliance' | 'surprise'>('none')
|
||||||
const showLabels = ref(false)
|
const showLabels = ref(false)
|
||||||
const tab = ref<'carto' | 'besoins' | 'competences'>('carto')
|
const tab = ref<'carto' | 'annuaire'>('carto')
|
||||||
const selectedFiche = ref<CodevFiche | null>(null)
|
const selectedFiche = ref<CodevFiche | null>(null)
|
||||||
const isMobileView = typeof window !== 'undefined' ? window.innerWidth < 600 : false
|
const isMobileView = typeof window !== 'undefined' ? window.innerWidth < 600 : false
|
||||||
|
|
||||||
@@ -403,6 +413,96 @@ function onSelectFiche(id: number) {
|
|||||||
.sheet-enter-active, .sheet-leave-active { transition: opacity 0.2s; }
|
.sheet-enter-active, .sheet-leave-active { transition: opacity 0.2s; }
|
||||||
.sheet-enter-from, .sheet-leave-to { opacity: 0; }
|
.sheet-enter-from, .sheet-leave-to { opacity: 0; }
|
||||||
|
|
||||||
|
/* ── QR link ── */
|
||||||
|
|
||||||
|
.qr-link {
|
||||||
|
font-size: 0.75rem;
|
||||||
|
color: #9ca3af;
|
||||||
|
text-decoration: none;
|
||||||
|
align-self: flex-end;
|
||||||
|
}
|
||||||
|
.qr-link:hover { color: #6b7280; }
|
||||||
|
|
||||||
|
/* ── Annuaire ── */
|
||||||
|
|
||||||
|
.annuaire-wrap {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.annuaire-scroll {
|
||||||
|
overflow-x: auto;
|
||||||
|
-webkit-overflow-scrolling: touch;
|
||||||
|
border: 1px solid #e5e7eb;
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.annuaire-table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
min-width: 480px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.annuaire-table thead tr {
|
||||||
|
background: #f9fafb;
|
||||||
|
border-bottom: 2px solid #e5e7eb;
|
||||||
|
}
|
||||||
|
|
||||||
|
.annuaire-table th {
|
||||||
|
padding: 10px 14px;
|
||||||
|
text-align: left;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
font-weight: 700;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.05em;
|
||||||
|
color: #6b7280;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.annuaire-table td {
|
||||||
|
padding: 12px 14px;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
color: #374151;
|
||||||
|
vertical-align: top;
|
||||||
|
border-bottom: 1px solid #f3f4f6;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.annuaire-row {
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background 0.12s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.annuaire-row:hover { background: #f9fafb; }
|
||||||
|
.annuaire-row:last-child td { border-bottom: none; }
|
||||||
|
|
||||||
|
.col-nom {
|
||||||
|
position: sticky;
|
||||||
|
left: 0;
|
||||||
|
background: inherit;
|
||||||
|
z-index: 1;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #1a1a2e !important;
|
||||||
|
white-space: nowrap;
|
||||||
|
min-width: 80px;
|
||||||
|
border-right: 1px solid #e5e7eb;
|
||||||
|
}
|
||||||
|
|
||||||
|
.annuaire-row:hover .col-nom { background: #f9fafb; }
|
||||||
|
thead tr .col-nom { background: #f9fafb; }
|
||||||
|
|
||||||
|
.col-besoin { min-width: 200px; max-width: 260px; }
|
||||||
|
.col-offre { min-width: 200px; max-width: 260px; }
|
||||||
|
|
||||||
|
.annuaire-hint {
|
||||||
|
font-size: 0.75rem;
|
||||||
|
color: #9ca3af;
|
||||||
|
text-align: center;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
/* ── Mobile ── */
|
/* ── Mobile ── */
|
||||||
|
|
||||||
@media (max-width: 600px) {
|
@media (max-width: 600px) {
|
||||||
|
|||||||
@@ -109,6 +109,10 @@
|
|||||||
{{ isEdit ? (loading ? 'Modification...' : 'Enregistrer les modifications') : (loading ? 'Envoi en cours...' : 'Ajouter ma fiche') }}
|
{{ isEdit ? (loading ? 'Modification...' : 'Enregistrer les modifications') : (loading ? 'Envoi en cours...' : 'Ajouter ma fiche') }}
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
|
<NuxtLink to="/codev/carto" class="skip-link">
|
||||||
|
Voir la carte sans créer de fiche →
|
||||||
|
</NuxtLink>
|
||||||
|
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
@@ -390,6 +394,17 @@ async function submit() {
|
|||||||
cursor: not-allowed;
|
cursor: not-allowed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.skip-link {
|
||||||
|
display: block;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 0.825rem;
|
||||||
|
color: var(--nav-text-muted, #9ca3af);
|
||||||
|
text-decoration: none;
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
padding: 0.5rem;
|
||||||
|
}
|
||||||
|
.skip-link:hover { color: var(--nav-text, #1a1a2e); }
|
||||||
|
|
||||||
/* ── Responsive ── */
|
/* ── Responsive ── */
|
||||||
|
|
||||||
@media (max-width: 480px) {
|
@media (max-width: 480px) {
|
||||||
|
|||||||
94
pages/codev/qr.vue
Normal file
94
pages/codev/qr.vue
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
<template>
|
||||||
|
<div class="qr-page">
|
||||||
|
<div class="qr-card">
|
||||||
|
<h1>Co-développement</h1>
|
||||||
|
<p class="qr-subtitle">Scanne pour rejoindre la session</p>
|
||||||
|
|
||||||
|
<img
|
||||||
|
:src="`https://api.qrserver.com/v1/create-qr-code/?size=280x280&data=${encodeURIComponent(APP_URL)}&bgcolor=ffffff&color=1B4436&margin=2`"
|
||||||
|
alt="QR code aep.trans-former.fr/codev"
|
||||||
|
class="qr-img"
|
||||||
|
width="280"
|
||||||
|
height="280"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<p class="qr-url">{{ APP_URL }}</p>
|
||||||
|
<p class="qr-password">Mot de passe : <strong>merci</strong></p>
|
||||||
|
|
||||||
|
<a :href="`https://api.qrserver.com/v1/create-qr-code/?size=600x600&data=${encodeURIComponent(APP_URL)}&bgcolor=ffffff&color=1B4436&margin=2`"
|
||||||
|
download="codev-qr.png"
|
||||||
|
class="qr-download"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
Télécharger le QR code
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
const APP_URL = 'https://aep.trans-former.fr/codev'
|
||||||
|
useHead({ title: 'QR Code — Co-développement' })
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.qr-page {
|
||||||
|
min-height: 100vh;
|
||||||
|
background: var(--nav-bg, #fafafa);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 2rem 1rem;
|
||||||
|
}
|
||||||
|
.qr-card {
|
||||||
|
background: white;
|
||||||
|
border-radius: 16px;
|
||||||
|
padding: 2rem;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: 1rem;
|
||||||
|
max-width: 360px;
|
||||||
|
width: 100%;
|
||||||
|
box-shadow: 0 4px 24px rgba(0,0,0,0.08);
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
.qr-card h1 {
|
||||||
|
font-size: 1.25rem;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #1a1a2e;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
.qr-subtitle {
|
||||||
|
font-size: 0.9rem;
|
||||||
|
color: #6b7280;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
.qr-img {
|
||||||
|
border-radius: 8px;
|
||||||
|
border: 2px solid #e5e7eb;
|
||||||
|
}
|
||||||
|
.qr-url {
|
||||||
|
font-size: 0.8rem;
|
||||||
|
color: #9ca3af;
|
||||||
|
margin: 0;
|
||||||
|
font-family: monospace;
|
||||||
|
}
|
||||||
|
.qr-password {
|
||||||
|
font-size: 0.95rem;
|
||||||
|
color: #374151;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
.qr-download {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 10px 20px;
|
||||||
|
background: #1B4436;
|
||||||
|
color: white;
|
||||||
|
border-radius: 8px;
|
||||||
|
text-decoration: none;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
font-weight: 600;
|
||||||
|
transition: opacity 0.15s;
|
||||||
|
}
|
||||||
|
.qr-download:hover { opacity: 0.88; }
|
||||||
|
</style>
|
||||||
Reference in New Issue
Block a user