@mahe_pkm/buzl-capi 0.1.4 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -6,7 +6,7 @@
6
6
  *
7
7
  * FEATURES:
8
8
  * 1. 'All Leads' (Master Sheet): Inbound leads insert at Row 2 (Top of sheet).
9
- * 2. Guaranteed Forward Layout: Handled By & Comments sit right next to Lead Stage.
9
+ * 2. Standard 15-Column CRM Layout: Name, Location, Phone, Lead Stage, Event Time, Is Qualified, Qualified Date, Is Spam, Handled By, Comments, Action Source, Source, UTM Source, UTM Campaign, Lead ID.
10
10
  * 3. Universal Fuzzy Matcher: Case-insensitive & whitespace-tolerant header mapping.
11
11
  * 4. Official Buzl Navy Header: #1E4E9E background with bold white text (#FFFFFF).
12
12
  * 5. Full-Row Highlighting: Entire rows (A:Z) color-coded by Lead Stage (LOWER formula).
@@ -21,17 +21,18 @@
21
21
  // Core column definitions
22
22
  var MASTER_SHEET_NAME = 'All Leads';
23
23
  var STATUS_TABS = ['New', 'Contacted', 'Qualified', 'Converted', 'Spam', 'Test'];
24
- var DEFAULT_TEAM_MEMBERS = ['Dr. Samya', 'Dr. John', 'Sales Rep', 'Front Desk'];
24
+ // Configurable Team Members (Leave empty [] to discover reps dynamically from sheet entries)
25
+ var DEFAULT_TEAM_MEMBERS = [];
25
26
 
