Application Laravel 12 + Inertia + Vue 3 + Tailwind. Fonctionnalités : dashboard glycémique, saisie de mesures, courbe SVG, statistiques (jour/semaine/mois/trimestre), défis & badges, chat coach IA (Gemini), paramètres profil avec palette de couleurs, pages auth redessinées, emails transactionnels via Resend avec thème Diabetix. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
118 lines
7.3 KiB
Vue
118 lines
7.3 KiB
Vue
<script setup>
|
|
import { ref, computed } from 'vue';
|
|
import { Head } from '@inertiajs/vue3';
|
|
import DiabetixLayout from '@/Layouts/DiabetixLayout.vue';
|
|
import ProgressBar from '@/Components/Diabetix/ProgressBar.vue';
|
|
import BadgeChip from '@/Components/Diabetix/BadgeChip.vue';
|
|
|
|
const props = defineProps({
|
|
challenges: Array,
|
|
badges: Array,
|
|
level: Object,
|
|
});
|
|
|
|
const tab = ref('defis');
|
|
|
|
const unlocked = computed(() => props.badges.filter(b => b.unlocked));
|
|
const locked = computed(() => props.badges.filter(b => !b.unlocked));
|
|
</script>
|
|
|
|
<template>
|
|
<Head title="Défis & Badges" />
|
|
<DiabetixLayout v-slot="{ tok, font }">
|
|
<div :style="{ background: tok.white, padding: '14px 20px 18px', borderBottom: '1px solid ' + tok.border }">
|
|
<div :style="{ fontFamily: font.title, fontSize: '20px', fontWeight: 600, color: tok.text, marginBottom: '14px' }">Défis & Badges</div>
|
|
|
|
<div :style="{ background: tok.light, borderRadius: '18px', padding: '16px' }">
|
|
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:10px;">
|
|
<div>
|
|
<div :style="{ fontSize: '11px', color: tok.muted }">Niveau actuel</div>
|
|
<div :style="{ fontFamily: font.title, fontSize: '17px', fontWeight: 600, color: tok.text }">{{ level.icon }} {{ level.label }}</div>
|
|
</div>
|
|
<div style="text-align:right;">
|
|
<div :style="{ fontSize: '11px', color: tok.muted }">Points</div>
|
|
<div :style="{ fontSize: '22px', fontWeight: 800, color: tok.amber }">{{ level.points.toLocaleString('fr-FR') }}</div>
|
|
</div>
|
|
</div>
|
|
<ProgressBar :value="level.points" :max="level.next_at" :color="tok.amber" :tok="tok" :height="8" />
|
|
<div style="display:flex;justify-content:space-between;margin-top:5px;">
|
|
<span :style="{ fontSize: '10px', color: tok.muted }">{{ level.icon }} {{ level.label }}</span>
|
|
<span :style="{ fontSize: '10px', color: tok.amber }">{{ level.remaining }} pts → {{ level.next_icon }} {{ level.next_label }}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div style="padding:14px 20px 0;">
|
|
<div :style="{ display: 'flex', gap: '4px', background: tok.bgAlt, borderRadius: '12px', padding: '4px' }">
|
|
<button v-for="t in [['defis','🎯 Défis actifs'],['badges','🏅 Badges']]" :key="t[0]"
|
|
@click="tab = t[0]"
|
|
:style="{
|
|
flex: 1, padding: '8px', borderRadius: '9px', border: 'none',
|
|
background: tab === t[0] ? tok.primary : 'transparent',
|
|
color: tab === t[0] ? '#fff' : tok.muted,
|
|
fontSize: '12px', fontWeight: tab === t[0] ? 600 : 400, cursor: 'pointer',
|
|
}">{{ t[1] }}</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div style="padding:14px 20px;display:flex;flex-direction:column;gap:14px;">
|
|
<template v-if="tab === 'defis'">
|
|
<div v-for="c in challenges" :key="c.id"
|
|
:style="{ background: tok.white, borderRadius: '18px', padding: '16px', boxShadow: '0 2px 12px rgba(42,53,51,0.06)' }">
|
|
<div style="display:flex;align-items:flex-start;gap:12px;">
|
|
<div style="font-size:28px;">{{ c.icon }}</div>
|
|
<div style="flex:1;">
|
|
<div style="display:flex;justify-content:space-between;align-items:center;">
|
|
<div :style="{ fontSize: '14px', fontWeight: 600, color: tok.text }">{{ c.title }}</div>
|
|
<div :style="{ background: tok.amberLight, borderRadius: '20px', padding: '3px 8px', fontSize: '10px', color: tok.amber, fontWeight: 600 }">+{{ c.points }} pts</div>
|
|
</div>
|
|
<div :style="{ fontSize: '11px', color: tok.muted, marginBottom: '10px' }">{{ c.description }}</div>
|
|
<ProgressBar :value="c.progress" :tok="tok" :height="7" />
|
|
<div style="display:flex;justify-content:flex-end;margin-top:5px;">
|
|
<span :style="{ fontSize: '11px', color: tok.primary }">{{ c.progress }}%</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<template v-else>
|
|
<template v-if="unlocked.length">
|
|
<div :style="{ fontSize: '11px', color: tok.muted, fontWeight: 700, letterSpacing: '0.8px' }">DÉBLOQUÉS ({{ unlocked.length }})</div>
|
|
<div v-for="b in unlocked" :key="b.id"
|
|
:style="{ background: tok.white, borderRadius: '16px', padding: '14px 16px', boxShadow: '0 2px 10px rgba(42,53,51,0.06)', display: 'flex', alignItems: 'center', gap: '14px' }">
|
|
<div :style="{
|
|
width: '48px', height: '48px', borderRadius: '50%', flexShrink: 0,
|
|
background: tok.light, border: '2px solid ' + tok.primary,
|
|
display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: '22px',
|
|
}">{{ b.icon }}</div>
|
|
<div style="flex:1;min-width:0;">
|
|
<div :style="{ fontSize: '13px', fontWeight: 700, color: tok.text }">{{ b.label }}</div>
|
|
<div :style="{ fontSize: '11px', color: tok.muted, marginTop: '2px', lineHeight: 1.4 }">{{ b.criteria }}</div>
|
|
</div>
|
|
<div :style="{ fontSize: '16px', flexShrink: 0 }">✅</div>
|
|
</div>
|
|
</template>
|
|
|
|
<template v-if="locked.length">
|
|
<div :style="{ fontSize: '11px', color: tok.muted, fontWeight: 700, letterSpacing: '0.8px', marginTop: unlocked.length ? '4px' : 0 }">À DÉBLOQUER ({{ locked.length }})</div>
|
|
<div v-for="b in locked" :key="b.id"
|
|
:style="{ background: tok.white, borderRadius: '16px', padding: '14px 16px', boxShadow: '0 2px 10px rgba(42,53,51,0.04)', display: 'flex', alignItems: 'center', gap: '14px', opacity: 0.7 }">
|
|
<div :style="{
|
|
width: '48px', height: '48px', borderRadius: '50%', flexShrink: 0,
|
|
background: tok.bgAlt, border: '2px solid ' + tok.border,
|
|
display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: '22px',
|
|
filter: 'grayscale(1)',
|
|
}">{{ b.icon }}</div>
|
|
<div style="flex:1;min-width:0;">
|
|
<div :style="{ fontSize: '13px', fontWeight: 600, color: tok.muted }">{{ b.label }}</div>
|
|
<div :style="{ fontSize: '11px', color: tok.muted, marginTop: '2px', lineHeight: 1.4 }">{{ b.criteria }}</div>
|
|
</div>
|
|
<div :style="{ fontSize: '16px', flexShrink: 0 }">🔒</div>
|
|
</div>
|
|
</template>
|
|
</template>
|
|
</div>
|
|
</DiabetixLayout>
|
|
</template>
|