From c3d141402fcbb663f7227ca01f87aa375a68ac39 Mon Sep 17 00:00:00 2001 From: R0m1k3 Date: Fri, 10 Apr 2026 11:03:02 +0200 Subject: [PATCH] feat: add IconPicker component and utility library for Lucide icon selection and color management --- app/admin/page.tsx | 33 +++----- components/AddButtonForm.tsx | 40 +++++---- components/IconPicker.tsx | 155 +++++++++++++++++++++++++++++++++++ components/MenuButton.tsx | 36 ++------ lib/lucideIcons.ts | 74 +++++++++++++++++ 5 files changed, 272 insertions(+), 66 deletions(-) create mode 100644 components/IconPicker.tsx create mode 100644 lib/lucideIcons.ts diff --git a/app/admin/page.tsx b/app/admin/page.tsx index bcf3f6f..972dd16 100644 --- a/app/admin/page.tsx +++ b/app/admin/page.tsx @@ -2,17 +2,14 @@ import { prisma } from '@/lib/db'; import { isAuthenticated, logoutAdmin, deleteButton } from '@/app/actions/admin'; import { redirect } from 'next/navigation'; import { ThemeToggle } from '@/components/ThemeToggle'; -import { LogOut, ArrowLeft, Trash2, Globe } from 'lucide-react'; -import Image from 'next/image'; +import { LogOut, ArrowLeft, Trash2 } from 'lucide-react'; import Link from 'next/link'; import { AddButtonForm } from '@/components/AddButtonForm'; +import { ICON_MAP, parseIcon } from '@/lib/lucideIcons'; +import { Globe } from 'lucide-react'; export const dynamic = 'force-dynamic'; -function getDomain(url: string): string { - try { return new URL(url).hostname; } catch { return url; } -} - export default async function AdminPage() { const auth = await isAuthenticated(); if (!auth) redirect('/admin/login'); @@ -41,7 +38,7 @@ export default async function AdminPage() {

Administration

-

+

{buttons.length} application{buttons.length !== 1 ? 's' : ''} configurée{buttons.length !== 1 ? 's' : ''}

@@ -56,7 +53,7 @@ export default async function AdminPage() { border: '1px solid rgba(255,255,255,0.12)', borderRadius: '50px', padding: '0.45rem 0.9rem', - color: 'rgba(255,255,255,0.7)', + color: 'rgba(255,255,255,0.6)', fontSize: '0.82rem', fontWeight: 500, display: 'flex', alignItems: 'center', gap: '0.4rem', @@ -72,14 +69,14 @@ export default async function AdminPage() { {/* Button list */} {buttons.length === 0 ? ( -
+
Aucun bouton configuré.
) : (
{(buttons as any[]).map((btn) => { - const domain = getDomain(btn.url); - const faviconUrl = `https://www.google.com/s2/favicons?domain=${domain}&sz=32`; + const { name: iconName, color } = parseIcon(btn.icon); + const IconComponent = ICON_MAP[iconName] || Globe; return (
- {/* Icon: emoji or favicon */}
- {btn.icon ? btn.icon : ( - {btn.name} - )} +
-
{btn.name}
-
+
{btn.url}
@@ -124,13 +116,14 @@ export default async function AdminPage() { }}> diff --git a/components/AddButtonForm.tsx b/components/AddButtonForm.tsx index 881c0e3..67b4767 100644 --- a/components/AddButtonForm.tsx +++ b/components/AddButtonForm.tsx @@ -2,22 +2,25 @@ import React, { useState } from 'react'; import { addButton } from '@/app/actions/admin'; -import { EmojiPicker } from '@/components/EmojiPicker'; +import { IconPicker } from '@/components/IconPicker'; +import { serializeIcon } from '@/lib/lucideIcons'; import { PlusCircle } from 'lucide-react'; export function AddButtonForm() { - const [icon, setIcon] = useState('🔗'); + const [iconName, setIconName] = useState('Globe'); + const [iconColor, setIconColor] = useState('#22d3ee'); const [loading, setLoading] = useState(false); const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); setLoading(true); const formData = new FormData(e.currentTarget); - formData.set('icon', icon); + formData.set('icon', serializeIcon(iconName, iconColor)); try { await addButton(formData); (e.target as HTMLFormElement).reset(); - setIcon('🔗'); + setIconName('Globe'); + setIconColor('#22d3ee'); } finally { setLoading(false); } @@ -34,30 +37,35 @@ export function AddButtonForm() { marginBottom: '2rem', }}>

- AJOUTER UNE APPLICATION + AJOUTER UNE APPLICATION

- {/* Emoji picker */} + {/* Icon picker */}
-
{/* Name */}
-