mirror of
https://github.com/R0m1k3/noteflow.git
synced 2026-10-11 17:29:37 +02:00
feat: add note templates system with 10 pre-built templates
- Create noteTemplates.ts with 10 default templates (Meeting, Daily Journal, Project Planning, etc.) - Add TemplateSelector component with search and category filtering - Integrate template selector in desktop and mobile note creation flows - Support automatic todo creation from templates - Organize templates by category (Travail, Personnel, Créativité, Autre)
This commit is contained in:
4 files changed
+547
-4
No files matched your search
@@ -0,0 +1,126 @@
|
||||
import { useState } from "react";
|
||||
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription } from "@/components/ui/dialog";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { ScrollArea } from "@/components/ui/scroll-area";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Search, Sparkles } from "lucide-react";
|
||||
import { DEFAULT_TEMPLATES, getTemplatesByCategory, searchTemplates, type NoteTemplate } from "@/utils/noteTemplates";
|
||||
|
||||
interface TemplateSelectorProps {
|
||||
open: boolean;
|
||||
onOpenChange: (open: boolean) => void;
|
||||
onSelectTemplate: (template: NoteTemplate) => void;
|
||||
}
|
||||
|
||||
export function TemplateSelector({ open, onOpenChange, onSelectTemplate }: TemplateSelectorProps) {
|
||||
const [searchQuery, setSearchQuery] = useState("");
|
||||
|
||||
const templates = searchQuery.trim()
|
||||
? searchTemplates(searchQuery)
|
||||
: DEFAULT_TEMPLATES;
|
||||
|
||||
const templatesByCategory = getTemplatesByCategory();
|
||||
|
||||
const handleSelectTemplate = (template: NoteTemplate) => {
|
||||
onSelectTemplate(template);
|
||||
onOpenChange(false);
|
||||
setSearchQuery("");
|
||||
};
|
||||
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||
<DialogContent className="max-w-3xl max-h-[80vh]">
|
||||
<DialogHeader>
|
||||
<DialogTitle className="flex items-center gap-2">
|
||||
<Sparkles className="h-5 w-5 text-primary" />
|
||||
Choisir un template
|
||||
</DialogTitle>
|
||||
<DialogDescription>
|
||||
Démarrez rapidement avec un modèle prédéfini
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
{/* Search */}
|
||||
<div className="relative">
|
||||
<Search className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" />
|
||||
<Input
|
||||
placeholder="Rechercher un template..."
|
||||
value={searchQuery}
|
||||
onChange={(e) => setSearchQuery(e.target.value)}
|
||||
className="pl-9"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Templates Grid */}
|
||||
<ScrollArea className="h-[400px] pr-4">
|
||||
{searchQuery.trim() ? (
|
||||
// Search Results
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-3">
|
||||
{templates.map((template) => (
|
||||
<TemplateCard
|
||||
key={template.id}
|
||||
template={template}
|
||||
onClick={() => handleSelectTemplate(template)}
|
||||
/>
|
||||
))}
|
||||
{templates.length === 0 && (
|
||||
<div className="col-span-2 text-center py-8 text-muted-foreground">
|
||||
Aucun template trouvé
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
// Templates by Category
|
||||
<div className="space-y-6">
|
||||
{Array.from(templatesByCategory.entries()).map(([category, categoryTemplates]) => (
|
||||
<div key={category}>
|
||||
<h3 className="font-semibold mb-3 text-sm text-muted-foreground uppercase tracking-wide">
|
||||
{category}
|
||||
</h3>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-3">
|
||||
{categoryTemplates.map((template) => (
|
||||
<TemplateCard
|
||||
key={template.id}
|
||||
template={template}
|
||||
onClick={() => handleSelectTemplate(template)}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</ScrollArea>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
|
||||
interface TemplateCardProps {
|
||||
template: NoteTemplate;
|
||||
onClick: () => void;
|
||||
}
|
||||
|
||||
function TemplateCard({ template, onClick }: TemplateCardProps) {
|
||||
return (
|
||||
<Button
|
||||
variant="outline"
|
||||
className="h-auto p-4 flex flex-col items-start gap-2 hover:bg-accent hover:border-primary transition-all"
|
||||
onClick={onClick}
|
||||
>
|
||||
<div className="flex items-center gap-2 w-full">
|
||||
<span className="text-2xl">{template.icon}</span>
|
||||
<span className="font-semibold text-left flex-1">{template.name}</span>
|
||||
</div>
|
||||
<p className="text-sm text-muted-foreground text-left">
|
||||
{template.description}
|
||||
</p>
|
||||
{template.todos && template.todos.length > 0 && (
|
||||
<Badge variant="secondary" className="text-xs">
|
||||
{template.todos.length} tâche{template.todos.length > 1 ? 's' : ''}
|
||||
</Badge>
|
||||
)}
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
+32
-2
@@ -34,6 +34,8 @@ import { AddUserModal } from "@/components/modals/AddUserModal";
|
||||
import { compressImage, formatFileSize } from "@/utils/imageCompression";
|
||||
import { ModeToggle } from "@/components/mode-toggle";
|
||||
import { downloadNoteAsMarkdown, downloadAllNotesAsMarkdown, downloadTodosAsMarkdown } from "@/utils/markdownExport";
|
||||
import { TemplateSelector } from "@/components/TemplateSelector";
|
||||
import type { NoteTemplate } from "@/utils/noteTemplates";
|
||||
|
||||
// ===== FONCTIONS UTILITAIRES TIMEZONE EUROPE/PARIS =====
|
||||
|
||||
@@ -147,6 +149,7 @@ const Index = () => {
|
||||
const [addUserModal, setAddUserModal] = useState(false);
|
||||
const [deleteUserModal, setDeleteUserModal] = useState<{ open: boolean, userId?: number }>({ open: false });
|
||||
const [changePasswordModal, setChangePasswordModal] = useState<{ open: boolean, userId?: number }>({ open: false });
|
||||
const [templateSelectorOpen, setTemplateSelectorOpen] = useState(false);
|
||||
const [addNoteTodoModal, setAddNoteTodoModal] = useState(false);
|
||||
const [addTagModal, setAddTagModal] = useState(false);
|
||||
const [selectedImage, setSelectedImage] = useState<string | null>(null);
|
||||
@@ -410,13 +413,33 @@ const Index = () => {
|
||||
navigate("/login");
|
||||
};
|
||||
|
||||
const handleCreateNote = async () => {
|
||||
const handleCreateNote = () => {
|
||||
setTemplateSelectorOpen(true);
|
||||
};
|
||||
|
||||
const handleCreateNoteFromTemplate = async (template: NoteTemplate) => {
|
||||
try {
|
||||
const newNote = await NotesService.createNote("Nouvelle note");
|
||||
const title = template.name === "Note vide" ? "Nouvelle note" : template.name;
|
||||
const newNote = await NotesService.createNote(title, template.content);
|
||||
if (newNote) {
|
||||
// Add todos if template has any
|
||||
if (template.todos && template.todos.length > 0) {
|
||||
for (const todo of template.todos) {
|
||||
await NotesService.addTodo(newNote.id!, todo.text);
|
||||
}
|
||||
// Reload note to get the todos
|
||||
const notes = await NotesService.getNotes();
|
||||
const updatedNote = notes.find(n => n.id === newNote.id);
|
||||
if (updatedNote) {
|
||||
setNotes([updatedNote, ...notes.filter(n => n.id !== newNote.id)]);
|
||||
setOpenNote(updatedNote);
|
||||
}
|
||||
} else {
|
||||
setNotes([newNote, ...notes]);
|
||||
setOpenNote(newNote);
|
||||
}
|
||||
showSuccess("Note créée depuis le template");
|
||||
}
|
||||
} catch (error) {
|
||||
showError("Erreur lors de la création de la note");
|
||||
}
|
||||
@@ -2287,6 +2310,13 @@ const Index = () => {
|
||||
confirmText="Ajouter"
|
||||
/>
|
||||
|
||||
{/* Template Selector */}
|
||||
<TemplateSelector
|
||||
open={templateSelectorOpen}
|
||||
onOpenChange={setTemplateSelectorOpen}
|
||||
onSelectTemplate={handleCreateNoteFromTemplate}
|
||||
/>
|
||||
|
||||
{/* Add Event Modal */}
|
||||
<Dialog open={addEventModal} onOpenChange={setAddEventModal}>
|
||||
<DialogContent className="max-w-2xl max-h-[90vh] overflow-y-auto">
|
||||
|
||||
@@ -8,6 +8,8 @@ import { MobileCard } from "@/components/mobile/MobileCard";
|
||||
import { Plus, Search, CheckSquare, Image as ImageIcon, Paperclip, Tag as TagIcon, FileText } from "lucide-react";
|
||||
import NotesService, { Note } from "@/services/NotesService";
|
||||
import { showError, showSuccess } from "@/utils/toast";
|
||||
import { TemplateSelector } from "@/components/TemplateSelector";
|
||||
import type { NoteTemplate } from "@/utils/noteTemplates";
|
||||
|
||||
const NOTES_PER_PAGE = 10;
|
||||
|
||||
@@ -16,6 +18,7 @@ export default function NotesPage() {
|
||||
const [searchQuery, setSearchQuery] = useState("");
|
||||
const [showArchived, setShowArchived] = useState(false);
|
||||
const [page, setPage] = useState(0);
|
||||
const [templateSelectorOpen, setTemplateSelectorOpen] = useState(false);
|
||||
const navigate = useNavigate();
|
||||
|
||||
const loadNotes = async () => {
|
||||
@@ -48,10 +51,24 @@ export default function NotesPage() {
|
||||
const totalPages = Math.ceil(filteredNotes.length / NOTES_PER_PAGE);
|
||||
const paginatedNotes = filteredNotes.slice(page * NOTES_PER_PAGE, (page + 1) * NOTES_PER_PAGE);
|
||||
|
||||
const handleCreateNote = async () => {
|
||||
const handleCreateNote = () => {
|
||||
setTemplateSelectorOpen(true);
|
||||
};
|
||||
|
||||
const handleCreateNoteFromTemplate = async (template: NoteTemplate) => {
|
||||
try {
|
||||
const newNote = await NotesService.createNote("Nouvelle note", "");
|
||||
const title = template.name === "Note vide" ? "Nouvelle note" : template.name;
|
||||
const newNote = await NotesService.createNote(title, template.content);
|
||||
if (newNote) {
|
||||
// Add todos if template has any
|
||||
if (template.todos && template.todos.length > 0) {
|
||||
for (const todo of template.todos) {
|
||||
await NotesService.addTodo(newNote.id!, todo.text);
|
||||
}
|
||||
}
|
||||
showSuccess("Note créée depuis le template");
|
||||
navigate(`/mobile/notes/${newNote.id}`);
|
||||
}
|
||||
} catch (error) {
|
||||
showError("Erreur lors de la création de la note");
|
||||
}
|
||||
@@ -238,6 +255,13 @@ export default function NotesPage() {
|
||||
|
||||
{/* FAB */}
|
||||
<MobileFAB icon={Plus} onClick={handleCreateNote} label="Créer une note" />
|
||||
|
||||
{/* Template Selector */}
|
||||
<TemplateSelector
|
||||
open={templateSelectorOpen}
|
||||
onOpenChange={setTemplateSelectorOpen}
|
||||
onSelectTemplate={handleCreateNoteFromTemplate}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,363 @@
|
||||
export interface NoteTemplate {
|
||||
id: string;
|
||||
name: string;
|
||||
description: string;
|
||||
icon: string;
|
||||
category: string;
|
||||
content: string;
|
||||
todos?: Array<{ text: string; completed: boolean }>;
|
||||
}
|
||||
|
||||
export const DEFAULT_TEMPLATES: NoteTemplate[] = [
|
||||
{
|
||||
id: 'blank',
|
||||
name: 'Note vide',
|
||||
description: 'Commencer avec une page blanche',
|
||||
icon: '📄',
|
||||
category: 'Général',
|
||||
content: '',
|
||||
todos: []
|
||||
},
|
||||
{
|
||||
id: 'meeting',
|
||||
name: 'Réunion',
|
||||
description: 'Notes de réunion structurées',
|
||||
icon: '🤝',
|
||||
category: 'Travail',
|
||||
content: `<h2>Réunion - [Titre]</h2>
|
||||
<p><strong>Date:</strong> ${new Date().toLocaleDateString('fr-FR')}</p>
|
||||
<p><strong>Participants:</strong> </p>
|
||||
<p><strong>Objectif:</strong> </p>
|
||||
|
||||
<h3>Points discutés</h3>
|
||||
<p>1. </p>
|
||||
<p>2. </p>
|
||||
<p>3. </p>
|
||||
|
||||
<h3>Décisions</h3>
|
||||
<p>• </p>
|
||||
|
||||
<h3>Prochaines étapes</h3>
|
||||
<p>→ </p>`,
|
||||
todos: [
|
||||
{ text: 'Envoyer le compte-rendu', completed: false },
|
||||
{ text: 'Planifier le suivi', completed: false }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'daily-journal',
|
||||
name: 'Journal quotidien',
|
||||
description: 'Réflexions et gratitude du jour',
|
||||
icon: '📔',
|
||||
category: 'Personnel',
|
||||
content: `<h2>Journal - ${new Date().toLocaleDateString('fr-FR', { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' })}</h2>
|
||||
|
||||
<h3>🌅 Ce matin je me sens...</h3>
|
||||
<p></p>
|
||||
|
||||
<h3>🎯 Mes priorités aujourd'hui</h3>
|
||||
<p>1. </p>
|
||||
<p>2. </p>
|
||||
<p>3. </p>
|
||||
|
||||
<h3>💡 Idées et réflexions</h3>
|
||||
<p></p>
|
||||
|
||||
<h3>🙏 Gratitude</h3>
|
||||
<p>Aujourd'hui je suis reconnaissant(e) pour:</p>
|
||||
<p>• </p>
|
||||
|
||||
<h3>🌙 Bilan de la journée</h3>
|
||||
<p></p>`,
|
||||
todos: []
|
||||
},
|
||||
{
|
||||
id: 'project-planning',
|
||||
name: 'Planification de projet',
|
||||
description: 'Organiser un nouveau projet',
|
||||
icon: '🎯',
|
||||
category: 'Travail',
|
||||
content: `<h2>Projet - [Nom du projet]</h2>
|
||||
|
||||
<h3>📋 Vue d'ensemble</h3>
|
||||
<p><strong>Objectif:</strong> </p>
|
||||
<p><strong>Deadline:</strong> </p>
|
||||
<p><strong>Responsable:</strong> </p>
|
||||
|
||||
<h3>🎯 Objectifs</h3>
|
||||
<p>1. </p>
|
||||
<p>2. </p>
|
||||
<p>3. </p>
|
||||
|
||||
<h3>📊 Phases du projet</h3>
|
||||
<p><strong>Phase 1:</strong> Préparation</p>
|
||||
<p>• </p>
|
||||
|
||||
<p><strong>Phase 2:</strong> Développement</p>
|
||||
<p>• </p>
|
||||
|
||||
<p><strong>Phase 3:</strong> Finalisation</p>
|
||||
<p>• </p>
|
||||
|
||||
<h3>👥 Équipe</h3>
|
||||
<p>• </p>
|
||||
|
||||
<h3>⚠️ Risques identifiés</h3>
|
||||
<p>• </p>
|
||||
|
||||
<h3>📌 Ressources nécessaires</h3>
|
||||
<p>• </p>`,
|
||||
todos: [
|
||||
{ text: 'Définir le scope', completed: false },
|
||||
{ text: 'Assembler l\'équipe', completed: false },
|
||||
{ text: 'Créer le planning', completed: false }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'brainstorming',
|
||||
name: 'Brainstorming',
|
||||
description: 'Session de génération d\'idées',
|
||||
icon: '💭',
|
||||
category: 'Créativité',
|
||||
content: `<h2>💭 Brainstorming - [Sujet]</h2>
|
||||
|
||||
<p><strong>Date:</strong> ${new Date().toLocaleDateString('fr-FR')}</p>
|
||||
<p><strong>Question centrale:</strong> </p>
|
||||
|
||||
<h3>💡 Idées</h3>
|
||||
<p>1. </p>
|
||||
<p>2. </p>
|
||||
<p>3. </p>
|
||||
<p>4. </p>
|
||||
<p>5. </p>
|
||||
|
||||
<h3>⭐ Idées à creuser</h3>
|
||||
<p>• </p>
|
||||
|
||||
<h3>🚀 Actions possibles</h3>
|
||||
<p>→ </p>
|
||||
|
||||
<h3>🔗 Connexions et insights</h3>
|
||||
<p></p>`,
|
||||
todos: []
|
||||
},
|
||||
{
|
||||
id: 'recipe',
|
||||
name: 'Recette de cuisine',
|
||||
description: 'Noter une recette',
|
||||
icon: '👨🍳',
|
||||
category: 'Personnel',
|
||||
content: `<h2>🍳 Recette - [Nom du plat]</h2>
|
||||
|
||||
<p><strong>⏱️ Temps de préparation:</strong> </p>
|
||||
<p><strong>👥 Portions:</strong> </p>
|
||||
<p><strong>😋 Difficulté:</strong> Facile / Moyen / Difficile</p>
|
||||
|
||||
<h3>📝 Ingrédients</h3>
|
||||
<p>• </p>
|
||||
|
||||
<h3>👨🍳 Préparation</h3>
|
||||
<p>1. </p>
|
||||
<p>2. </p>
|
||||
<p>3. </p>
|
||||
|
||||
<h3>💡 Astuces</h3>
|
||||
<p>• </p>
|
||||
|
||||
<h3>📌 Notes</h3>
|
||||
<p></p>`,
|
||||
todos: []
|
||||
},
|
||||
{
|
||||
id: 'weekly-review',
|
||||
name: 'Revue hebdomadaire',
|
||||
description: 'Bilan et planification de la semaine',
|
||||
icon: '📊',
|
||||
category: 'Productivité',
|
||||
content: `<h2>📊 Revue de la semaine</h2>
|
||||
<p><strong>Semaine du:</strong> ${new Date().toLocaleDateString('fr-FR')}</p>
|
||||
|
||||
<h3>✅ Accomplissements</h3>
|
||||
<p>Cette semaine j'ai réussi à:</p>
|
||||
<p>1. </p>
|
||||
<p>2. </p>
|
||||
<p>3. </p>
|
||||
|
||||
<h3>📈 Progrès</h3>
|
||||
<p>• </p>
|
||||
|
||||
<h3>⚠️ Défis rencontrés</h3>
|
||||
<p>• </p>
|
||||
|
||||
<h3>💡 Leçons apprises</h3>
|
||||
<p>• </p>
|
||||
|
||||
<h3>🎯 Objectifs semaine prochaine</h3>
|
||||
<p>1. </p>
|
||||
<p>2. </p>
|
||||
<p>3. </p>
|
||||
|
||||
<h3>🔄 Habitudes à améliorer</h3>
|
||||
<p>• </p>`,
|
||||
todos: [
|
||||
{ text: 'Planifier la semaine prochaine', completed: false }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'bug-report',
|
||||
name: 'Rapport de bug',
|
||||
description: 'Documenter un problème technique',
|
||||
icon: '🐛',
|
||||
category: 'Développement',
|
||||
content: `<h2>🐛 Bug Report</h2>
|
||||
|
||||
<p><strong>Date:</strong> ${new Date().toLocaleDateString('fr-FR')}</p>
|
||||
<p><strong>Priorité:</strong> 🔴 Critique / 🟠 Haute / 🟡 Moyenne / 🟢 Basse</p>
|
||||
<p><strong>Statut:</strong> Nouveau / En cours / Résolu</p>
|
||||
|
||||
<h3>📋 Description</h3>
|
||||
<p></p>
|
||||
|
||||
<h3>🔄 Étapes pour reproduire</h3>
|
||||
<p>1. </p>
|
||||
<p>2. </p>
|
||||
<p>3. </p>
|
||||
|
||||
<h3>✅ Résultat attendu</h3>
|
||||
<p></p>
|
||||
|
||||
<h3>❌ Résultat actuel</h3>
|
||||
<p></p>
|
||||
|
||||
<h3>💻 Environnement</h3>
|
||||
<p><strong>OS:</strong> </p>
|
||||
<p><strong>Navigateur:</strong> </p>
|
||||
<p><strong>Version:</strong> </p>
|
||||
|
||||
<h3>📌 Notes additionnelles</h3>
|
||||
<p></p>
|
||||
|
||||
<h3>🔧 Solution proposée</h3>
|
||||
<p></p>`,
|
||||
todos: [
|
||||
{ text: 'Reproduire le bug', completed: false },
|
||||
{ text: 'Identifier la cause', completed: false },
|
||||
{ text: 'Implémenter le fix', completed: false },
|
||||
{ text: 'Tester la solution', completed: false }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'learning-notes',
|
||||
name: 'Notes d\'apprentissage',
|
||||
description: 'Apprendre un nouveau sujet',
|
||||
icon: '📚',
|
||||
category: 'Éducation',
|
||||
content: `<h2>📚 Notes d'apprentissage - [Sujet]</h2>
|
||||
|
||||
<p><strong>Date:</strong> ${new Date().toLocaleDateString('fr-FR')}</p>
|
||||
<p><strong>Source:</strong> </p>
|
||||
|
||||
<h3>🎯 Objectifs d'apprentissage</h3>
|
||||
<p>• </p>
|
||||
|
||||
<h3>📝 Concepts clés</h3>
|
||||
<p><strong>Concept 1:</strong> </p>
|
||||
<p>• </p>
|
||||
|
||||
<p><strong>Concept 2:</strong> </p>
|
||||
<p>• </p>
|
||||
|
||||
<h3>💡 Exemples</h3>
|
||||
<p></p>
|
||||
|
||||
<h3>❓ Questions</h3>
|
||||
<p>• </p>
|
||||
|
||||
<h3>🔗 Ressources</h3>
|
||||
<p>• </p>
|
||||
|
||||
<h3>📌 À retenir</h3>
|
||||
<p>• </p>
|
||||
|
||||
<h3>✍️ Pratique</h3>
|
||||
<p>Exercices à faire:</p>
|
||||
<p>1. </p>`,
|
||||
todos: [
|
||||
{ text: 'Réviser les notes', completed: false },
|
||||
{ text: 'Faire les exercices', completed: false }
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'travel-planning',
|
||||
name: 'Planification de voyage',
|
||||
description: 'Organiser un déplacement',
|
||||
icon: '✈️',
|
||||
category: 'Personnel',
|
||||
content: `<h2>✈️ Voyage - [Destination]</h2>
|
||||
|
||||
<p><strong>📅 Dates:</strong> du [date] au [date]</p>
|
||||
<p><strong>👥 Voyageurs:</strong> </p>
|
||||
<p><strong>💰 Budget:</strong> </p>
|
||||
|
||||
<h3>🎒 À emporter</h3>
|
||||
<p>Vêtements:</p>
|
||||
<p>• </p>
|
||||
|
||||
<p>Documents:</p>
|
||||
<p>• Passeport</p>
|
||||
<p>• Billets</p>
|
||||
<p>• Assurance</p>
|
||||
|
||||
<h3>🏨 Hébergement</h3>
|
||||
<p><strong>Hôtel/Airbnb:</strong> </p>
|
||||
<p><strong>Adresse:</strong> </p>
|
||||
<p><strong>Check-in:</strong> </p>
|
||||
|
||||
<h3>✈️ Transport</h3>
|
||||
<p><strong>Vol aller:</strong> </p>
|
||||
<p><strong>Vol retour:</strong> </p>
|
||||
|
||||
<h3>📍 Lieux à visiter</h3>
|
||||
<p>1. </p>
|
||||
<p>2. </p>
|
||||
<p>3. </p>
|
||||
|
||||
<h3>🍽️ Restaurants recommandés</h3>
|
||||
<p>• </p>
|
||||
|
||||
<h3>💡 Conseils et infos pratiques</h3>
|
||||
<p>• </p>`,
|
||||
todos: [
|
||||
{ text: 'Réserver les vols', completed: false },
|
||||
{ text: 'Réserver l\'hébergement', completed: false },
|
||||
{ text: 'Faire la valise', completed: false }
|
||||
]
|
||||
}
|
||||
];
|
||||
|
||||
export function getTemplatesByCategory(): Map<string, NoteTemplate[]> {
|
||||
const categorized = new Map<string, NoteTemplate[]>();
|
||||
|
||||
DEFAULT_TEMPLATES.forEach(template => {
|
||||
const category = template.category;
|
||||
if (!categorized.has(category)) {
|
||||
categorized.set(category, []);
|
||||
}
|
||||
categorized.get(category)!.push(template);
|
||||
});
|
||||
|
||||
return categorized;
|
||||
}
|
||||
|
||||
export function getTemplateById(id: string): NoteTemplate | undefined {
|
||||
return DEFAULT_TEMPLATES.find(t => t.id === id);
|
||||
}
|
||||
|
||||
export function searchTemplates(query: string): NoteTemplate[] {
|
||||
const lowerQuery = query.toLowerCase();
|
||||
return DEFAULT_TEMPLATES.filter(template =>
|
||||
template.name.toLowerCase().includes(lowerQuery) ||
|
||||
template.description.toLowerCase().includes(lowerQuery) ||
|
||||
template.category.toLowerCase().includes(lowerQuery)
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user