feat: Phase 4 — module Notes complet
Backend : - schemas/notes.py : NoteCreate/Update/Response + AttachmentResponse - api/notes.py : CRUD + FTS français (plainto_tsquery) + filtres rapides (has_photo/audio/gps/tag/category) + pièces jointes (image/audio) - main.py : enregistrement /api/notes Frontend : - api/notes.ts : fetchNotes/create/update/delete + add/deleteAttachment - NoteForm.tsx : titre, contenu, catégorie, tags CSV, GPS - NotesPage.tsx : liste mobile (chronologique) + grille laptop, FAB +, enregistrement audio inline (MediaRecorder), upload photo, filtres rapides Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,91 @@
|
||||
export interface NoteAttachment {
|
||||
id: string
|
||||
file_path: string | null
|
||||
thumbnail_path: string | null
|
||||
file_type: 'image' | 'audio' | null
|
||||
original_name: string | null
|
||||
created_at: string
|
||||
}
|
||||
|
||||
export interface Note {
|
||||
id: string
|
||||
title: string | null
|
||||
content: string
|
||||
category: string | null
|
||||
tags: string[]
|
||||
gps_lat: number | null
|
||||
gps_lon: number | null
|
||||
created_at: string
|
||||
attachments: NoteAttachment[]
|
||||
}
|
||||
|
||||
export interface NoteCreate {
|
||||
title?: string
|
||||
content: string
|
||||
category?: string
|
||||
tags?: string[]
|
||||
gps_lat?: number
|
||||
gps_lon?: number
|
||||
}
|
||||
|
||||
export interface NoteFilters {
|
||||
q?: string
|
||||
category?: string
|
||||
tag?: string
|
||||
has_photo?: boolean
|
||||
has_audio?: boolean
|
||||
has_gps?: boolean
|
||||
}
|
||||
|
||||
const BASE = '/api/notes'
|
||||
|
||||
export async function fetchNotes(filters: NoteFilters = {}): Promise<Note[]> {
|
||||
const params = new URLSearchParams()
|
||||
if (filters.q) params.set('q', filters.q)
|
||||
if (filters.category) params.set('category', filters.category)
|
||||
if (filters.tag) params.set('tag', filters.tag)
|
||||
if (filters.has_photo !== undefined) params.set('has_photo', String(filters.has_photo))
|
||||
if (filters.has_audio !== undefined) params.set('has_audio', String(filters.has_audio))
|
||||
if (filters.has_gps !== undefined) params.set('has_gps', String(filters.has_gps))
|
||||
const res = await fetch(`${BASE}/?${params}`)
|
||||
if (!res.ok) throw new Error('Erreur chargement notes')
|
||||
return res.json() as Promise<Note[]>
|
||||
}
|
||||
|
||||
export async function createNote(data: NoteCreate): Promise<Note> {
|
||||
const res = await fetch(`${BASE}/`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(data),
|
||||
})
|
||||
if (!res.ok) throw new Error('Erreur création note')
|
||||
return res.json() as Promise<Note>
|
||||
}
|
||||
|
||||
export async function updateNote(id: string, data: Partial<NoteCreate>): Promise<Note> {
|
||||
const res = await fetch(`${BASE}/${id}`, {
|
||||
method: 'PATCH',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(data),
|
||||
})
|
||||
if (!res.ok) throw new Error('Erreur mise à jour note')
|
||||
return res.json() as Promise<Note>
|
||||
}
|
||||
|
||||
export async function deleteNote(id: string): Promise<void> {
|
||||
const res = await fetch(`${BASE}/${id}`, { method: 'DELETE' })
|
||||
if (!res.ok) throw new Error('Erreur suppression note')
|
||||
}
|
||||
|
||||
export async function addAttachment(noteId: string, file: File): Promise<Note> {
|
||||
const fd = new FormData()
|
||||
fd.append('file', file)
|
||||
const res = await fetch(`${BASE}/${noteId}/attachments`, { method: 'POST', body: fd })
|
||||
if (!res.ok) throw new Error('Erreur upload pièce jointe')
|
||||
return res.json() as Promise<Note>
|
||||
}
|
||||
|
||||
export async function deleteAttachment(noteId: string, attId: string): Promise<void> {
|
||||
const res = await fetch(`${BASE}/${noteId}/attachments/${attId}`, { method: 'DELETE' })
|
||||
if (!res.ok) throw new Error('Erreur suppression pièce jointe')
|
||||
}
|
||||
Reference in New Issue
Block a user