mirror of
https://github.com/arthur-pbty/form.git
synced 2026-06-11 15:56:20 +02:00
195 lines
9.7 KiB
TypeScript
195 lines
9.7 KiB
TypeScript
"use client"
|
|
|
|
import { useEffect, useState } from "react"
|
|
import Link from "next/link"
|
|
import { getMyForms, removeForm, LocalStorageForm } from "@/lib/localStorage"
|
|
|
|
export default function MyFormsPage() {
|
|
const [forms, setForms] = useState<LocalStorageForm[]>([])
|
|
const [loading, setLoading] = useState(true)
|
|
const [deleteConfirm, setDeleteConfirm] = useState<string | null>(null)
|
|
const [deleting, setDeleting] = useState(false)
|
|
|
|
useEffect(() => {
|
|
setForms(getMyForms())
|
|
setLoading(false)
|
|
}, [])
|
|
|
|
const handleDelete = async (publicId: string, secretKey: string) => {
|
|
setDeleting(true)
|
|
try {
|
|
const response = await fetch(`/api/forms/${publicId}?secret=${secretKey}`, {
|
|
method: "DELETE",
|
|
})
|
|
|
|
if (response.ok) {
|
|
removeForm(publicId)
|
|
setForms(getMyForms())
|
|
}
|
|
} catch (error) {
|
|
console.error("Error deleting form:", error)
|
|
} finally {
|
|
setDeleting(false)
|
|
setDeleteConfirm(null)
|
|
}
|
|
}
|
|
|
|
const copyLink = async (publicId: string) => {
|
|
const url = `${window.location.origin}/formulaire/${publicId}`
|
|
await navigator.clipboard.writeText(url)
|
|
}
|
|
|
|
if (loading) {
|
|
return (
|
|
<div className="min-h-screen bg-gray-50 flex items-center justify-center">
|
|
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600"></div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
return (
|
|
<div className="min-h-screen bg-gray-50 py-8">
|
|
<div className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8">
|
|
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between gap-4 mb-6 sm:mb-8">
|
|
<div>
|
|
<h1 className="text-2xl sm:text-3xl font-bold text-gray-900">Mes formulaires</h1>
|
|
<p className="mt-1 sm:mt-2 text-sm sm:text-base text-gray-600">
|
|
Retrouvez tous vos formulaires créés sur cet appareil.
|
|
</p>
|
|
</div>
|
|
<Link
|
|
href="/creer"
|
|
className="w-full sm:w-auto px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors flex items-center justify-center font-medium"
|
|
>
|
|
<svg className="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 4v16m8-8H4" />
|
|
</svg>
|
|
Nouveau formulaire
|
|
</Link>
|
|
</div>
|
|
|
|
{forms.length === 0 ? (
|
|
<div className="bg-white rounded-xl shadow-sm border border-gray-200 p-6 sm:p-12 text-center">
|
|
<div className="w-20 h-20 bg-gray-100 rounded-full flex items-center justify-center mx-auto mb-6">
|
|
<svg className="w-10 h-10 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
|
|
</svg>
|
|
</div>
|
|
<h2 className="text-xl font-semibold text-gray-900 mb-2">Aucun formulaire</h2>
|
|
<p className="text-gray-600 mb-6">
|
|
Vous n'avez pas encore créé de formulaire sur cet appareil.
|
|
</p>
|
|
<Link
|
|
href="/creer"
|
|
className="inline-flex items-center px-6 py-3 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors font-medium"
|
|
>
|
|
<svg className="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M12 4v16m8-8H4" />
|
|
</svg>
|
|
Créer mon premier formulaire
|
|
</Link>
|
|
</div>
|
|
) : (
|
|
<div className="space-y-4">
|
|
{forms.map((form) => (
|
|
<div
|
|
key={form.publicId}
|
|
className="bg-white rounded-xl shadow-sm border border-gray-200 p-6"
|
|
>
|
|
<div className="flex flex-col md:flex-row md:items-center md:justify-between gap-4">
|
|
<div className="flex-1">
|
|
<h2 className="text-lg font-semibold text-gray-900">{form.title}</h2>
|
|
<p className="text-sm text-gray-500 mt-1">
|
|
Créé le {new Date(form.createdAt).toLocaleDateString("fr-FR", {
|
|
day: "numeric",
|
|
month: "long",
|
|
year: "numeric",
|
|
hour: "2-digit",
|
|
minute: "2-digit",
|
|
})}
|
|
</p>
|
|
</div>
|
|
<div className="flex flex-wrap gap-2">
|
|
<button
|
|
onClick={() => copyLink(form.publicId)}
|
|
className="px-3 py-2 bg-gray-100 text-gray-700 rounded-lg hover:bg-gray-200 transition-colors flex items-center text-sm font-medium"
|
|
>
|
|
<svg className="w-4 h-4 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8.684 13.342C8.886 12.938 9 12.482 9 12c0-.482-.114-.938-.316-1.342m0 2.684a3 3 0 110-2.684m0 2.684l6.632 3.316m-6.632-6l6.632-3.316m0 0a3 3 0 105.367-2.684 3 3 0 00-5.367 2.684zm0 9.316a3 3 0 105.368 2.684 3 3 0 00-5.368-2.684z" />
|
|
</svg>
|
|
Partager
|
|
</button>
|
|
<Link
|
|
href={`/formulaire/${form.publicId}`}
|
|
className="px-3 py-2 bg-gray-100 text-gray-700 rounded-lg hover:bg-gray-200 transition-colors flex items-center text-sm font-medium"
|
|
>
|
|
<svg className="w-4 h-4 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z" />
|
|
</svg>
|
|
Voir
|
|
</Link>
|
|
<Link
|
|
href={`/formulaire/${form.publicId}/resultats?secret=${form.secretKey}`}
|
|
className="px-3 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors flex items-center text-sm font-medium"
|
|
>
|
|
<svg className="w-4 h-4 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z" />
|
|
</svg>
|
|
Résultats
|
|
</Link>
|
|
{deleteConfirm === form.publicId ? (
|
|
<div className="flex items-center gap-2">
|
|
<button
|
|
onClick={() => handleDelete(form.publicId, form.secretKey)}
|
|
disabled={deleting}
|
|
className="px-3 py-2 bg-red-600 text-white rounded-lg hover:bg-red-700 transition-colors text-sm font-medium disabled:opacity-50"
|
|
>
|
|
{deleting ? "..." : "Confirmer"}
|
|
</button>
|
|
<button
|
|
onClick={() => setDeleteConfirm(null)}
|
|
className="px-3 py-2 bg-gray-200 text-gray-700 rounded-lg hover:bg-gray-300 transition-colors text-sm font-medium"
|
|
>
|
|
Annuler
|
|
</button>
|
|
</div>
|
|
) : (
|
|
<button
|
|
onClick={() => setDeleteConfirm(form.publicId)}
|
|
className="px-3 py-2 bg-red-100 text-red-700 rounded-lg hover:bg-red-200 transition-colors flex items-center text-sm font-medium"
|
|
>
|
|
<svg className="w-4 h-4 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
|
|
</svg>
|
|
Supprimer
|
|
</button>
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
)}
|
|
|
|
{/* Information */}
|
|
<div className="mt-8 bg-blue-50 border border-blue-100 rounded-xl p-6">
|
|
<div className="flex items-start space-x-3">
|
|
<svg className="w-6 h-6 text-blue-600 flex-shrink-0 mt-0.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
|
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
|
|
</svg>
|
|
<div>
|
|
<h3 className="font-medium text-blue-900">Information importante</h3>
|
|
<p className="text-sm text-blue-700 mt-1">
|
|
Vos formulaires sont sauvegardés localement sur cet appareil.
|
|
Si vous effacez les données de votre navigateur ou changez d'appareil,
|
|
vous perdrez l'accès aux résultats. Pensez à sauvegarder les liens d'administration.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|