Initial commit — Diabetix V2

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>
This commit is contained in:
jeremy bayse
2026-04-29 07:01:41 +02:00
commit 26c6d8031c
150 changed files with 19863 additions and 0 deletions

View File

@@ -0,0 +1,113 @@
<script setup>
import { Head, router } from '@inertiajs/vue3';
import DiabetixLayout from '@/Layouts/DiabetixLayout.vue';
import GlucoseChart from '@/Components/Diabetix/GlucoseChart.vue';
const props = defineProps({
period: String,
points: Array,
stats: Object,
history: Array,
target: Object,
});
const tabs = [
{ id: 'day', label: 'Auj.' },
{ id: 'week', label: 'Sem.' },
{ id: 'month', label: 'Mois' },
{ id: 'quarter', label: 'Trim.' },
];
function setPeriod(p) {
router.get(route('stats'), { period: p }, { preserveState: false, preserveScroll: true });
}
function fmt(v) {
return v == null ? '' : String(v);
}
</script>
<template>
<Head title="Statistiques" />
<DiabetixLayout v-slot="{ tok, font }">
<div :style="{ background: tok.white, padding: '14px 20px', borderBottom: '1px solid ' + tok.border }">
<div :style="{ fontFamily: font.title, fontSize: '20px', fontWeight: 600, color: tok.text, marginBottom: '12px' }">Statistiques</div>
<div :style="{ display: 'flex', gap: '3px', background: tok.bgAlt, borderRadius: '12px', padding: '3px' }">
<button v-for="t in tabs" :key="t.id" @click="setPeriod(t.id)"
:style="{
flex: 1, padding: '7px 4px', borderRadius: '10px', border: 'none',
background: period === t.id ? tok.primary : 'transparent',
color: period === t.id ? '#fff' : tok.muted,
fontSize: '12px', fontWeight: period === t.id ? 600 : 400, cursor: 'pointer',
}">{{ t.label }}</button>
</div>
</div>
<div style="padding:14px 20px;display:flex;flex-direction:column;gap:14px;">
<div :style="{ background: tok.white, borderRadius: '20px', padding: '20px', boxShadow: '0 2px 14px rgba(42,53,51,0.06)' }">
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:12px;">
<div :style="{ fontSize: '13px', fontWeight: 600, color: tok.text }">Courbe glycémique</div>
<div style="display:flex;gap:8px;">
<span :style="{ display: 'flex', alignItems: 'center', gap: '3px', fontSize: '9px', color: tok.muted }">
<span :style="{ width: '7px', height: '7px', borderRadius: '2px', background: tok.primary, display: 'inline-block' }" />Dans la cible
</span>
<span :style="{ display: 'flex', alignItems: 'center', gap: '3px', fontSize: '9px', color: tok.muted }">
<span :style="{ width: '7px', height: '7px', borderRadius: '2px', background: tok.amber, display: 'inline-block' }" />Hors cible
</span>
</div>
</div>
<GlucoseChart :data="points" :tok="tok" :width="316" :height="120" :target-min="target.min" :target-max="target.max" />
</div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:10px;">
<div :style="{ background: tok.white, borderRadius: '16px', padding: '14px', boxShadow: '0 2px 10px rgba(42,53,51,0.05)' }">
<div style="font-size:16px;margin-bottom:4px;">📊</div>
<div :style="{ fontSize: '22px', fontWeight: 800, color: tok.text }">{{ fmt(stats.avg) }}</div>
<div :style="{ fontSize: '10px', color: tok.muted, marginTop: '2px' }">Moyenne · mg/dL</div>
</div>
<div :style="{ background: tok.white, borderRadius: '16px', padding: '14px', boxShadow: '0 2px 10px rgba(42,53,51,0.05)' }">
<div style="font-size:16px;margin-bottom:4px;">🎯</div>
<div :style="{ fontSize: '22px', fontWeight: 800, color: tok.primary }">{{ stats.in_range_pct }}%</div>
<div :style="{ fontSize: '10px', color: tok.muted, marginTop: '2px' }">Dans la cible</div>
</div>
<div :style="{ background: tok.white, borderRadius: '16px', padding: '14px', boxShadow: '0 2px 10px rgba(42,53,51,0.05)' }">
<div style="font-size:16px;margin-bottom:4px;"></div>
<div :style="{ fontSize: '22px', fontWeight: 800, color: tok.amber }">{{ fmt(stats.min) }}</div>
<div :style="{ fontSize: '10px', color: tok.muted, marginTop: '2px' }">Minimum · mg/dL</div>
</div>
<div :style="{ background: tok.white, borderRadius: '16px', padding: '14px', boxShadow: '0 2px 10px rgba(42,53,51,0.05)' }">
<div style="font-size:16px;margin-bottom:4px;"></div>
<div :style="{ fontSize: '22px', fontWeight: 800, color: '#d4826a' }">{{ fmt(stats.max) }}</div>
<div :style="{ fontSize: '10px', color: tok.muted, marginTop: '2px' }">Maximum · mg/dL</div>
</div>
</div>
<div :style="{ background: tok.white, borderRadius: '20px', padding: '20px', boxShadow: '0 2px 14px rgba(42,53,51,0.06)' }">
<div :style="{ fontSize: '13px', fontWeight: 600, color: tok.text, marginBottom: '14px' }">Historique</div>
<div v-for="(e, i) in history" :key="e.id"
:style="{
display: 'flex', alignItems: 'center', gap: '12px',
paddingBottom: i < history.length - 1 ? '12px' : 0,
borderBottom: i < history.length - 1 ? '1px solid ' + tok.border : 'none',
marginBottom: i < history.length - 1 ? '12px' : 0,
}">
<div style="display:flex;flex-direction:column;align-items:center;width:12px;">
<div :style="{ width: '10px', height: '10px', borderRadius: '50%', background: e.in_range ? tok.primary : tok.amber }" />
<div v-if="i < history.length - 1" :style="{ width: '1px', height: '24px', background: tok.border, marginTop: '2px' }" />
</div>
<div style="flex:1;">
<div style="display:flex;justify-content:space-between;align-items:center;">
<div>
<span :style="{ fontSize: '14px', fontWeight: 700, color: tok.text }">{{ fmt(e.value) }} </span>
<span :style="{ fontSize: '11px', color: tok.muted }">mg/dL </span>
<span v-if="!e.in_range" :style="{ fontSize: '11px', color: tok.amber, fontWeight: 600 }"></span>
</div>
<span :style="{ fontSize: '11px', color: tok.muted }">{{ e.time }}</span>
</div>
<div :style="{ fontSize: '11px', color: tok.muted }">{{ e.context }}</div>
</div>
</div>
</div>
</div>
</DiabetixLayout>
</template>