diff --git a/backend/app/Controllers/GroupController.php b/backend/app/Controllers/GroupController.php index 7f567e6..37bd615 100644 --- a/backend/app/Controllers/GroupController.php +++ b/backend/app/Controllers/GroupController.php @@ -70,4 +70,47 @@ class GroupController extends BaseController $response->json(['status' => 'success', 'message' => 'Contact added to group']); } + + /** + * Attach multiple contacts to a group in bulk + */ + public function bulkAddContacts(Request $request, Response $response) + { + $errors = $this->validate($request, [ + 'group_id' => 'required', + 'contact_ids' => 'required' + ]); + if (!empty($errors)) { + $response->status(400)->json(['status' => 'error', 'errors' => $errors]); + return; + } + + $body = $request->getBody(); + $groupId = (int)$body['group_id']; + $contactIds = $body['contact_ids']; + + if (!is_array($contactIds)) { + $response->status(400)->json(['status' => 'error', 'message' => 'contact_ids must be an array']); + return; + } + + $groupModel = new ContactGroup(); + + $pdo = \App\Core\Database::getConnection(); + try { + $pdo->beginTransaction(); + foreach ($contactIds as $contactId) { + $groupModel->attachContact($groupId, (int)$contactId); + } + $pdo->commit(); + } catch (\Exception $e) { + if ($pdo->inTransaction()) { + $pdo->rollBack(); + } + $response->status(500)->json(['status' => 'error', 'message' => 'Bulk insert failed: ' . $e->getMessage()]); + return; + } + + $response->json(['status' => 'success', 'message' => 'Contacts added to group in bulk']); + } } diff --git a/backend/public/index.html b/backend/public/index.html index ab8198a..e26e56f 100644 --- a/backend/public/index.html +++ b/backend/public/index.html @@ -713,7 +713,7 @@ 📱 WhatsApp Connection - + 👥 Contacts Directory @@ -846,10 +846,31 @@ + Add Contact + + + + Selected: contact(s) + + + + -- Add to Existing Group -- + + + + + or + + Apply Grouping + + + + + c.id) : []" :checked="contacts.length > 0 && selectedContactIds.length === contacts.length" style="cursor: pointer; width: 16px; height: 16px;"> + Name Phone Email @@ -859,6 +880,9 @@ + + + @@ -1154,6 +1178,9 @@ activeDashboardTab: 'whatsapp', whatsappSession: null, contacts: [], + selectedContactIds: [], + bulkGroupId: '', + bulkNewGroupName: '', templates: [], campaigns: [], pollingIntervalId: null, @@ -1397,6 +1424,9 @@ // CRM List Fetchers async fetchContacts() { + this.selectedContactIds = []; + this.bulkGroupId = ''; + this.bulkNewGroupName = ''; try { const response = await fetch('/api/contacts', { headers: { 'Authorization': `Bearer ${this.token}` } @@ -1410,6 +1440,67 @@ } }, + async applyBulkGroup() { + if (this.selectedContactIds.length === 0) { + alert('Please select at least one contact first.'); + return; + } + if (!this.bulkGroupId && !this.bulkNewGroupName.trim()) { + alert('Please select an existing group or type a new group name.'); + return; + } + + this.actionLoading = true; + try { + let groupId = this.bulkGroupId; + + // If a new group name is typed, create the group first + if (this.bulkNewGroupName.trim()) { + const newGroupRes = await fetch('/api/groups', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${this.token}` + }, + body: JSON.stringify({ name: this.bulkNewGroupName.trim() }) + }); + const newGroupData = await newGroupRes.json(); + if (!newGroupRes.ok) { + throw new Error(newGroupData.message || newGroupData.error || 'Failed to create group'); + } + groupId = newGroupData.id; + } + + // Attach selected contacts in bulk + const bulkRes = await fetch('/api/groups/bulk-add', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${this.token}` + }, + body: JSON.stringify({ + group_id: groupId, + contact_ids: this.selectedContactIds + }) + }); + const bulkData = await bulkRes.json(); + if (bulkRes.ok) { + alert('Successfully added selected contacts to the group!'); + this.selectedContactIds = []; + this.bulkGroupId = ''; + this.bulkNewGroupName = ''; + await this.fetchGroups(); + } else { + alert(bulkData.error || bulkData.message || 'Failed to apply grouping.'); + } + } catch (err) { + console.error('Error applying bulk grouping:', err); + alert(err.message || 'An error occurred during bulk grouping.'); + } finally { + this.actionLoading = false; + } + }, + async fetchTemplates() { try { const response = await fetch('/api/templates', { diff --git a/backend/public/index.php b/backend/public/index.php index 84b1ff4..18a1998 100644 --- a/backend/public/index.php +++ b/backend/public/index.php @@ -56,6 +56,7 @@ $router->post('/api/contacts', [\App\Controllers\ContactController::class, ' $router->get('/api/groups', [\App\Controllers\GroupController::class, 'index'], [\App\Middlewares\AuthMiddleware::class]); $router->post('/api/groups', [\App\Controllers\GroupController::class, 'store'], [\App\Middlewares\AuthMiddleware::class]); $router->post('/api/groups/add', [\App\Controllers\GroupController::class, 'addContact'], [\App\Middlewares\AuthMiddleware::class]); +$router->post('/api/groups/bulk-add', [\App\Controllers\GroupController::class, 'bulkAddContacts'], [\App\Middlewares\AuthMiddleware::class]); $router->get('/api/templates', [\App\Controllers\TemplateController::class, 'index'], [\App\Middlewares\AuthMiddleware::class]); $router->post('/api/templates', [\App\Controllers\TemplateController::class, 'store'], [\App\Middlewares\AuthMiddleware::class]);