feat: Implement initial Noteflow application UI, foundational notes management, and mobile notes page.

This commit is contained in:
Michael committed 2025-12-08 09:00:59 +01:00
1 parent ad3f415469
commit 4ba078bf4e
4 files changed
+60 -7

No files matched your search

+24
View File
@@ -38,6 +38,30 @@ const upload = multer({
// Toutes les routes nécessitent authentification // Toutes les routes nécessitent authentification
router.use(authenticateToken); router.use(authenticateToken);
/**
* GET /api/notes/counts
* Récupérer le nombre de notes actives et archivées
*/
router.get('/counts', async (req, res) => {
try {
const activeCount = await getAll(`
SELECT COUNT(*) as count FROM notes WHERE user_id = $1 AND archived = false
`, [req.user.id]);
const archivedCount = await getAll(`
SELECT COUNT(*) as count FROM notes WHERE user_id = $1 AND archived = true
`, [req.user.id]);
res.json({
active: parseInt(activeCount[0]?.count || 0),
archived: parseInt(archivedCount[0]?.count || 0)
});
} catch (error) {
logger.error('Erreur lors de la récupération des compteurs:', error);
res.status(500).json({ error: 'Erreur serveur' });
}
});
/** /**
* GET /api/notes?archived=true/false * GET /api/notes?archived=true/false
* Liste toutes les notes de l'utilisateur * Liste toutes les notes de l'utilisateur
+9 -3
View File
@@ -159,6 +159,7 @@ const Index = () => {
const [rssFeeds, setRssFeeds] = useState<RssFeed[]>([]); const [rssFeeds, setRssFeeds] = useState<RssFeed[]>([]);
const [rssArticles, setRssArticles] = useState<RssArticle[]>([]); const [rssArticles, setRssArticles] = useState<RssArticle[]>([]);
const [calendarEvents, setCalendarEvents] = useState<CalendarEvent[]>([]); const [calendarEvents, setCalendarEvents] = useState<CalendarEvent[]>([]);
const [counts, setCounts] = useState({ active: 0, archived: 0 });
const [settings, setSettings] = useState<AppSettings>({}); const [settings, setSettings] = useState<AppSettings>({});
const [adminTab, setAdminTab] = useState("users"); const [adminTab, setAdminTab] = useState("users");
const [showArchived, setShowArchived] = useState(false); const [showArchived, setShowArchived] = useState(false);
@@ -303,10 +304,15 @@ const Index = () => {
const loadNotes = async () => { const loadNotes = async () => {
try { try {
const fetchedNotes = await NotesService.getNotes(showArchived); const [fetchedNotes, fetchedCounts] = await Promise.all([
NotesService.getNotes(showArchived),
NotesService.getCounts()
]);
if (Array.isArray(fetchedNotes)) { if (Array.isArray(fetchedNotes)) {
setNotes(fetchedNotes); setNotes(fetchedNotes);
} }
setCounts(fetchedCounts);
} catch (error) { } catch (error) {
console.error("Erreur lors du chargement des notes:", error); console.error("Erreur lors du chargement des notes:", error);
} }
@@ -1352,13 +1358,13 @@ const Index = () => {
variant={!showArchived ? "default" : "outline"} variant={!showArchived ? "default" : "outline"}
onClick={() => setShowArchived(false)} onClick={() => setShowArchived(false)}
> >
Actives ({notes.filter(n => !n.archived).length}) Actives ({counts.active})
</Button> </Button>
<Button <Button
variant={showArchived ? "default" : "outline"} variant={showArchived ? "default" : "outline"}
onClick={() => setShowArchived(true)} onClick={() => setShowArchived(true)}
> >
Archivées ({notes.filter(n => n.archived).length}) Archivées ({counts.archived})
</Button> </Button>
</div> </div>
</div> </div>
+10 -4
View File
@@ -31,10 +31,16 @@ export default function NotesPage() {
const [viewMode, setViewMode] = useState<'list' | 'kanban'>('list'); const [viewMode, setViewMode] = useState<'list' | 'kanban'>('list');
const navigate = useNavigate(); const navigate = useNavigate();
const [counts, setCounts] = useState({ active: 0, archived: 0 });
const loadNotes = async () => { const loadNotes = async () => {
try { try {
const data = await NotesService.getNotes(showArchived); const [notesData, countsData] = await Promise.all([
setNotes(data); NotesService.getNotes(showArchived),
NotesService.getCounts()
]);
setNotes(notesData);
setCounts(countsData);
} catch (error) { } catch (error) {
showError("Erreur lors du chargement des notes"); showError("Erreur lors du chargement des notes");
} }
@@ -184,7 +190,7 @@ export default function NotesPage() {
}} }}
className="flex-1" className="flex-1"
> >
Actives ({notes.filter(n => !n.archived).length}) Actives ({counts.active})
</Button> </Button>
<Button <Button
variant={showArchived ? "default" : "outline"} variant={showArchived ? "default" : "outline"}
@@ -195,7 +201,7 @@ export default function NotesPage() {
}} }}
className="flex-1" className="flex-1"
> >
Archivées ({notes.filter(n => n.archived).length}) Archivées ({counts.archived})
</Button> </Button>
</div> </div>
+17
View File
@@ -63,6 +63,23 @@ class NotesService {
} }
} }
async getCounts(): Promise<{ active: number; archived: number }> {
try {
const response = await fetch("/api/notes/counts", {
headers: AuthService.getHeaders()
});
if (!response.ok) {
throw new Error("Erreur lors de la récupération des compteurs");
}
return await response.json();
} catch (error) {
console.error("Erreur compteur:", error);
return { active: 0, archived: 0 };
}
}
async createNote(title: string, content: string = ""): Promise<Note | null> { async createNote(title: string, content: string = ""): Promise<Note | null> {
try { try {
const response = await fetch("/api/notes", { const response = await fetch("/api/notes", {