Files
panel/migrate-project-contacts.mjs
RKWojs 60b79fa360 feat: add contact management functionality
- Implemented ContactForm component for creating and editing contacts.
- Added ProjectContactSelector component to manage project-specific contacts.
- Updated ProjectForm to include ProjectContactSelector for associating contacts with projects.
- Enhanced Card component with a new CardTitle subcomponent for better structure.
- Updated Navigation to include a link to the contacts page.
- Added translations for contact-related terms in the i18n module.
- Initialized contacts database schema and created necessary tables for contact management.
- Developed queries for CRUD operations on contacts, including linking and unlinking contacts to projects.
- Created a test script to validate contact queries against the database.
2025-12-03 16:23:05 +01:00

189 lines
6.0 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import db from './src/lib/db.js';
import initializeDatabase from './src/lib/init-db.js';
console.log('🚀 Migrating contact data from projects...\n');
try {
// Run database initialization to ensure tables exist
initializeDatabase();
console.log('✅ Database tables verified\n');
// Get all projects with contact data
const projectsWithContacts = db.prepare(`
SELECT project_id, project_name, contact
FROM projects
WHERE contact IS NOT NULL AND contact != '' AND TRIM(contact) != ''
`).all();
if (projectsWithContacts.length === 0) {
console.log(' No contact data found in projects to migrate.\n');
process.exit(0);
}
console.log(`📋 Found ${projectsWithContacts.length} projects with contact information\n`);
let created = 0;
let linked = 0;
let skipped = 0;
const createContact = db.prepare(`
INSERT INTO contacts (name, phone, email, contact_type, notes, is_active)
VALUES (?, ?, ?, 'project', ?, 1)
`);
const linkContact = db.prepare(`
INSERT OR IGNORE INTO project_contacts (project_id, contact_id, is_primary, relationship_type)
VALUES (?, ?, 1, 'general')
`);
// Process each project
for (const project of projectsWithContacts) {
try {
const contactText = project.contact.trim();
// Parse contact information - common formats:
// "Jan Kowalski, tel. 123-456-789"
// "Jan Kowalski 123-456-789"
// "123-456-789"
// "Jan Kowalski"
let name = '';
let phone = '';
let email = '';
let notes = '';
// Try to extract email
const emailPattern = /([a-zA-Z0-9._-]+@[a-zA-Z0-9._-]+\.[a-zA-Z0-9_-]+)/;
const emailMatch = contactText.match(emailPattern);
if (emailMatch) {
email = emailMatch[1].trim();
}
// Try to extract phone number (various formats)
const phonePatterns = [
/(?:\+?48)?[\s-]?(\d{3}[\s-]?\d{3}[\s-]?\d{3})/, // Polish: 123-456-789, 123 456 789, +48 123456789
/(?:\+?48)?[\s-]?(\d{9})/, // 9 digits
/tel\.?\s*[:.]?\s*([+\d\s-]+)/i, // tel. 123-456-789
/phone\s*[:.]?\s*([+\d\s-]+)/i, // phone: 123-456-789
/(\d{3}[-\s]?\d{3}[-\s]?\d{3})/, // Generic phone pattern
];
for (const pattern of phonePatterns) {
const match = contactText.match(pattern);
if (match) {
phone = match[1] || match[0];
phone = phone.replace(/\s+/g, ' ').trim();
break;
}
}
// Extract name (text before phone/email or comma)
let textForName = contactText;
if (phone) {
// Remove phone from text to get name
textForName = textForName.replace(phone, '');
}
if (email) {
// Remove email from text to get name
textForName = textForName.replace(email, '');
}
// Remove common prefixes like "tel.", "phone:", "email:", commas, etc.
name = textForName.replace(/tel\.?|phone:?|email:?|e-mail:?|,/gi, '').trim();
// Clean up name
name = name.replace(/^[,\s-]+|[,\s-]+$/g, '').trim();
// If we couldn't extract structured data, use project name and put original text in notes
if (!phone && !email) {
// No structured contact info found, put everything in notes
notes = `Original contact info: ${contactText}`;
name = project.project_name;
} else if (!name) {
// We have phone/email but no clear name
name = project.project_name;
}
// Check if this contact already exists (by name, phone, or email)
let existingContact = null;
if (phone) {
existingContact = db.prepare(`
SELECT contact_id FROM contacts
WHERE phone LIKE ? OR phone LIKE ?
`).get(`%${phone}%`, `%${phone.replace(/\s/g, '')}%`);
}
if (!existingContact && email) {
existingContact = db.prepare(`
SELECT contact_id FROM contacts
WHERE LOWER(email) = LOWER(?)
`).get(email);
}
if (!existingContact && name && name !== project.project_name) {
existingContact = db.prepare(`
SELECT contact_id FROM contacts
WHERE LOWER(name) = LOWER(?)
`).get(name);
}
let contactId;
if (existingContact) {
contactId = existingContact.contact_id;
console.log(` ♻️ Using existing contact "${name}" for project "${project.project_name}"`);
} else {
// Create new contact
const result = createContact.run(
name,
phone || null,
email || null,
notes || `Migrated from project: ${project.project_name}`
);
contactId = result.lastInsertRowid;
created++;
const contactInfo = [];
if (phone) contactInfo.push(`📞 ${phone}`);
if (email) contactInfo.push(`📧 ${email}`);
const infoStr = contactInfo.length > 0 ? ` (${contactInfo.join(', ')})` : '';
console.log(` ✨ Created contact "${name}"${infoStr} for project "${project.project_name}"`);
}
// Link contact to project
linkContact.run(project.project_id, contactId);
linked++;
} catch (error) {
console.error(` ❌ Error processing project "${project.project_name}":`, error.message);
skipped++;
}
}
console.log('\n📊 Migration Summary:');
console.log(` - Contacts created: ${created}`);
console.log(` - Project-contact links created: ${linked}`);
console.log(` - Projects skipped: ${skipped}`);
console.log(` - Total projects processed: ${projectsWithContacts.length}`);
// Show final statistics
const contactsCount = db.prepare('SELECT COUNT(*) as count FROM contacts').get();
const projectContactsCount = db.prepare('SELECT COUNT(*) as count FROM project_contacts').get();
console.log('\n📈 Current Database Statistics:');
console.log(` - Total contacts: ${contactsCount.count}`);
console.log(` - Total project-contact links: ${projectContactsCount.count}`);
console.log('\n✨ Migration complete!');
console.log(' - Visit /contacts to view and manage your contacts');
console.log(' - Edit projects to see linked contacts');
console.log(' - The old contact text field is preserved for reference\n');
} catch (error) {
console.error('❌ Error during migration:', error);
process.exit(1);
}