Pages corrections journal
This commit is contained in:
parent
721e8b6030
commit
926d78a379
@ -5,6 +5,7 @@ import { Image, FileText, Link, List, Plus } from 'lucide-react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { useSession } from 'next-auth/react';
|
||||
import { noteContentCache } from '@/lib/cache-utils';
|
||||
import { HealthForm } from './health-form';
|
||||
|
||||
interface Note {
|
||||
id: string;
|
||||
@ -118,6 +119,12 @@ export const Editor: React.FC<EditorProps> = ({ note, onSave, currentFolder = 'N
|
||||
debouncedSave();
|
||||
};
|
||||
|
||||
// Handle content change from HealthForm (direct string update)
|
||||
const handleHealthContentChange = (newContent: string) => {
|
||||
setContent(newContent);
|
||||
debouncedSave();
|
||||
};
|
||||
|
||||
const debouncedSave = () => {
|
||||
if (saveTimeout.current) {
|
||||
clearTimeout(saveTimeout.current);
|
||||
@ -264,18 +271,28 @@ export const Editor: React.FC<EditorProps> = ({ note, onSave, currentFolder = 'N
|
||||
)}
|
||||
|
||||
{/* Editor Area */}
|
||||
<div className="flex-1 p-4">
|
||||
<div className="flex-1 overflow-y-auto">
|
||||
{isLoading ? (
|
||||
<div className="flex items-center justify-center h-full">
|
||||
<div className="animate-spin rounded-full h-8 w-8 border-t-2 border-b-2 border-primary"></div>
|
||||
</div>
|
||||
) : (
|
||||
<textarea
|
||||
value={content ?? ''}
|
||||
onChange={handleContentChange}
|
||||
placeholder="Ecrire..."
|
||||
className="w-full h-full resize-none focus:outline-none bg-transparent text-carnet-text-primary placeholder-carnet-text-muted"
|
||||
) : currentFolder === 'Health' ? (
|
||||
// Use HealthForm for Health folder
|
||||
<HealthForm
|
||||
content={content}
|
||||
onContentChange={handleHealthContentChange}
|
||||
date={note?.lastModified}
|
||||
/>
|
||||
) : (
|
||||
<div className="p-4">
|
||||
<textarea
|
||||
value={content ?? ''}
|
||||
onChange={handleContentChange}
|
||||
placeholder="Ecrire..."
|
||||
className="w-full h-full resize-none focus:outline-none bg-transparent text-carnet-text-primary placeholder-carnet-text-muted"
|
||||
style={{ minHeight: '400px' }}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{isSaving && (
|
||||
|
||||
416
components/carnet/health-form.tsx
Normal file
416
components/carnet/health-form.tsx
Normal file
@ -0,0 +1,416 @@
|
||||
"use client";
|
||||
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import { Calendar, Activity, Heart, Pill, Droplet, Dumbbell, FileText } from 'lucide-react';
|
||||
|
||||
interface HealthData {
|
||||
// 1. Informations de base
|
||||
date?: string;
|
||||
poids?: number;
|
||||
temperature?: number;
|
||||
tension?: string; // Format: "120/80"
|
||||
frequenceCardiaque?: number;
|
||||
|
||||
// 2. État de santé / symptômes
|
||||
maladie?: string;
|
||||
symptomes?: string;
|
||||
niveauDouleur?: number; // 0-10
|
||||
niveauEnergie?: 'faible' | 'moyen' | 'bon';
|
||||
qualiteSommeil?: 'mauvais' | 'moyen' | 'bon' | number; // ou 1-5
|
||||
|
||||
// 3. Médicaments et soins
|
||||
medicaments?: string;
|
||||
traitementEnCours?: boolean;
|
||||
consultationMedicale?: boolean;
|
||||
notesMedicales?: string;
|
||||
|
||||
// 4. Habitudes & mode de vie
|
||||
hydratation?: number; // verres d'eau
|
||||
activitePhysique?: 'aucune' | 'légère' | 'intense';
|
||||
dureeActivite?: number; // minutes
|
||||
alimentationParticuliere?: boolean;
|
||||
commentaireAlimentation?: string;
|
||||
stress?: 'faible' | 'moyen' | 'élevé';
|
||||
|
||||
// 5. Champ libre
|
||||
notesPersonnelles?: string;
|
||||
}
|
||||
|
||||
interface HealthFormProps {
|
||||
content: string;
|
||||
onContentChange: (content: string) => void;
|
||||
date?: string;
|
||||
}
|
||||
|
||||
export const HealthForm: React.FC<HealthFormProps> = ({ content, onContentChange, date }) => {
|
||||
const [data, setData] = useState<HealthData>(() => {
|
||||
// Parse existing content if available
|
||||
if (content) {
|
||||
try {
|
||||
return JSON.parse(content);
|
||||
} catch {
|
||||
// If not JSON, try to parse as markdown or return empty
|
||||
return {};
|
||||
}
|
||||
}
|
||||
return {};
|
||||
});
|
||||
|
||||
// Initialize date if not set
|
||||
useEffect(() => {
|
||||
if (!data.date && date) {
|
||||
setData(prev => ({ ...prev, date: date.split('T')[0] }));
|
||||
} else if (!data.date) {
|
||||
setData(prev => ({ ...prev, date: new Date().toISOString().split('T')[0] }));
|
||||
}
|
||||
}, [data.date, date]);
|
||||
|
||||
// Update parent content whenever data changes (with a small delay to avoid too many updates)
|
||||
useEffect(() => {
|
||||
const timer = setTimeout(() => {
|
||||
const jsonContent = JSON.stringify(data, null, 2);
|
||||
onContentChange(jsonContent);
|
||||
}, 100);
|
||||
return () => clearTimeout(timer);
|
||||
}, [data, onContentChange]);
|
||||
|
||||
const updateField = (field: keyof HealthData, value: any) => {
|
||||
setData(prev => ({ ...prev, [field]: value }));
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex flex-col h-full bg-carnet-bg overflow-y-auto">
|
||||
<div className="p-6 space-y-6">
|
||||
{/* 1. Informations de base */}
|
||||
<section className="border-b border-carnet-border pb-4">
|
||||
<div className="flex items-center space-x-2 mb-4">
|
||||
<Calendar className="h-5 w-5 text-carnet-text-primary" />
|
||||
<h3 className="text-lg font-semibold text-carnet-text-primary">Informations de base</h3>
|
||||
</div>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-carnet-text-primary mb-1">
|
||||
Date
|
||||
</label>
|
||||
<input
|
||||
type="date"
|
||||
value={data.date || ''}
|
||||
onChange={(e) => updateField('date', e.target.value)}
|
||||
className="w-full px-3 py-2 border border-carnet-border rounded-md text-sm text-carnet-text-primary bg-white focus:outline-none focus:ring-1 focus:ring-primary"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-carnet-text-primary mb-1">
|
||||
Poids (kg)
|
||||
</label>
|
||||
<input
|
||||
type="number"
|
||||
step="0.1"
|
||||
value={data.poids || ''}
|
||||
onChange={(e) => updateField('poids', e.target.value ? parseFloat(e.target.value) : undefined)}
|
||||
placeholder="Ex: 70.5"
|
||||
className="w-full px-3 py-2 border border-carnet-border rounded-md text-sm text-carnet-text-primary bg-white focus:outline-none focus:ring-1 focus:ring-primary"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-carnet-text-primary mb-1">
|
||||
Température corporelle (°C)
|
||||
</label>
|
||||
<input
|
||||
type="number"
|
||||
step="0.1"
|
||||
value={data.temperature || ''}
|
||||
onChange={(e) => updateField('temperature', e.target.value ? parseFloat(e.target.value) : undefined)}
|
||||
placeholder="Ex: 37.2"
|
||||
className="w-full px-3 py-2 border border-carnet-border rounded-md text-sm text-carnet-text-primary bg-white focus:outline-none focus:ring-1 focus:ring-primary"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-carnet-text-primary mb-1">
|
||||
Tension artérielle (optionnel)
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
value={data.tension || ''}
|
||||
onChange={(e) => updateField('tension', e.target.value)}
|
||||
placeholder="Ex: 120/80"
|
||||
className="w-full px-3 py-2 border border-carnet-border rounded-md text-sm text-carnet-text-primary bg-white focus:outline-none focus:ring-1 focus:ring-primary"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-carnet-text-primary mb-1">
|
||||
Fréquence cardiaque (bpm)
|
||||
</label>
|
||||
<input
|
||||
type="number"
|
||||
value={data.frequenceCardiaque || ''}
|
||||
onChange={(e) => updateField('frequenceCardiaque', e.target.value ? parseInt(e.target.value) : undefined)}
|
||||
placeholder="Ex: 72"
|
||||
className="w-full px-3 py-2 border border-carnet-border rounded-md text-sm text-carnet-text-primary bg-white focus:outline-none focus:ring-1 focus:ring-primary"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* 2. État de santé / symptômes */}
|
||||
<section className="border-b border-carnet-border pb-4">
|
||||
<div className="flex items-center space-x-2 mb-4">
|
||||
<Activity className="h-5 w-5 text-carnet-text-primary" />
|
||||
<h3 className="text-lg font-semibold text-carnet-text-primary">État de santé / symptômes</h3>
|
||||
</div>
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-carnet-text-primary mb-1">
|
||||
Maladie / état
|
||||
</label>
|
||||
<input
|
||||
type="text"
|
||||
value={data.maladie || ''}
|
||||
onChange={(e) => updateField('maladie', e.target.value)}
|
||||
placeholder="Ex: grippe, rhume, migraine, fatigue, aucun"
|
||||
className="w-full px-3 py-2 border border-carnet-border rounded-md text-sm text-carnet-text-primary bg-white focus:outline-none focus:ring-1 focus:ring-primary"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-carnet-text-primary mb-1">
|
||||
Symptômes
|
||||
</label>
|
||||
<textarea
|
||||
value={data.symptomes || ''}
|
||||
onChange={(e) => updateField('symptomes', e.target.value)}
|
||||
placeholder="Ex: fièvre, toux, maux de tête, douleurs, nausées..."
|
||||
rows={3}
|
||||
className="w-full px-3 py-2 border border-carnet-border rounded-md text-sm text-carnet-text-primary bg-white focus:outline-none focus:ring-1 focus:ring-primary"
|
||||
/>
|
||||
</div>
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-carnet-text-primary mb-1">
|
||||
Niveau de douleur (0-10)
|
||||
</label>
|
||||
<input
|
||||
type="number"
|
||||
min="0"
|
||||
max="10"
|
||||
value={data.niveauDouleur || ''}
|
||||
onChange={(e) => updateField('niveauDouleur', e.target.value ? parseInt(e.target.value) : undefined)}
|
||||
className="w-full px-3 py-2 border border-carnet-border rounded-md text-sm text-carnet-text-primary bg-white focus:outline-none focus:ring-1 focus:ring-primary"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-carnet-text-primary mb-1">
|
||||
Niveau d'énergie
|
||||
</label>
|
||||
<select
|
||||
value={data.niveauEnergie || ''}
|
||||
onChange={(e) => updateField('niveauEnergie', e.target.value as 'faible' | 'moyen' | 'bon' || undefined)}
|
||||
className="w-full px-3 py-2 border border-carnet-border rounded-md text-sm text-carnet-text-primary bg-white focus:outline-none focus:ring-1 focus:ring-primary"
|
||||
>
|
||||
<option value="">Sélectionner</option>
|
||||
<option value="faible">Faible</option>
|
||||
<option value="moyen">Moyen</option>
|
||||
<option value="bon">Bon</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-carnet-text-primary mb-1">
|
||||
Qualité du sommeil
|
||||
</label>
|
||||
<select
|
||||
value={data.qualiteSommeil || ''}
|
||||
onChange={(e) => {
|
||||
const value = e.target.value;
|
||||
if (value === 'mauvais' || value === 'moyen' || value === 'bon') {
|
||||
updateField('qualiteSommeil', value);
|
||||
} else if (value) {
|
||||
updateField('qualiteSommeil', parseInt(value));
|
||||
} else {
|
||||
updateField('qualiteSommeil', undefined);
|
||||
}
|
||||
}}
|
||||
className="w-full px-3 py-2 border border-carnet-border rounded-md text-sm text-carnet-text-primary bg-white focus:outline-none focus:ring-1 focus:ring-primary"
|
||||
>
|
||||
<option value="">Sélectionner</option>
|
||||
<option value="mauvais">Mauvais</option>
|
||||
<option value="moyen">Moyen</option>
|
||||
<option value="bon">Bon</option>
|
||||
<option value="1">1</option>
|
||||
<option value="2">2</option>
|
||||
<option value="3">3</option>
|
||||
<option value="4">4</option>
|
||||
<option value="5">5</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* 3. Médicaments et soins */}
|
||||
<section className="border-b border-carnet-border pb-4">
|
||||
<div className="flex items-center space-x-2 mb-4">
|
||||
<Pill className="h-5 w-5 text-carnet-text-primary" />
|
||||
<h3 className="text-lg font-semibold text-carnet-text-primary">Médicaments et soins</h3>
|
||||
</div>
|
||||
<div className="space-y-4">
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-carnet-text-primary mb-1">
|
||||
Médicaments pris
|
||||
</label>
|
||||
<textarea
|
||||
value={data.medicaments || ''}
|
||||
onChange={(e) => updateField('medicaments', e.target.value)}
|
||||
placeholder="Ex: Paracétamol 500mg, 2 comprimés"
|
||||
rows={2}
|
||||
className="w-full px-3 py-2 border border-carnet-border rounded-md text-sm text-carnet-text-primary bg-white focus:outline-none focus:ring-1 focus:ring-primary"
|
||||
/>
|
||||
</div>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div className="flex items-center space-x-2">
|
||||
<input
|
||||
type="checkbox"
|
||||
id="traitementEnCours"
|
||||
checked={data.traitementEnCours || false}
|
||||
onChange={(e) => updateField('traitementEnCours', e.target.checked)}
|
||||
className="w-4 h-4 text-primary border-carnet-border rounded focus:ring-primary"
|
||||
/>
|
||||
<label htmlFor="traitementEnCours" className="text-sm text-carnet-text-primary">
|
||||
Traitement en cours
|
||||
</label>
|
||||
</div>
|
||||
<div className="flex items-center space-x-2">
|
||||
<input
|
||||
type="checkbox"
|
||||
id="consultationMedicale"
|
||||
checked={data.consultationMedicale || false}
|
||||
onChange={(e) => updateField('consultationMedicale', e.target.checked)}
|
||||
className="w-4 h-4 text-primary border-carnet-border rounded focus:ring-primary"
|
||||
/>
|
||||
<label htmlFor="consultationMedicale" className="text-sm text-carnet-text-primary">
|
||||
Consultation médicale
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-carnet-text-primary mb-1">
|
||||
Notes médicales
|
||||
</label>
|
||||
<textarea
|
||||
value={data.notesMedicales || ''}
|
||||
onChange={(e) => updateField('notesMedicales', e.target.value)}
|
||||
placeholder="Notes médicales..."
|
||||
rows={3}
|
||||
className="w-full px-3 py-2 border border-carnet-border rounded-md text-sm text-carnet-text-primary bg-white focus:outline-none focus:ring-1 focus:ring-primary"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* 4. Habitudes & mode de vie */}
|
||||
<section className="border-b border-carnet-border pb-4">
|
||||
<div className="flex items-center space-x-2 mb-4">
|
||||
<Dumbbell className="h-5 w-5 text-carnet-text-primary" />
|
||||
<h3 className="text-lg font-semibold text-carnet-text-primary">Habitudes & mode de vie (optionnel)</h3>
|
||||
</div>
|
||||
<div className="space-y-4">
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-carnet-text-primary mb-1">
|
||||
Hydratation (verres d'eau / jour)
|
||||
</label>
|
||||
<input
|
||||
type="number"
|
||||
value={data.hydratation || ''}
|
||||
onChange={(e) => updateField('hydratation', e.target.value ? parseInt(e.target.value) : undefined)}
|
||||
placeholder="Ex: 8"
|
||||
className="w-full px-3 py-2 border border-carnet-border rounded-md text-sm text-carnet-text-primary bg-white focus:outline-none focus:ring-1 focus:ring-primary"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-carnet-text-primary mb-1">
|
||||
Activité physique
|
||||
</label>
|
||||
<select
|
||||
value={data.activitePhysique || ''}
|
||||
onChange={(e) => updateField('activitePhysique', e.target.value as 'aucune' | 'légère' | 'intense' || undefined)}
|
||||
className="w-full px-3 py-2 border border-carnet-border rounded-md text-sm text-carnet-text-primary bg-white focus:outline-none focus:ring-1 focus:ring-primary"
|
||||
>
|
||||
<option value="">Sélectionner</option>
|
||||
<option value="aucune">Aucune</option>
|
||||
<option value="légère">Légère</option>
|
||||
<option value="intense">Intense</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-carnet-text-primary mb-1">
|
||||
Durée de l'activité (minutes)
|
||||
</label>
|
||||
<input
|
||||
type="number"
|
||||
value={data.dureeActivite || ''}
|
||||
onChange={(e) => updateField('dureeActivite', e.target.value ? parseInt(e.target.value) : undefined)}
|
||||
placeholder="Ex: 30"
|
||||
className="w-full px-3 py-2 border border-carnet-border rounded-md text-sm text-carnet-text-primary bg-white focus:outline-none focus:ring-1 focus:ring-primary"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-carnet-text-primary mb-1">
|
||||
Stress
|
||||
</label>
|
||||
<select
|
||||
value={data.stress || ''}
|
||||
onChange={(e) => updateField('stress', e.target.value as 'faible' | 'moyen' | 'élevé' || undefined)}
|
||||
className="w-full px-3 py-2 border border-carnet-border rounded-md text-sm text-carnet-text-primary bg-white focus:outline-none focus:ring-1 focus:ring-primary"
|
||||
>
|
||||
<option value="">Sélectionner</option>
|
||||
<option value="faible">Faible</option>
|
||||
<option value="moyen">Moyen</option>
|
||||
<option value="élevé">Élevé</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="flex items-center space-x-2 mb-2">
|
||||
<input
|
||||
type="checkbox"
|
||||
id="alimentationParticuliere"
|
||||
checked={data.alimentationParticuliere || false}
|
||||
onChange={(e) => updateField('alimentationParticuliere', e.target.checked)}
|
||||
className="w-4 h-4 text-primary border-carnet-border rounded focus:ring-primary"
|
||||
/>
|
||||
<label htmlFor="alimentationParticuliere" className="text-sm text-carnet-text-primary">
|
||||
Alimentation particulière
|
||||
</label>
|
||||
</div>
|
||||
{data.alimentationParticuliere && (
|
||||
<textarea
|
||||
value={data.commentaireAlimentation || ''}
|
||||
onChange={(e) => updateField('commentaireAlimentation', e.target.value)}
|
||||
placeholder="Commentaire sur l'alimentation..."
|
||||
rows={2}
|
||||
className="w-full px-3 py-2 border border-carnet-border rounded-md text-sm text-carnet-text-primary bg-white focus:outline-none focus:ring-1 focus:ring-primary"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* 5. Champ libre */}
|
||||
<section>
|
||||
<div className="flex items-center space-x-2 mb-4">
|
||||
<FileText className="h-5 w-5 text-carnet-text-primary" />
|
||||
<h3 className="text-lg font-semibold text-carnet-text-primary">Notes personnelles</h3>
|
||||
</div>
|
||||
<div>
|
||||
<textarea
|
||||
value={data.notesPersonnelles || ''}
|
||||
onChange={(e) => updateField('notesPersonnelles', e.target.value)}
|
||||
placeholder="Ex: Mal dormi, début de grippe, me sens mieux aujourd'hui..."
|
||||
rows={4}
|
||||
className="w-full px-3 py-2 border border-carnet-border rounded-md text-sm text-carnet-text-primary bg-white focus:outline-none focus:ring-1 focus:ring-primary"
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
Loading…
Reference in New Issue
Block a user