diff --git a/src/app/(admin)/admin/customers/components/EditCustomer.tsx b/src/app/(admin)/admin/customers/components/EditCustomer.tsx
index c8d9a8b..90bf468 100644
--- a/src/app/(admin)/admin/customers/components/EditCustomer.tsx
+++ b/src/app/(admin)/admin/customers/components/EditCustomer.tsx
@@ -9,6 +9,7 @@ interface Customer {
name: string;
url: string;
email: string;
+ city?: string;
createdAt: string;
modifiedAt: string;
}
@@ -23,6 +24,7 @@ export default function EditCustomer({ id }: EditCustomerProps) {
name: '',
url: '',
email: '',
+ city: '',
});
const [isLoading, setIsLoading] = useState(!!id); // Only loading if editing
const [isSubmitting, setIsSubmitting] = useState(false);
@@ -49,6 +51,7 @@ export default function EditCustomer({ id }: EditCustomerProps) {
name: customer.name,
url: customer.url || '',
email: customer.email,
+ city: customer.city || '',
});
setIsLoading(false);
@@ -169,7 +172,7 @@ export default function EditCustomer({ id }: EditCustomerProps) {
/>
-
+
@@ -185,6 +188,21 @@ export default function EditCustomer({ id }: EditCustomerProps) {
/>
+
+
+
+
+
+
+
City
+
+ {customer.city || -}
+
+
Created
diff --git a/src/app/(admin)/admin/customers/page.tsx b/src/app/(admin)/admin/customers/page.tsx
index e49650a..b5a5750 100644
--- a/src/app/(admin)/admin/customers/page.tsx
+++ b/src/app/(admin)/admin/customers/page.tsx
@@ -11,6 +11,7 @@ interface Customer {
name: string;
url: string;
email: string;
+ city?: string;
createdAt: string;
modifiedAt: string;
}
@@ -44,6 +45,7 @@ export default function AdminCustomers() {
name: '',
email: '',
url: '',
+ city: '',
hasEmail: false
});
const [debouncedFilters, setDebouncedFilters] = useState(filters);
@@ -74,6 +76,7 @@ export default function AdminCustomers() {
if (debouncedFilters.name) params.append('name', debouncedFilters.name);
if (debouncedFilters.email) params.append('email', debouncedFilters.email);
if (debouncedFilters.url) params.append('url', debouncedFilters.url);
+ if (debouncedFilters.city) params.append('city', debouncedFilters.city);
if (debouncedFilters.hasEmail) params.append('hasEmail', 'true');
const response = await fetch(`/api/customers?${params.toString()}`);
@@ -116,6 +119,7 @@ export default function AdminCustomers() {
name: '',
email: '',
url: '',
+ city: '',
hasEmail: false
});
};
@@ -238,6 +242,31 @@ export default function AdminCustomers() {
)}
+
+
+
+
handleFilterChange('city', e.target.value)}
+ className="block w-full rounded-md border-gray-300 pr-10 focus:border-indigo-500 focus:ring-indigo-500 dark:bg-gray-700 dark:border-gray-600 dark:text-white sm:text-sm"
+ placeholder="Enter city..."
+ />
+ {filters.city && (
+
+ )}
+
+
- {(filters.name || filters.email || filters.url || filters.hasEmail) && (
+ {(filters.name || filters.email || filters.url || filters.city || filters.hasEmail) && (
Active filters: {[
filters.name && 'Name',
filters.email && 'Email',
filters.url && 'URL',
+ filters.city && 'City',
filters.hasEmail && 'Has Email'
].filter(Boolean).join(', ')}
@@ -321,6 +351,12 @@ export default function AdminCustomers() {
>
Email
+
+ City
+ |
+ |
+ {customer.city || -}
+ |
{new Date(customer.createdAt).toLocaleDateString()}
|
@@ -399,7 +438,7 @@ export default function AdminCustomers() {
))
) : (
- |
+ |
No customers found. Create your first customer!
|
diff --git a/src/app/api/customers/[id]/route.ts b/src/app/api/customers/[id]/route.ts
index bb6e1d7..9496603 100644
--- a/src/app/api/customers/[id]/route.ts
+++ b/src/app/api/customers/[id]/route.ts
@@ -55,7 +55,7 @@ export async function PUT(
}
const data = await request.json();
- const { name, url, email } = data;
+ const { name, url, email, city } = data;
// Validate required fields
if (!name || !email) {
@@ -69,6 +69,7 @@ export async function PUT(
customer.name = name;
customer.url = url || '';
customer.email = email;
+ customer.city = city || null;
// Save the updated customer
const updatedCustomer = await customerRepository.save(customer);
diff --git a/src/app/api/customers/route.ts b/src/app/api/customers/route.ts
index c33eae3..f377752 100644
--- a/src/app/api/customers/route.ts
+++ b/src/app/api/customers/route.ts
@@ -19,6 +19,7 @@ export async function GET(request: NextRequest) {
const nameFilter = url.searchParams.get('name');
const emailFilter = url.searchParams.get('email');
const urlFilter = url.searchParams.get('url');
+ const cityFilter = url.searchParams.get('city');
const hasEmailFilter = url.searchParams.get('hasEmail');
// Build query
@@ -34,6 +35,9 @@ export async function GET(request: NextRequest) {
if (urlFilter) {
queryBuilder = queryBuilder.andWhere('LOWER(customer.url) LIKE LOWER(:url)', { url: `%${urlFilter}%` });
}
+ if (cityFilter) {
+ queryBuilder = queryBuilder.andWhere('LOWER(customer.city) LIKE LOWER(:city)', { city: `%${cityFilter}%` });
+ }
if (hasEmailFilter === 'true') {
queryBuilder = queryBuilder.andWhere('customer.email IS NOT NULL AND customer.email != :emptyString', { emptyString: '' });
}
@@ -80,7 +84,7 @@ export async function POST(request: NextRequest) {
const customerRepository = dataSource.getRepository(Customer);
const data = await request.json();
- const { name, url, email } = data;
+ const { name, url, email, city } = data;
// Validate required fields
if (!name || !email) {
@@ -95,6 +99,7 @@ export async function POST(request: NextRequest) {
customer.name = name;
customer.url = url || '';
customer.email = email;
+ customer.city = city || null;
const savedCustomer = await customerRepository.save(customer);
diff --git a/src/lib/database/entities/Customer.ts b/src/lib/database/entities/Customer.ts
index 3ef2154..48013fb 100644
--- a/src/lib/database/entities/Customer.ts
+++ b/src/lib/database/entities/Customer.ts
@@ -15,6 +15,9 @@ export class Customer {
@Column()
email: string;
+ @Column({ nullable: true })
+ city: string;
+
@CreateDateColumn()
createdAt: Date;
diff --git a/src/scripts/import-customers.ts b/src/scripts/import-customers.ts
index 9c9c4b5..55b27eb 100644
--- a/src/scripts/import-customers.ts
+++ b/src/scripts/import-customers.ts
@@ -28,65 +28,78 @@ async function importCustomers(csvFilePath: string): Promise {
console.log(`Found ${records.length} records in CSV file`);
- // Track processed emails to skip duplicates
- const processedEmails = new Set();
- // Track existing names to ensure uniqueness
- const existingNames = new Set(
- (await customerRepository.find()).map(customer => customer.name)
- );
+ // Get existing customers for update
+ const existingCustomers = await customerRepository.find();
+ const customersByEmail = new Map();
+ const customersByName = new Map();
+
+ // Create lookup maps for faster access
+ existingCustomers.forEach(customer => {
+ if (customer.email) {
+ customersByEmail.set(customer.email.toLowerCase(), customer);
+ }
+ customersByName.set(customer.name.toLowerCase(), customer);
+ });
let importedCount = 0;
- let skippedDuplicateEmail = 0;
- let skippedDuplicateName = 0;
+ let updatedCount = 0;
for (const record of records) {
const email = record.Email === 'null' ? '' : record.Email;
const name = record.Name;
- // Skip if email is already processed (not empty and already seen)
- if (email && processedEmails.has(email)) {
- console.log(`Skipping record with duplicate email: ${email}`);
- skippedDuplicateEmail++;
- continue;
+ let customer: Customer;
+ let isUpdate = false;
+
+ // Check if customer exists by email or name
+ if (email && customersByEmail.has(email.toLowerCase())) {
+ // Update existing customer by email
+ customer = customersByEmail.get(email.toLowerCase())!;
+ isUpdate = true;
+ console.log(`Updating customer with email: ${email}`);
+ } else if (customersByName.has(name.toLowerCase())) {
+ // Update existing customer by name
+ customer = customersByName.get(name.toLowerCase())!;
+ isUpdate = true;
+ console.log(`Updating customer with name: ${name}`);
+ } else {
+ // Create new customer
+ customer = new Customer();
+ console.log(`Creating new customer: ${name}`);
}
- // Skip if name already exists in database
- if (existingNames.has(name)) {
- console.log(`Skipping record with duplicate name: ${name}`);
- skippedDuplicateName++;
- continue;
- }
-
- // Add to processed sets
- if (email) {
- processedEmails.add(email);
- }
- existingNames.add(name);
-
- // Create new customer
- const customer = new Customer();
+ // Update customer fields
customer.name = name;
- customer.url = record['URL'] === 'null' ? '' : record['URL'];
+ customer.url = record.URL === 'null' ? '' : record.URL;
customer.email = email;
+ customer.city = record.City === 'null' ? '' : record.City;
try {
-
// Save to database
await customerRepository.save(customer);
- importedCount++;
- console.log(`Imported customer: ${name}`);
+ if (isUpdate) {
+ updatedCount++;
+ console.log(`Updated customer: ${name}`);
+ } else {
+ importedCount++;
+ console.log(`Imported customer: ${name}`);
+
+ // Add to lookup maps for future reference
+ if (email) {
+ customersByEmail.set(email.toLowerCase(), customer);
+ }
+ customersByName.set(name.toLowerCase(), customer);
+ }
} catch (e) {
- console.log(`Skipped: ${name}`);
+ console.log(`Error saving customer: ${name}`, e);
}
-
}
console.log('Import summary:');
console.log(`- Total records in CSV: ${records.length}`);
- console.log(`- Successfully imported: ${importedCount}`);
- console.log(`- Skipped (duplicate email): ${skippedDuplicateEmail}`);
- console.log(`- Skipped (duplicate name): ${skippedDuplicateName}`);
+ console.log(`- Successfully imported (new): ${importedCount}`);
+ console.log(`- Successfully updated: ${updatedCount}`);
} catch (error) {
console.error('Error importing customers:', error);