diff --git a/app/carnet/page.tsx b/app/carnet/page.tsx index 0b65bd7f..ac9b085b 100644 --- a/app/carnet/page.tsx +++ b/app/carnet/page.tsx @@ -43,7 +43,7 @@ interface Contact { export default function CarnetPage() { const { data: session, status } = useSession(); - const [isLoading, setIsLoading] = useState(true); + const [isLoading, setIsLoading] = useState(false); const [layoutMode, setLayoutMode] = useState("item-selection"); const [selectedNote, setSelectedNote] = useState(null); const [isMobile, setIsMobile] = useState(false); @@ -259,55 +259,30 @@ export default function CarnetPage() { const fetchContacts = async (folder: string) => { try { - setIsLoadingContacts(true); - // First, check if we're looking at a specific VCF file - if (folder.endsWith('.vcf')) { - const response = await fetch(`/api/nextcloud/files/content?path=${encodeURIComponent(`/files/cube-${session?.user?.id}/Private/Contacts/${folder}`)}`); - if (response.ok) { - const { content } = await response.json(); - const contacts = parseVCard(content); - setContacts(contacts.map(contact => ({ - ...contact, - group: folder.replace('.vcf', '') - }))); - } - } else { - // If not a VCF file, list all VCF files in the folder - const response = await fetch(`/api/nextcloud/files?folder=${folder}`); - if (response.ok) { - const files = await response.json(); - const vcfFiles = files.filter((file: any) => file.basename.endsWith('.vcf')); - - // Parse VCF files and extract contact information - const parsedContacts = await Promise.all( - vcfFiles.map(async (file: any) => { - try { - const contentResponse = await fetch(`/api/nextcloud/files/content?path=${encodeURIComponent(file.filename)}`); - if (contentResponse.ok) { - const { content } = await contentResponse.json(); - const contacts = parseVCard(content); - return contacts.map(contact => ({ - ...contact, - group: file.basename.replace('.vcf', '') - })); - } - return []; - } catch (error) { - console.error('Error fetching VCF content:', error); - return []; - } - }) - ); - - // Flatten the array of contact arrays - setContacts(parsedContacts.flat().filter(Boolean)); - } + setIsLoading(true); + const response = await fetch(`/api/nextcloud/files?folder=${folder}`); + if (!response.ok) { + throw new Error('Failed to fetch contacts'); } + const data = await response.json(); + const vcfFiles = data.files.filter((file: any) => file.filename.endsWith('.vcf')); + + const contacts = await Promise.all( + vcfFiles.map(async (file: any) => { + const contentResponse = await fetch(`/api/nextcloud/files/content?path=${encodeURIComponent(file.filename)}`); + if (!contentResponse.ok) { + throw new Error(`Failed to fetch content for ${file.filename}`); + } + const content = await contentResponse.text(); + return parseVCard(content); + }) + ); + + setContacts(contacts.flat()); + setIsLoading(false); } catch (error) { console.error('Error fetching contacts:', error); - setContacts([]); - } finally { - setIsLoadingContacts(false); + setIsLoading(false); } }; @@ -433,54 +408,90 @@ export default function CarnetPage() { const handleContactSave = async (contact: Contact) => { try { - const vcfContent = generateVCard(contact); - const response = await fetch('/api/nextcloud/files', { - method: 'PUT', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - path: `/files/cube-${session?.user?.id}/Private/Contacts/${selectedFolder}`, - content: vcfContent - }), - }); - + setIsLoading(true); + const filePath = `/files/cube-${session?.user?.id}/Private/Contacts/${selectedFolder}`; + + // Fetch current VCF content + const response = await fetch(`/api/nextcloud/files/content?path=${encodeURIComponent(filePath)}`); if (!response.ok) { + throw new Error('Failed to fetch VCF content'); + } + const currentContent = await response.text(); + + // Parse current contacts and update/add the edited contact + const existingContacts = currentContent.split('BEGIN:VCARD') + .filter(card => card.trim()) + .map(card => parseVCard('BEGIN:VCARD' + card)) + .flat(); + + const updatedContacts = contact.id + ? existingContacts.map(c => c.id === contact.id ? contact : c) + : [...existingContacts, { ...contact, id: crypto.randomUUID() }]; + + // Generate new VCF content + const newContent = updatedContacts.map(c => generateVCard(c)).join('\r\n'); + + // Save updated VCF file + const saveResponse = await fetch('/api/nextcloud/files', { + method: 'PUT', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ path: filePath, content: newContent }) + }); + + if (!saveResponse.ok) { throw new Error('Failed to save contact'); } - + // Refresh contacts list - fetchContacts(selectedFolder); + await fetchContacts(selectedFolder); + setIsLoading(false); } catch (error) { console.error('Error saving contact:', error); + setIsLoading(false); } }; const handleContactDelete = async (contact: Contact) => { - if (!confirm('Êtes-vous sûr de vouloir supprimer ce contact ?')) { - return; - } - try { - const response = await fetch('/api/nextcloud/files', { - method: 'DELETE', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - path: `/files/cube-${session?.user?.id}/Private/Contacts/${selectedFolder}` - }), - }); - + setIsLoading(true); + const filePath = `/files/cube-${session?.user?.id}/Private/Contacts/${selectedFolder}`; + + // Fetch current VCF content + const response = await fetch(`/api/nextcloud/files/content?path=${encodeURIComponent(filePath)}`); if (!response.ok) { + throw new Error('Failed to fetch VCF content'); + } + const currentContent = await response.text(); + + // Parse current contacts and remove the deleted contact + const existingContacts = currentContent.split('BEGIN:VCARD') + .filter(card => card.trim()) + .map(card => parseVCard('BEGIN:VCARD' + card)) + .flat(); + + const updatedContacts = existingContacts.filter(c => c.id !== contact.id); + + // Generate new VCF content + const newContent = updatedContacts.map(c => generateVCard(c)).join('\r\n'); + + // Save updated VCF file + const saveResponse = await fetch('/api/nextcloud/files', { + method: 'PUT', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ path: filePath, content: newContent }) + }); + + if (!saveResponse.ok) { throw new Error('Failed to delete contact'); } - - // Clear selected contact and refresh list + + // Refresh contacts list + await fetchContacts(selectedFolder); setSelectedContact(null); - fetchContacts('Contacts'); + setIsLoading(false); } catch (error) { console.error('Error deleting contact:', error); + setIsLoading(false); } }; @@ -489,18 +500,17 @@ export default function CarnetPage() { 'BEGIN:VCARD', 'VERSION:3.0', `UID:${contact.id}`, - 'PRODID:-//Infomaniak//Workspace//pim', - contact.fullName ? `FN:${contact.fullName}` : '', - contact.fullName ? `N:${contact.fullName.split(' ').reverse().join(';')};` : '', + `FN:${contact.fullName}`, + contact.email ? `EMAIL;TYPE=INTERNET:${contact.email}` : '', + contact.phone ? `TEL;TYPE=CELL:${contact.phone}` : '', contact.organization ? `ORG:${contact.organization}` : '', - contact.email ? `EMAIL;TYPE=WORK:${contact.email}` : '', - contact.phone ? `TEL;TYPE=WORK:${contact.phone}` : '', - contact.address ? `ADR;TYPE=WORK:;;${contact.address};;;;` : '', + contact.address ? `ADR;TYPE=HOME:;;${contact.address}` : '', contact.notes ? `NOTE:${contact.notes}` : '', + contact.group ? `CATEGORIES:${contact.group}` : '', 'END:VCARD' - ].filter(Boolean); + ]; - return lines.join('\n'); + return lines.filter(line => line).join('\r\n'); }; if (isLoading) {