Willog Sales CRM
Provides a complete Google Apps Script sales CRM with Kanban board, AI assistant, and bilingual support, backed by Google Sheets.
What this file does
Provides a complete Google Apps Script sales CRM with Kanban board, AI assistant, and bilingual support, backed by Google Sheets.
When to use it
- Building a lightweight CRM inside Google Workspace without external servers
- Need a Kanban board for sales pipeline management with drag-and-drop
- Want AI-powered lead insights using Gemini API
- Require bilingual (English/Korean) interface for a sales team
Assumes this stack
Willog Sales CRM
A Google Apps Script web application for sales lead management with Kanban board, AI assistant, and bilingual (EN/KO) support.
Project Structure
willog-sales-crm/
├── Code.gs # Backend logic & API integrations (~2,100 lines)
├── index.html # HTML structure with template includes (~2,100 lines)
├── styles.html # All CSS styles (~3,600 lines)
├── scripts.html # All JavaScript (~6,800 lines)
├── appsscript.json # Apps Script manifest
├── .clasp.json # Clasp configuration (scriptId)
└── .claspignore # Files to exclude from push
Tech Stack
- Backend: Google Apps Script (V8 runtime)
- Frontend: Vanilla HTML/CSS/JavaScript (modular files)
- Database: Google Sheets (Leads, Team_Members, Activity_Log sheets)
- APIs: Gmail, Google Calendar, Google Drive, Gemini AI
- Deployment: Apps Script Web App
How to Recreate This Project
Prerequisites
- Google Account with Google Workspace (for Gmail/Calendar integration)
- Node.js installed (for clasp CLI)
- Clasp CLI installed globally:
npm install -g @google/clasp
Step 1: Create Google Sheet
-
Go to Google Sheets and create a new spreadsheet
-
Name it "Willog CRM Data" (or any name)
-
Create these sheets (tabs):
LeadsTeam_MembersActivity_LogCustomers(or auto-created when first lead moves to Won)Lost_Leads(or auto-created when first lead moves to Lost)
-
Leads sheet headers (Row 1):
Lead_ID, Stage, Contact_Name, Email, Phone, Source, Assigned_Rep, Department, Company, Job_Title, Industry, Company_Size, Role_Type, Website, Location, LinkedIn_URL, Company_Notes, Deal_Name, Deal_Value, Close_Date, Win_Probability, Opportunity_Notes, Proposal_Docs, Budget_Status, Timeline, Pain_Points, Pain_Points_Notes, Current_Stack, Current_Stack_Notes, Use_Case, Objections, Created_By, Created_By_Role, Created_Date, Last_Touch_Date, Is_Archived, Archived_Date, Archived_By, Archive_Reason, Previous_Stage, Previous_Assigned_Rep -
Team_Members sheet headers (Row 1):
Member_ID, Name, Email, Role, Department -
Activity_Log sheet headers (Row 1):
Activity_ID, Lead_ID, Activity_Type, Activity_Date, Performed_By, Recipient, Subject, Gmail_Thread_ID, Calendar_Event_ID, Calendar_Event_Link, Notes, Custom_Link -
Customers sheet headers (Row 1) - Auto-created on first Won deal:
Customer_ID, Lead_ID, Won_Date, Contact_Name, Email, Phone, Source, Assigned_Rep, Department, Company, Job_Title, Industry, Company_Size, Role_Type, Website, Location, LinkedIn_URL, Company_Notes, Deal_Name, Deal_Value, Close_Date, Win_Probability, Opportunity_Notes, Proposal_Docs, Budget_Status, Timeline, Pain_Points, Pain_Points_Notes, Current_Stack, Current_Stack_Notes, Use_Case, Objections, Created_By, Created_By_Role, Created_Date, Last_Touch_Date -
Lost_Leads sheet headers (Row 1) - Auto-created on first Lost deal:
Lead_ID, Lost_Date, Lost_Reason, Stage_Before_Lost, Contact_Name, Email, Phone, Source, Assigned_Rep, Department, Company, Job_Title, Industry, Company_Size, Role_Type, Website, Location, LinkedIn_URL, Company_Notes, Deal_Name, Deal_Value, Close_Date, Win_Probability, Opportunity_Notes, Proposal_Docs, Budget_Status, Timeline, Pain_Points, Pain_Points_Notes, Current_Stack, Current_Stack_Notes, Use_Case, Objections, Created_By, Created_By_Role, Created_Date, Last_Touch_Date -
Copy the Spreadsheet ID from the URL:
https://docs.google.com/spreadsheets/d/[THIS_IS_YOUR_SHEET_ID]/edit
Step 2: Create Apps Script Project
- Go to Google Apps Script
- Click New Project
- Name it "Willog Sales CRM"
- Copy the Script ID from Project Settings (gear icon)
Step 3: Set Up Local Development
-
Clone or create project folder:
mkdir willog-sales-crm cd willog-sales-crm -
Login to clasp:
clasp login -
Create
.clasp.json:{ "scriptId": "YOUR_SCRIPT_ID_HERE", "rootDir": "." } -
Create
.claspignore:.git/** .gitignore node_modules/** *.md .clasp.json .claspignore *.txt *.rtf
Step 4: Configure Code.gs
Update the SHEET_ID constant in Code.gs:
const SHEET_ID = 'YOUR_SPREADSHEET_ID_HERE';
Step 5: Enable Required Services
In Apps Script editor:
- Click Services (+ icon)
- Add Google Calendar API (v3)
- Add Google Drive API (v2)
Step 6: Set Up Gemini AI (Optional)
- Go to Google AI Studio
- Create an API key
- In Apps Script editor:
- Go to Project Settings (gear icon)
- Scroll to Script Properties
- Click Add script property
- Property:
GEMINI_API_KEY - Value: Your API key
Step 7: Deploy
-
Push files to Apps Script:
clasp push -
In Apps Script editor:
- Click Deploy > New deployment
- Type: Web app
- Execute as: User accessing the web app
- Who has access: Anyone within your organization (or as needed)
- Click Deploy
-
Copy the web app URL
Step 8: First-Time Setup
- Open the web app URL
- Authorize the required permissions
- Register yourself as the first team member
- Start adding leads!
Data Model
Lead Fields
Core: Lead_ID, Stage, Created_By, Created_By_Role, Created_Date, Last_Touch_Date
Contact: Contact_Name, Email, Phone, Source, Assigned_Rep, Department
Company: Company, Job_Title, Industry, Company_Size, Role_Type, Website, Location, LinkedIn_URL, Company_Notes
Opportunity: Deal_Name, Deal_Value, Close_Date, Win_Probability, Opportunity_Notes, Proposal_Docs
Qualification: Budget_Status, Timeline, Pain_Points, Pain_Points_Notes, Current_Stack, Current_Stack_Notes, Use_Case, Objections
Archive: Is_Archived, Archived_Date, Archived_By, Archive_Reason, Previous_Stage, Previous_Assigned_Rep
Activity_Log Fields
Activity_ID, Lead_ID, Activity_Type, Activity_Date, Performed_By, Recipient, Subject, Gmail_Thread_ID, Calendar_Event_ID, Calendar_Event_Link, Notes, Custom_Link
Team Member Fields
Member_ID (TM-XXX), Name, Email, Role (Sales/Marketing/Admin), Department
Customers Fields (Won Deals)
Customer_ID (C-XXX), Lead_ID, Won_Date, + all Lead fields
Lost_Leads Fields
Lead_ID, Lost_Date, Lost_Reason, Stage_Before_Lost, + all Lead fields
Sales Pipeline Stages
- Lead - Initial contact (requires Contact Info)
- Qualification/Discovery - Requires Company Info (5 fields)
- Proposal - Requires Qualification Info (6 fields)
- Won - Deal closed successfully
- Lost - Deal lost
User Roles & Permissions
| Role | All Leads Tab | My Board Tab | Can Assign | Auto-Assign |
|---|---|---|---|---|
| Sales | View unassigned | Full access | Yes | To self |
| Marketing | Full access | Hidden | Yes | No |
| Admin | Full access | Hidden | Yes | No |
Key Features
Views
- All Leads: Table view showing unassigned leads, searchable, filterable by source
- My Board: Kanban board with drag-and-drop, shows assigned rep's leads
- Dashboard: Visual analytics with KPIs and charts
AI Assistant
- Floating chat button (bottom-right corner)
- Powered by Google Gemini 2.5 Flash
- Has access to all CRM data (leads, activities, customers, team)
- Context-aware recommendations when viewing a lead
Gmail Integration
- Send emails directly from lead cards
- Auto-sync sent/received emails to Activity Log
- View email thread history in Activity panel
Google Calendar Integration
- Schedule meetings from lead cards
- Auto-sync calendar events with lead's email
- View meeting details, attendees, and Google Meet links
Activity Log
- Slide-out panel showing all interactions with a lead
- Custom activity logging (calls, meetings, notes)
- Edit/delete custom activities
- Auto-updates Last_Touch_Date
Stage Validation
- Lead → Qualification: Requires Company, Job Title, Industry, Company Size, Role Type
- Qualification → Proposal: Requires Deal Name, Deal Value, Budget Status, Timeline, Pain Points, Current Stack
Archive System
- Archive reasons: Duplicate, Not interested, Lost contact, Other
- Preserves previous stage and assigned rep for restore
- Slide-out panel with search
UI Components
- Readiness indicators (green/orange dots) on cards
- Collapsible form sections with edit/display modes
- Unsaved changes confirmation dialog
- View-only mode when viewing another rep's board
- Distinct pale pastel colors per sales rep
Clasp Commands
clasp login # Authenticate with Google
clasp push # Deploy local files to Apps Script
echo Yes | clasp push # Push without confirmation prompt
clasp deploy -d "description" # Create new versioned deployment
clasp deployments # List all deployments
clasp undeploy <id> # Delete a deployment
clasp open # Open Apps Script editor in browser
Code.gs Functions
User Functions
getCurrentUser()- Returns user info from Team_Members sheetregisterNewUser(userData)- Creates new team membergetTeamMembers()- Returns all team membersgetSalesReps()- Returns only Sales role members
Lead Functions
getLeads()- Returns non-archived leadsgetArchivedLeads()- Returns archived leads onlyaddLead(leadData)- Creates lead with auto-filled fieldsupdateLead(leadId, updates)- Updates specific fieldsassignLead(leadId, repName)- Assigns to sales repdeleteLead(leadId)- Permanently deletes leadarchiveLead(leadId, reason)- Soft-deletes with reasonrestoreLead(leadId)- Restores to previous state
Activity Functions
addActivity(activityData)- Logs activity and updates Last_Touch_DategetActivitiesForLead(leadId)- Returns all activities for a leadupdateActivity(activityId, updates)- Updates existing activitydeleteActivity(activityId)- Deletes an activity
Gmail Functions
syncGmailSentEmails(leadId, email, sinceDate)- Syncs sent emails to Activity Log
Calendar Functions
syncCalendarEventsForLead(leadId, email, sinceDate)- Syncs calendar eventsgetCalendarEventDetails(eventId)- Gets event details (attendees, Meet link)
AI Functions
askAI(question, leadId)- Main AI query functionbuildAIContext(leadId)- Builds context from all CRM datacallGeminiAPI(prompt, apiKey)- Calls Gemini 2.5 Flash
Utility Functions
generateLeadId()- Creates L-XXX format IDinclude(filename)- Includes HTML files for modular structure
File Structure Details
index.html
Main template file with HTML structure. Uses Apps Script templating:
<?!= include('styles') ?> <!-- Includes styles.html -->
<?!= include('scripts') ?> <!-- Includes scripts.html -->
styles.html
All CSS wrapped in <style> tags. Key variables:
--blue-accent: #2563EB /* Primary actions */
--purple-accent: #8B5CF6 /* Toggle buttons */
--destructive: #d4183d /* Errors/delete */
--muted-foreground: #717182
scripts.html
All JavaScript wrapped in <script> tags. Key DOM IDs:
#allLeads/#myBoard- Main sections#addLeadModal- New lead form#leadDetailModal- Edit lead (All Leads)#myLeadModal- Edit lead (My Board)#archivePanel- Slide-out archive list#activityPanel- Slide-out activity log#aiChatPanel- AI chat interface
Internationalization (i18n)
- Languages: English (en), Korean (ko)
- Storage:
localStorage.setItem('willog_lang', lang) - Usage:
data-i18n="keyName"- Text contentdata-i18n-placeholder="keyName"- Input placeholderst(key)- JavaScript translation function
When adding new UI text:
- Add
data-i18n="keyName"attribute to element - Add key to
translations.enobject in scripts.html - Add Korean translation to
translations.koobject
Development Workflow
- Edit files locally
- Run
clasp pushto deploy to Apps Script - Test at dev URL
- When ready, create versioned deployment:
clasp deploy -d "description"
Script URL
https://script.google.com/a/macros/willog.io/s/AKfycbwQZqtX2P1yhOWVfNq0Q5dZpWMH0WgQPQlieuYJnmbI/dev
Notes
- Use
google.script.run.functionName()for backend calls - Modular HTML approach with
include()function - Max 20 versioned deployments allowed (delete old ones as needed)
- Rep colors assigned via
getRepColor(name)function - Gemini API uses
gemini-2.5-flashmodel
What's inside
8 setup steps, 5 sheet schemas, 5 pipeline stages, 3 user roles, 6 API integrations, and full file structure.
Change this for your project
- Replace
SHEET_ID = 'YOUR_SPREADSHEET_ID_HERE'with your own spreadsheet ID - Replace
scriptIdin.clasp.jsonwith your Apps Script project ID - Replace
GEMINI_API_KEYscript property value with your own API key - Replace the deployment URL in the Script URL section with your own
Where it goes
A standard operating procedure. Keep where the team or agent running the process will find it.
Worth borrowing
- Modular HTML via
include()function to split large web app into manageable files - Stage validation logic that enforces required fields before moving a lead forward
- Bilingual i18n using
data-i18nattributes and a single translation object per language
Related Documents
Comprehensive AI Assistant Tools Reference
Lists 80+ tools with MCP server associations, bulk support, parallel capability, resource impact, and execution type for AI agent workflows.
iOS Deployment Guide
Walks through setting up an iOS development environment, building a Tauri app for iOS, and publishing to the App Store or alternative channels.
How to Add Resources to Your FastMCP Server
Teaches how to add static and dynamic MCP resources to a FastMCP server, with six ready-to-copy examples for a GitHub crawler.
PCI Compliance Guide
Explains PCI DSS compliance via tokenization, lists forbidden and allowed data, and provides code examples for BigCommerce and Stripe.