From 5418447d2952b4c55c3f103058891ce477bd575b Mon Sep 17 00:00:00 2001 From: Julien Froidefond Date: Fri, 19 Dec 2025 10:29:02 +0100 Subject: [PATCH] Update .gitignore to exclude user-uploaded CSV files and remove the group-dev-ad.csv file. Refactor page.tsx to handle CSV file uploads, localStorage management, and enhance user interface for data management. Remove API route for fetching people data from CSV. --- .gitignore | 4 ++ app/api/people/route.ts | 18 ----- app/page.tsx | 145 +++++++++++++++++++++++++++++++++++---- lib/csv-parser-client.ts | 71 +++++++++++++++++++ 4 files changed, 207 insertions(+), 31 deletions(-) delete mode 100644 app/api/people/route.ts create mode 100644 lib/csv-parser-client.ts diff --git a/.gitignore b/.gitignore index 15e8571..1bc2360 100644 --- a/.gitignore +++ b/.gitignore @@ -62,5 +62,9 @@ logs *.tmp *.temp +# CSV files (uploaded by users) +*.csv +!group-dev-ad.csv.example + .pnpm-store diff --git a/app/api/people/route.ts b/app/api/people/route.ts deleted file mode 100644 index 3882419..0000000 --- a/app/api/people/route.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { NextResponse } from 'next/server'; -import { parseCSV } from '@/lib/csv-parser'; -import { join } from 'path'; - -export async function GET() { - try { - const filePath = join(process.cwd(), 'group-dev-ad.csv'); - const people = await parseCSV(filePath); - return NextResponse.json(people); - } catch (error) { - console.error('Error parsing CSV:', error); - return NextResponse.json( - { error: 'Failed to parse CSV file' }, - { status: 500 } - ); - } -} - diff --git a/app/page.tsx b/app/page.tsx index d75ad2b..57254ad 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -1,7 +1,9 @@ 'use client'; -import { useState, useEffect, useMemo } from 'react'; -import { parseCSV, Person } from '@/lib/csv-parser'; +import { useState, useEffect, useMemo, useRef } from 'react'; +import { parseCSVFromText, Person } from '@/lib/csv-parser-client'; + +const STORAGE_KEY = 'people-randomizr-data'; export default function Home() { const [people, setPeople] = useState([]); @@ -11,21 +13,71 @@ export default function Home() { const [randomPeople, setRandomPeople] = useState([]); const [showResults, setShowResults] = useState(false); const [searchPoste, setSearchPoste] = useState(''); + const [hasData, setHasData] = useState(false); + const fileInputRef = useRef(null); + // Charger les données depuis localStorage au démarrage useEffect(() => { - async function loadData() { - try { - const response = await fetch('/api/people'); - const data = await response.json(); + try { + const stored = localStorage.getItem(STORAGE_KEY); + if (stored) { + const data = JSON.parse(stored); setPeople(data); + setHasData(true); + } + } catch (error) { + console.error('Error loading from localStorage:', error); + } finally { + setLoading(false); + } + }, []); + + // Sauvegarder dans localStorage quand les données changent + useEffect(() => { + if (people.length > 0) { + localStorage.setItem(STORAGE_KEY, JSON.stringify(people)); + setHasData(true); + } + }, [people]); + + const handleFileUpload = (event: React.ChangeEvent) => { + const file = event.target.files?.[0]; + if (!file) return; + + if (!file.name.endsWith('.csv')) { + alert('Veuillez sélectionner un fichier CSV'); + return; + } + + const reader = new FileReader(); + reader.onload = (e) => { + try { + const content = e.target?.result as string; + const parsed = parseCSVFromText(content); + setPeople(parsed); + setSelectedPostes(new Set()); + setShowResults(false); + alert(`✅ ${parsed.length} personnes chargées avec succès !`); } catch (error) { - console.error('Error loading data:', error); - } finally { - setLoading(false); + console.error('Error parsing CSV:', error); + alert('Erreur lors du parsing du CSV. Vérifiez le format du fichier.'); + } + }; + reader.readAsText(file, 'UTF-8'); + }; + + const handleClearData = () => { + if (confirm('Êtes-vous sûr de vouloir supprimer toutes les données ?')) { + localStorage.removeItem(STORAGE_KEY); + setPeople([]); + setHasData(false); + setSelectedPostes(new Set()); + setShowResults(false); + if (fileInputRef.current) { + fileInputRef.current.value = ''; } } - loadData(); - }, []); + }; // Compter le nombre de personnes par poste const posteCounts = useMemo(() => { @@ -121,6 +173,51 @@ export default function Home() { ); } + if (!hasData) { + return ( +
+ {/* Effets de fond animés */} +
+
+
+
+
+ +
+
+
+
📊
+

+ People Randomizr +

+

+ Importez votre fichier CSV pour commencer +

+
+ +
+ +

+ Le fichier CSV doit contenir les colonnes : Nom, Description, Type +

+
+
+
+
+ ); + } + return (
{/* Effets de fond animés */} @@ -131,11 +228,33 @@ export default function Home() {
-
+

People Randomizr

-

Sélection intelligente • Extraction aléatoire

+

Sélection intelligente • Extraction aléatoire

+ + {/* Boutons de gestion de données */} +
+ + +
diff --git a/lib/csv-parser-client.ts b/lib/csv-parser-client.ts new file mode 100644 index 0000000..b641edb --- /dev/null +++ b/lib/csv-parser-client.ts @@ -0,0 +1,71 @@ +export interface Person { + nom: string; + description: string; + type: string; +} + +export function parseCSVFromText(content: string): Person[] { + const lines = content.split('\n'); + + // Skip header line + const dataLines = lines.slice(1); + + const people: Person[] = []; + + for (const line of dataLines) { + if (!line.trim()) continue; + + // Parse CSV line (handling quoted fields) + const fields = parseCSVLine(line); + + if (fields.length >= 7) { + const nom = fields[4]?.trim() || ''; + const description = fields[5]?.trim() || ''; + const type = fields[6]?.trim() || ''; + + // Skip empty names and service accounts + if (nom && !nom.startsWith('svc.') && !nom.startsWith('!') && nom !== 'datascience') { + people.push({ + nom, + description, + type, + }); + } + } + } + + return people; +} + +function parseCSVLine(line: string): string[] { + const fields: string[] = []; + let currentField = ''; + let inQuotes = false; + + for (let i = 0; i < line.length; i++) { + const char = line[i]; + + if (char === '"') { + if (inQuotes && line[i + 1] === '"') { + // Escaped quote + currentField += '"'; + i++; + } else { + // Toggle quote state + inQuotes = !inQuotes; + } + } else if (char === ',' && !inQuotes) { + // Field separator + fields.push(currentField); + currentField = ''; + } else { + currentField += char; + } + } + + // Add last field + fields.push(currentField); + + return fields.map(field => field.trim()); +} +