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:
73
app/resources/js/Pages/Diabetix/Chat.vue
Normal file
73
app/resources/js/Pages/Diabetix/Chat.vue
Normal file
@@ -0,0 +1,73 @@
|
||||
<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>
|
||||
Reference in New Issue
Block a user