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>
74 lines
4.2 KiB
Vue
74 lines
4.2 KiB
Vue
<script setup>
|
|
import { ref, watch, nextTick } from 'vue';
|
|
import { Head, useForm } from '@inertiajs/vue3';
|
|
import DiabetixLayout from '@/Layouts/DiabetixLayout.vue';
|
|
import CoachAvatar from '@/Components/Diabetix/CoachAvatar.vue';
|
|
|
|
const props = defineProps({
|
|
messages: Array,
|
|
});
|
|
|
|
const form = useForm({ body: '' });
|
|
const listRef = ref(null);
|
|
|
|
function send() {
|
|
if (!form.body.trim()) return;
|
|
form.post(route('chat.store'), {
|
|
preserveScroll: true,
|
|
onSuccess: () => form.reset('body'),
|
|
});
|
|
}
|
|
|
|
watch(() => props.messages.length, async () => {
|
|
await nextTick();
|
|
if (listRef.value) listRef.value.scrollTop = listRef.value.scrollHeight;
|
|
}, { immediate: true });
|
|
</script>
|
|
|
|
<template>
|
|
<Head title="Coach IA" />
|
|
<DiabetixLayout v-slot="{ tok, font }">
|
|
<div :style="{ display: 'flex', flexDirection: 'column', height: 'calc(100vh - 78px)', background: tok.bg }">
|
|
<div :style="{ background: tok.white, padding: '12px 16px', display: 'flex', alignItems: 'center', gap: '10px', borderBottom: '1px solid ' + tok.border, flexShrink: 0 }">
|
|
<CoachAvatar :size="36" :tok="tok" />
|
|
<div>
|
|
<div :style="{ fontFamily: font.title, fontSize: '16px', fontWeight: 600, color: tok.text }">Coach IA</div>
|
|
<div :style="{ fontSize: '11px', color: tok.primary }">🟢 En ligne · Répond en quelques secondes</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div ref="listRef" style="flex:1;overflow-y:auto;padding:16px;display:flex;flex-direction:column;gap:12px;">
|
|
<div v-for="(msg, i) in messages" :key="msg.id ?? i"
|
|
:style="{ display: 'flex', flexDirection: 'column', alignItems: msg.sender === 'coach' ? 'flex-start' : 'flex-end', gap: '5px' }">
|
|
<div :style="{ display: 'flex', alignItems: 'flex-end', gap: '8px', flexDirection: msg.sender === 'coach' ? 'row' : 'row-reverse' }">
|
|
<CoachAvatar v-if="msg.sender === 'coach'" :size="28" :tok="tok" />
|
|
<div :style="{
|
|
maxWidth: '78%',
|
|
background: msg.sender === 'coach' ? tok.light : tok.primary,
|
|
color: msg.sender === 'coach' ? tok.text : '#fff',
|
|
borderRadius: msg.sender === 'coach' ? '14px 14px 14px 3px' : '14px 14px 3px 14px',
|
|
padding: '10px 14px', fontSize: '13px', lineHeight: 1.5,
|
|
}">{{ msg.body }}</div>
|
|
</div>
|
|
<div v-if="msg.actions && msg.actions.length" :style="{ display: 'flex', flexWrap: 'wrap', gap: '6px', marginLeft: msg.sender === 'coach' ? '36px' : 0 }">
|
|
<button v-for="(a, j) in msg.actions" :key="j" :style="{
|
|
padding: '5px 13px', borderRadius: '20px', background: tok.white,
|
|
border: '1.5px solid ' + tok.primary, fontSize: '11px',
|
|
color: tok.primary, fontWeight: 500, cursor: 'pointer',
|
|
}">{{ a }}</button>
|
|
</div>
|
|
<div :style="{ fontSize: '9px', color: tok.muted, paddingLeft: msg.sender === 'coach' ? '36px' : 0 }">{{ msg.time }}</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div :style="{ padding: '10px 14px', background: tok.white, borderTop: '1px solid ' + tok.border, flexShrink: 0, display: 'flex', gap: '8px', alignItems: 'center' }">
|
|
<input v-model="form.body" @keydown.enter="send"
|
|
placeholder="Écrire au coach..."
|
|
:style="{ flex: 1, padding: '10px 14px', borderRadius: '20px', border: '1.5px solid ' + tok.border, background: tok.bg, fontSize: '13px', color: tok.text, outline: 'none' }" />
|
|
<button @click="send" :disabled="form.processing"
|
|
:style="{ width: '38px', height: '38px', borderRadius: '50%', background: tok.primary, border: 'none', color: '#fff', fontSize: '17px', cursor: 'pointer', display: 'flex', alignItems: 'center', justifyContent: 'center', opacity: form.processing ? 0.6 : 1 }">↑</button>
|
|
</div>
|
|
</div>
|
|
</DiabetixLayout>
|
|
</template>
|