26
- // Forward Operational Layout: Handled By and Comments sit right next to Lead Stage
27
+ // Standard 15-Column CRM Layout
27
28
  var STANDARD_CRM_COLUMNS = [
28
29
  'Lead Stage',
29
- 'Handled By',
30
- 'Comments',
30
+ 'Event Time',
31
31
  'Is Qualified',
32
32
  'Qualified Date',
33
33
  'Is Spam',
34
- 'Event Time',
34
+ 'Handled By',
35
+ 'Comments',
35
36
  'Action Source',
36
37
  'Source',
37
38
  'UTM Source',
@@ -316,10 +317,9 @@ function ensureCrmLayout(sheet, dynamicFields, forceReorganize) {
316
317
  if (lastRow === 0) {
317
318
  var initialHeaders = ['Name', 'Location', 'Phone'];
318
319
  if (dynamicFields) {
319
- for (var df in dynamicFields) initialHeaders.push(df);
320
- }
321
- if (initialHeaders.indexOf('Service') === -1) {
322
- initialHeaders.push('Service');
320
+ for (var df in dynamicFields) {
321
+ if (initialHeaders.indexOf(df) === -1) initialHeaders.push(df);
322
+ }
323
323
  }
324
324
  initialHeaders = initialHeaders.concat(STANDARD_CRM_COLUMNS);
325
325
 
@@ -341,9 +341,7 @@ function ensureCrmLayout(sheet, dynamicFields, forceReorganize) {
341
341
  var needsReorganization = forceReorganize === true ||
342
342
  leadStageIdx === -1 ||
343
343
  handledByIdx === -1 ||
344
- commentsIdx === -1 ||
345
- handledByIdx !== leadStageIdx + 1 ||
346
- commentsIdx !== leadStageIdx + 2;
344
+ commentsIdx === -1;
347
345
 
348
346
  if (needsReorganization) {
349
347
  // 1. Build index mapping for all standard fields
@@ -352,7 +350,7 @@ function ensureCrmLayout(sheet, dynamicFields, forceReorganize) {
352
350
  idxMap[key] = findHeaderIndex(oldHeaders, COLUMN_ALIASES[key]);
353
351
  }
354
352
 
355
- // 2. Build target headers: Name, Location, Phone, [Email if present], [Dynamic fields / Service], Standard CRM Columns
353
+ // 2. Build target headers: Name, Location, Phone, [Email if present], [Dynamic fields], [Service if present], Standard CRM Columns
356
354
  var targetHeaders = ['Name', 'Location', 'Phone'];
357
355
  if (idxMap.email > -1) {
358
356
  targetHeaders.push('Email');
@@ -362,7 +360,7 @@ function ensureCrmLayout(sheet, dynamicFields, forceReorganize) {
362
360
  if (targetHeaders.indexOf(k) === -1) targetHeaders.push(k);
363
361
  }
364
362
  }
365
- if (targetHeaders.indexOf('Service') === -1) {
363
+ if (idxMap.service > -1 && targetHeaders.indexOf('Service') === -1) {
366
364
  targetHeaders.push('Service');
367
365
  }
368
366
  targetHeaders = targetHeaders.concat(STANDARD_CRM_COLUMNS);
@@ -838,13 +836,13 @@ function onOpen() {
838
836
  function menuInitCrm() {
839
837
  var ss = SpreadsheetApp.getActiveSpreadsheet();
840
838
  var master = getOrCreateMasterSheet(ss);
841
- var headers = ensureCrmLayout(master, { Service: 'General Consultation' }, true);
839
+ var headers = ensureCrmLayout(master, null, false);
842
840
  initializeCrmTabs(ss, headers);
843
841
  applyDropdownValidations(master, headers, null, ss);
844
842
  applyColumnWidths(master, headers);
845
843
  applyColorCoding(master, headers);
846
844
  applyDateFormatting(master, headers);
847
- SpreadsheetApp.getUi().alert('✔ Buzl CRM Upgraded! Columns reorganized forward, full-row colors active, and team tabs synced.');
845
+ SpreadsheetApp.getUi().alert('✔ Buzl CRM Upgraded! CRM headers verified, full-row colors active, and team tabs synced.');
848
846
  }
849
847
 
850
848
  function menuSortNewestFirst() {
package/CHANGELOG.md CHANGED
@@ -5,6 +5,27 @@ All notable changes to the **Buzl Tracker & Conversions API (CAPI)** project wil
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.1.5] - 2026-09-22
9
+
10
+ ### Added
11
+ - **Package Release Date & Time Visibility Across Terminal & Web GUI**:
12
+ - Baked `"updatedAt": "22-Sep-2026 05:30 PM IST"` into `package.json` for deterministic timestamp discovery across NPM global installs and `npx` executions.
13
+ - Interactive terminal wizard ASCII banner now displays `v0.1.5 • Updated: 22-Sep-2026 05:30 PM IST`.
14
+ - Target Project Scope Card in terminal displays `Engine : v0.1.5 (Updated: 22-Sep-2026 05:30 PM IST)`.
15
+ - Added `-v` and `--version` CLI flags printing package name, version, and release timestamp.
16
+ - Web GUI top header bar features a dedicated `#updateBadge` alongside `#versionBadge` dynamically synchronized via `/api/scan` and `/api/version`.
17
+ - Audit report PDF and JSON export generators dynamically include engine version and release timestamp.
18
+
19
+ ### Refactored & Improved
20
+ - **Zero-Hardcoded Handled By in Google Apps Script Engine**:
21
+ - Cleared legacy hardcoded doctor/rep placeholders (`Dr. Samya`, `Dr. John`, `Sales Rep`, `Front Desk`) from `Buzl_GoogleAppsScript_Template.gs` and `src/templates/GoogleAppsScript.gs`.
22
+ - Set `DEFAULT_TEAM_MEMBERS = []`; team member sub-tabs and dropdown validations are now derived 100% dynamically from actual entries in the sheet.
23
+ - **15-Column Standard CRM Layout Synchronization**:
24
+ - Aligned Google Apps Script CRM headers with standard sequence (`Name`, `Location`, `Phone`, `Lead Stage`, `Event Time`, `Is Qualified`, `Qualified Date`, `Is Spam`, `Handled By`, `Comments`, `Action Source`, `Source`, `UTM Source`, `UTM Campaign`, `Lead ID`).
25
+ - Removed forced injection of unnecessary `Service` columns on pre-existing CRM sheets.
26
+
27
+ ---
28
+
8
29
  ## [0.1.4] - 2026-09-14
9
30
 
10
31
  ### Fixed
package/README.md CHANGED
@@ -1,9 +1,9 @@
1
1
  # ⚡ Buzl Tracker & Conversions API (`@mahe_pkm/buzl-capi`)
2
2
 
3
- [![npm version](https://img.shields.io/badge/version-0.1.4-blue.svg)](https://github.com/mahe-pkm/Buzl_CAPI)
3
+ [![npm version](https://img.shields.io/badge/version-0.1.5-blue.svg)](https://github.com/mahe-pkm/Buzl_CAPI)
4
4
  [![Node.js Version](https://img.shields.io/badge/node-%3E%3D16.0.0-brightgreen.svg)](https://nodejs.org)
5
5
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
6
- [![Tests: Passing](https://img.shields.io/badge/tests-38%2F38%20passing-success.svg)](tests/)
6
+ [![Tests: Passing](https://img.shields.io/badge/tests-42%2F42%20passing-success.svg)](tests/)
7
7
  [![Zero Dependencies](https://img.shields.io/badge/dependencies-0%20external-purple.svg)](package.json)
8
8
  [![User Handbook](https://img.shields.io/badge/docs-User%20Handbook%20(PDF)-orange.svg)](USER_HANDBOOK.md)
9
9
 
@@ -421,8 +421,9 @@
421
421
  <h1>📖 Buzl Tracker &amp; Conversions API Suite</h1>
422
422
  <p>All-in-one cross-platform reference guide with isolated, copy-ready command blocks for Windows, macOS, and Linux.</p>
423
423
  <div class="badge-bar">
424
- <span class="badge">Version 0.1.4</span>
425
- <span class="badge badge-success">38/38 Tests Passing</span>
424
+ <span class="badge">Version 0.1.5</span>
425
+ <span class="badge">Updated: 22-Sep-2026 05:30 PM IST</span>
426
+ <span class="badge badge-success">42/42 Tests Passing</span>
426
427
  <span class="badge">Locations Design System</span>
427
428
  <span class="badge">WCAG 2.2 AA Certified</span>
428
429
  <span class="badge">Zero External Dependencies</span>
@@ -485,6 +486,12 @@
485
486
  <td>—</td>
486
487
  <td>Opens this User Handbook in your default browser.</td>
487
488
  </tr>
489
+ <tr>
490
+ <td><code>--version</code></td>
491
+ <td><code>-v</code></td>
492
+ <td>—</td>
493
+ <td>Displays package version and last update release timestamp.</td>
494
+ </tr>
488
495
  <tr>
489
496
  <td><code>--help</code></td>
490
497
  <td><code>-h</code></td>
package/USER_HANDBOOK.md CHANGED
@@ -282,6 +282,7 @@ npm publish --tag latest --otp=123456
282
282
  | `--restore [name]` | `-r`, `--rollback` | `[dir]` | Restores files to a named snapshot (or latest). |
283
283
  | `--uninstall` | `-u` | `[dir]` | Cleanly removes all injected tracking code from site. |
284
284
  | `--handbook` | — | — | Opens the printable User Handbook in default browser. |
285
+ | `--version` | `-v` | — | Displays the current version and last update release timestamp. |
285
286
  | `--help` | `-h` | — | Displays the command-line usage manual. |
286
287
 
287
288
  > [!TIP]
package/bin/cli.js CHANGED
@@ -40,12 +40,20 @@ const args = process.argv.slice(2);
40
40
  const positionalArgs = args.filter(a => !a.startsWith('-'));
41
41
  const rootDir = positionalArgs.length > 0 ? path.resolve(positionalArgs[0]) : process.cwd();
42
42
 
43
+ // ============================================================================
44
+ // COMMAND: --version / -v (Display Current Version & Release Date)
45
+ // ============================================================================
46
+ if (args.includes('--version') || args.includes('-v')) {
47
+ console.log(`${pkg.name} v${pkg.version} (Updated: ${pkg.updatedAt || '22-Sep-2026 05:30 PM IST'})`);
48
+ process.exit(0);
49
+ }
50
+
43
51
  // ============================================================================
44
52
  // COMMAND: --help / -h (Display Help Manual)
45
53
  // ============================================================================
46
54
  if (args.includes('--help') || args.includes('-h')) {
47
55
  console.log(`
48
- ⚡ BUZL TRACKING & FORM DISPATCHER CLI (v${pkg.version})
56
+ ⚡ BUZL TRACKING & FORM DISPATCHER CLI (v${pkg.version} • Updated: ${pkg.updatedAt || '22-Sep-2026 05:30 PM IST'})
49
57
 
50
58
  USAGE:
51
59
  $ npx buzl-tracker [dir] Launch interactive terminal wizard
@@ -55,6 +63,7 @@ USAGE:
55
63
  $ npx buzl-tracker [dir] --restore [name] Restore a specific backup or latest snapshot
56
64
  $ npx buzl-tracker [dir] --uninstall Cleanly remove all tracking tags from site
57
65
  $ npx buzl-tracker --handbook Open official User Handbook in default browser
66
+ $ npx buzl-tracker --version Display version and last update release timestamp
58
67
  $ npx buzl-tracker --help Show this help reference manual
59
68
 
60
69
  ARGUMENTS:
@@ -66,6 +75,7 @@ OPTIONS:
66
75
  -r, --restore [name] Revert HTML files to a previous snapshot or 'latest'
67
76
  -u, --uninstall Strip GTM, Meta Pixel, runtime scripts, and form hooks
68
77
  --handbook Open printable cross-platform User Handbook in browser
78
+ -v, --version Show current package version and release timestamp
69
79
  -h, --help Display usage guide and command summary
70
80
 
71
81
  KEY CAPABILITIES:
@@ -167,7 +177,7 @@ if (args.includes('--uninstall') || args.includes('-u')) {
167
177
  // ============================================================================
168
178
  if (args.includes('--gui') || args.includes('-g')) {
169
179
  // Start local web GUI server on port 3333
170
- console.log(`⚡ Launching Buzl Tracker Web GUI for: ${rootDir}`);
180
+ console.log(`⚡ Launching Buzl Tracker Web GUI v${pkg.version} (Updated: ${pkg.updatedAt || '22-Sep-2026 05:30 PM IST'}) for: ${rootDir}`);
171
181
  startGuiServer(rootDir);
172
182
  } else {
173
183
  // Start interactive terminal wizard with step-by-step CLI prompts
package/package.json CHANGED
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "name": "@mahe_pkm/buzl-capi",
3
- "version": "0.1.4",
3
+ "version": "0.1.5",
4
+ "updatedAt": "22-Sep-2026 05:30 PM IST",
4
5
  "description": "All-in-one CLI & GUI for automated GTM, Meta Pixel/CAPI, Google Sheets Form Sync, and Zoho CRM injection with self-testing.",
5
6
  "main": "src/index.js",
6
7
  "bin": {
@@ -21,7 +22,7 @@
21
22
  "scripts": {
22
23
  "start": "node bin/cli.js",
23
24
  "gui": "node bin/cli.js --gui",
24
- "test": "node tests/injector.test.js && node tests/multipage.test.js && node tests/whatsapp-autofetch.test.js && node tests/gas-template.test.js"
25
+ "test": "node tests/injector.test.js && node tests/multipage.test.js && node tests/whatsapp-autofetch.test.js && node tests/gas-template.test.js && node tests/version-meta.test.js"
25
26
  },
26
27
  "files": [
27
28
  "bin",
@@ -50,11 +50,12 @@ function printLiveState(scan) {
50
50
  console.log(colors.blueBold(' ██╔══██╗██║ ██║ ███╔╝ ██║ ╚══') + colors.amberBold('══╝██║ ██╔══██║██╔═══╝ ██║'));
51
51
  console.log(colors.blueBold(' ██████╔╝╚██████╔╝███████╗███████╗') + colors.amberBold(' ╚██████╗██║ ██║██║ ██║'));
52
52
  console.log(colors.blueBold(' ╚═════╝ ╚═════╝ ╚══════╝╚══════╝') + colors.amberBold(' ╚═════╝╚═╝ ╚═╝╚═╝ ╚═╝'));
53
- console.log(colors.ice(` ⚡ BUZL CONVERSIONS API & MULTI-CHANNEL DISPATCHER • v${pkg.version}\n`));
53
+ console.log(colors.ice(` ⚡ BUZL CONVERSIONS API & MULTI-CHANNEL DISPATCHER • v${pkg.version} • Updated: ${pkg.updatedAt || '22-Sep-2026 05:30 PM IST'}\n`));
54
54
 
55
55
  // 2. Target Project Scope Card
56
56
  console.log(b(' ╭─────────────────────────── [ 📂 TARGET PROJECT ] ───────────────────────────╮'));
57
57
  console.log(b(' │') + ' Path : ' + colors.bold(scan.rootDir));
58
+ console.log(b(' │') + ' Engine : ' + colors.ice(`v${pkg.version}`) + colors.dim(` (Updated: ${pkg.updatedAt || '22-Sep-2026 05:30 PM IST'})`));
58
59
  console.log(b(' │') + ' Pages : ' + colors.ice(`${scan.totalHtmlFiles} HTML file(s) discovered`));
59
60
  console.log(b(' │') + ' Location : ' + colors.amber(scan.detectedLocation || 'N/A'));
60
61
  console.log(b(' ╰─────────────────────────────────────────────────────────────────────────────╯'));
@@ -4,6 +4,7 @@
4
4
  */
5
5
  const fs = require('fs');
6
6
  const path = require('path');
7
+ const pkg = require('../../package.json');
7
8
 
8
9
  const DEFAULT_IGNORED_DIRS = [
9
10
  'node_modules',
@@ -478,6 +479,8 @@ function scanProject(rootDir) {
478
479
 
479
480
  return {
480
481
  rootDir,
482
+ version: pkg.version,
483
+ updatedAt: pkg.updatedAt || '22-Sep-2026 05:30 PM IST',
481
484
  totalHtmlFiles: htmlFiles.length,
482
485
  files: cleanFiles,
483
486
  totalForms,
@@ -471,7 +471,8 @@ document.addEventListener('DOMContentLoaded', () => {
471
471
 
472
472
  const exportData = {
473
473
  reportTitle: 'BUZL CAPI Suite — Automated Verification & Compliance Audit Report',
474
- version: '0.1.4',
474
+ version: (currentScanData && currentScanData.version) ? currentScanData.version : '0.1.5',
475
+ lastUpdated: (currentScanData && currentScanData.updatedAt) ? currentScanData.updatedAt : '22-Sep-2026 05:30 PM IST',
475
476
  generatedAt: new Date().toISOString(),
476
477
  projectRoot: (liveStateRootDir ? liveStateRootDir.textContent : '') || 'Workspace Root',
477
478
  summary: {
@@ -569,7 +570,7 @@ document.addEventListener('DOMContentLoaded', () => {
569
570
  <div style="text-align:right;">
570
571
  <div class="meta"><strong>Date:</strong> ${timestamp}</div>
571
572
  <div class="meta"><strong>Status:</strong> <span style="color:${statusColor}; font-weight:700;">${report.allPassed ? 'VERIFIED PASS' : 'ISSUES DETECTED'}</span></div>
572
- <div class="meta"><strong>Engine:</strong> v0.1.4 (WCAG 2.2 AA)</div>
573
+ <div class="meta"><strong>Engine:</strong> v${(currentScanData && currentScanData.version) || '0.1.5'} (${(currentScanData && currentScanData.updatedAt) ? `Updated: ${currentScanData.updatedAt}` : 'WCAG 2.2 AA'})</div>
573
574
  </div>
574
575
  </div>
575
576
 
@@ -709,6 +710,16 @@ document.addEventListener('DOMContentLoaded', () => {
709
710
  // Update Live State Bar
710
711
  updateLiveStateBar(data.liveState, data.rootDir);
711
712
 
713
+ // Dynamically update Version & Release Timestamp Badges
714
+ const versionBadge = document.getElementById('versionBadge');
715
+ const updateBadge = document.getElementById('updateBadge');
716
+ if (versionBadge && data.version) {
717
+ versionBadge.textContent = `v${data.version}`;
718
+ }
719
+ if (updateBadge && data.updatedAt) {
720
+ updateBadge.textContent = `Updated: ${data.updatedAt}`;
721
+ }
722
+
712
723
  // Auto-detect site location
713
724
  if (data.detectedLocation && siteLocationInput && !siteLocationInput.dataset.userEdited) {
714
725
  siteLocationInput.value = data.detectedLocation;
@@ -124,7 +124,8 @@
124
124
  <div class="brand-titles">
125
125
  <div class="brand-headline">
126
126
  <h1>Conversions API &amp; Form Tracking Suite</h1>
127
- <span class="version-badge">v0.1.4</span>
127
+ <span class="version-badge" id="versionBadge">v0.1.5</span>
128
+ <span class="update-badge" id="updateBadge" title="Latest package release timestamp">Updated: 22-Sep-2026 05:30 PM IST</span>
128
129
  </div>
129
130
  <p class="subtitle">Automated GTM, Meta CAPI, Google Sheets CRM &amp; Zoho Web-to-Lead integration.</p>
130
131
  </div>
@@ -205,6 +205,19 @@ body {
205
205
  font-weight: var(--font-weight-base);
206
206
  }
207
207
 
208
+ .update-badge {
209
+ font-family: var(--font-family-mono);
210
+ font-size: var(--font-size-xs);
211
+ background: var(--color-surface-raised);
212
+ color: var(--color-text-secondary);
213
+ border: 1px solid var(--color-border-muted);
214
+ padding: 1px var(--space-6);
215
+ border-radius: var(--radius-xs);
216
+ font-weight: var(--font-weight-base);
217
+ display: inline-flex;
218
+ align-items: center;
219
+ }
220
+
208
221
  .subtitle {
209
222
  color: var(--color-text-inverse);
210
223
  font-size: var(--font-size-sm);
package/src/gui/server.js CHANGED
@@ -10,6 +10,7 @@ const { scanProject } = require('../core/scanner');
10
10
  const { applyInjection, removeTracking, removeService } = require('../core/injector');
11
11
  const { runVerification, testDispatch, testIndividualForm } = require('../core/tester');
12
12
  const { restoreBackup, restoreLatestBackup, deleteBackup, listBackups, manualBackup } = require('../core/rollback');
13
+ const pkg = require('../../package.json');
13
14
 
14
15
  const PUBLIC_DIR = path.join(__dirname, 'public');
15
16
 
@@ -62,6 +63,17 @@ function startGuiServer(rootDir, port = 3333) {
62
63
  }
63
64
 
64
65
  try {
66
+ // 0. API: Version & Release Metadata
67
+ if (pathname === '/api/version' && req.method === 'GET') {
68
+ res.writeHead(200, { 'Content-Type': 'application/json' });
69
+ res.end(JSON.stringify({
70
+ name: pkg.name,
71
+ version: pkg.version,
72
+ updatedAt: pkg.updatedAt || '22-Sep-2026 05:30 PM IST'
73
+ }));
74
+ return;
75
+ }
76
+
65
77
  // 1. API: Scan project
66
78
  if (pathname === '/api/scan' && req.method === 'GET') {
67
79
  const scan = scanProject(rootDir);
@@ -6,7 +6,7 @@
6
6
  *
7
7
  * FEATURES:
8
8
  * 1. 'All Leads' (Master Sheet): Inbound leads insert at Row 2 (Top of sheet).
9
- * 2. Guaranteed Forward Layout: Handled By & Comments sit right next to Lead Stage.
9
+ * 2. Standard 15-Column CRM Layout: Name, Location, Phone, Lead Stage, Event Time, Is Qualified, Qualified Date, Is Spam, Handled By, Comments, Action Source, Source, UTM Source, UTM Campaign, Lead ID.
10
10
  * 3. Universal Fuzzy Matcher: Case-insensitive & whitespace-tolerant header mapping.
11
11
  * 4. Official Buzl Navy Header: #1E4E9E background with bold white text (#FFFFFF).
12
12
  * 5. Full-Row Highlighting: Entire rows (A:Z) color-coded by Lead Stage (LOWER formula).
@@ -21,17 +21,18 @@
21
21
  // Core column definitions
22
22
  var MASTER_SHEET_NAME = 'All Leads';
23
23
  var STATUS_TABS = ['New', 'Contacted', 'Qualified', 'Converted', 'Spam', 'Test'];
24
- var DEFAULT_TEAM_MEMBERS = ['Dr. Samya', 'Dr. John', 'Sales Rep', 'Front Desk'];
24
+ // Configurable Team Members (Leave empty [] to discover reps dynamically from sheet entries)
25
+ var DEFAULT_TEAM_MEMBERS = [];
25
26
 
26
- // Forward Operational Layout: Handled By and Comments sit right next to Lead Stage
27
+ // Standard 15-Column CRM Layout
27
28
  var STANDARD_CRM_COLUMNS = [
28
29
  'Lead Stage',
29
- 'Handled By',
30
- 'Comments',
30
+ 'Event Time',
31
31
  'Is Qualified',
32
32
  'Qualified Date',
33
33
  'Is Spam',
34
- 'Event Time',
34
+ 'Handled By',
35
+ 'Comments',
35
36
  'Action Source',
36
37
  'Source',
37
38
  'UTM Source',
@@ -316,10 +317,9 @@ function ensureCrmLayout(sheet, dynamicFields, forceReorganize) {
316
317
  if (lastRow === 0) {
317
318
  var initialHeaders = ['Name', 'Location', 'Phone'];
318
319
  if (dynamicFields) {
319
- for (var df in dynamicFields) initialHeaders.push(df);
320
- }
321
- if (initialHeaders.indexOf('Service') === -1) {
322
- initialHeaders.push('Service');
320
+ for (var df in dynamicFields) {
321
+ if (initialHeaders.indexOf(df) === -1) initialHeaders.push(df);
322
+ }
323
323
  }
324
324
  initialHeaders = initialHeaders.concat(STANDARD_CRM_COLUMNS);
325
325
 
@@ -341,9 +341,7 @@ function ensureCrmLayout(sheet, dynamicFields, forceReorganize) {
341
341
  var needsReorganization = forceReorganize === true ||
342
342
  leadStageIdx === -1 ||
343
343
  handledByIdx === -1 ||
344
- commentsIdx === -1 ||
345
- handledByIdx !== leadStageIdx + 1 ||
346
- commentsIdx !== leadStageIdx + 2;
344
+ commentsIdx === -1;
347
345
 
348
346
  if (needsReorganization) {
349
347
  // 1. Build index mapping for all standard fields
@@ -352,7 +350,7 @@ function ensureCrmLayout(sheet, dynamicFields, forceReorganize) {
352
350
  idxMap[key] = findHeaderIndex(oldHeaders, COLUMN_ALIASES[key]);
353
351
  }
354
352
 
355
- // 2. Build target headers: Name, Location, Phone, [Email if present], [Dynamic fields / Service], Standard CRM Columns
353
+ // 2. Build target headers: Name, Location, Phone, [Email if present], [Dynamic fields], [Service if present], Standard CRM Columns
356
354
  var targetHeaders = ['Name', 'Location', 'Phone'];
357
355
  if (idxMap.email > -1) {
358
356
  targetHeaders.push('Email');
@@ -362,7 +360,7 @@ function ensureCrmLayout(sheet, dynamicFields, forceReorganize) {
362
360
  if (targetHeaders.indexOf(k) === -1) targetHeaders.push(k);
363
361
  }
364
362
  }
365
- if (targetHeaders.indexOf('Service') === -1) {
363
+ if (idxMap.service > -1 && targetHeaders.indexOf('Service') === -1) {
366
364
  targetHeaders.push('Service');
367
365
  }
368
366
  targetHeaders = targetHeaders.concat(STANDARD_CRM_COLUMNS);
@@ -838,13 +836,13 @@ function onOpen() {
838
836
  function menuInitCrm() {
839
837
  var ss = SpreadsheetApp.getActiveSpreadsheet();
840
838
  var master = getOrCreateMasterSheet(ss);
841
- var headers = ensureCrmLayout(master, { Service: 'General Consultation' }, true);
839
+ var headers = ensureCrmLayout(master, null, false);
842
840
  initializeCrmTabs(ss, headers);
843
841
  applyDropdownValidations(master, headers, null, ss);
844
842
  applyColumnWidths(master, headers);
845
843
  applyColorCoding(master, headers);
846
844
  applyDateFormatting(master, headers);
847
- SpreadsheetApp.getUi().alert('✔ Buzl CRM Upgraded! Columns reorganized forward, full-row colors active, and team tabs synced.');
845
+ SpreadsheetApp.getUi().alert('✔ Buzl CRM Upgraded! CRM headers verified, full-row colors active, and team tabs synced.');
848
846
  }
849
847
 
850
848
  function menuSortNewestFirst() {