Found in Stripe Dashboard โ Developers โ API Keys. Never share this key.
Deal Adjustments
Target Dates
Internal Notes
Changes update totals immediately. Save to persist.
Fertigation Skid
Precision Irrigation
Control Panels & Modules
Sensors & Monitoring
Panels (Hardware)
Shipping
Commissioning
Data Platform ($/point/month)
Products are stored in Supabase. Changes here update pricing for all users instantly.
Category
Product Name
SKU
Price ($)
Active
Microsoft SSO Configuration
Configure Microsoft OAuth so your team can sign in with their work accounts.
Uses Supabase Auth โ no extra backend needed.
Azure Portal โ App Registrations โ your app โ Application (client) ID.
Open Azure Portal โ
Azure Portal โ Microsoft Entra ID โ Overview โ Tenant ID (the UUID shown there)
Azure App Registration setup:
1. Azure Portal โ Microsoft Entra ID โ App registrations โ your app โ Authentication
2. Add Redirect URI โ Single-page application (SPA) โ
3. Also add Supabase callback URL in Azure (from Supabase Auth โ Providers โ Azure)
4. Azure โ Overview โ copy Application (client) ID and Directory (tenant) ID
5. Paste both above, set Tenant URL in Supabase to https://login.microsoftonline.com/YOUR-TENANT-ID/
Supabase Connection
SQL Setup
Run in Supabase SQL Editor:
create extension if not exists "pgcrypto";
create table if not exists proposals (
id uuid default gen_random_uuid() primary key,
created_at timestamptz default now(),
updated_at timestamptz default now(),
project_name text, company_name text, account_executive text,
status text default 'draft' check (status in ('draft','sent','accepted','declined','expired')),
grand_total numeric(12,2), monthly_fee numeric(10,2),
payload jsonb not null default '{}'::jsonb
);
create index if not exists proposals_status_idx on proposals(status);
create index if not exists proposals_ae_idx on proposals(account_executive);
create index if not exists proposals_created_idx on proposals(created_at desc);
create table if not exists price_config (
id uuid default gen_random_uuid() primary key,
updated_at timestamptz default now(), updated_by text,
prices jsonb not null default '{}'::jsonb
);
create unique index if not exists price_config_one on price_config((true));
insert into price_config(updated_by,prices) values('system','{}') on conflict do nothing;
create or replace function set_updated_at() returns trigger as $$
begin new.updated_at=now(); return new; end; $$ language plpgsql;
drop trigger if exists t_prop_upd on proposals;
create trigger t_prop_upd before update on proposals for each row execute function set_updated_at();
drop trigger if exists t_price_upd on price_config;
create trigger t_price_upd before update on price_config for each row execute function set_updated_at();
alter table proposals enable row level security;
alter table price_config enable row level security;
drop policy if exists "open" on proposals;
drop policy if exists "open" on price_config;
create policy "open" on proposals for all using(true) with check(true);
create policy "open" on price_config for all using(true) with check(true);
-- Product Catalog table
create table if not exists product_catalog (
id uuid default gen_random_uuid() primary key,
created_at timestamptz default now(),
category text not null,
name text not null,
sku text,
price numeric(10,4) default 0,
active boolean default true
);
create index if not exists catalog_category_idx on product_catalog(category);
alter table product_catalog enable row level security;
drop policy if exists "open" on product_catalog;
create policy "open" on product_catalog for all using(true) with check(true);
-- Proposal templates table
create table if not exists proposal_templates (
id uuid default gen_random_uuid() primary key,
created_at timestamptz default now(),
updated_at timestamptz default now(),
template jsonb not null default '{}'::jsonb
);
alter table proposal_templates enable row level security;
drop policy if exists "open" on proposal_templates;
create policy "open" on proposal_templates for all using(true) with check(true);
-- Supabase Storage bucket for proposal images
-- Run this in the Supabase Dashboard > Storage > New bucket:
-- Name: proposal-images | Public: true
create or replace view proposals_summary as
select id, created_at, updated_at, project_name, company_name, account_executive,
status, grand_total, monthly_fee,
coalesce(jsonb_array_length(payload->'rooms'),0) as room_count,
coalesce(jsonb_array_length(payload->'skids'),0) as skid_count
from proposals order by created_at desc;
Saved Proposals
Click โป Refresh List above.
Grow Rooms
Add and configure each grow room or zone
Editing Room
I/O Points
Monitoring
Panel
Equipment
DO
Light Dim
AO
Relay
Contactor
RIB
DI
AI
PAR
SI
TO
BACnet Pts
Type
TOTALS
0
0
0
0
0
0
0
0
0
0
0
0
Tank Monitoring
Fertigation Skids
Configure each nutrient delivery skid independently
Editing Skid
Fert Panel I/O Points
I/O points inside the fert panel. Change pump count or size above to get a suggestion, or enter manually.
Fert Panel Enclosure
Monitoring Equipment
Extras / Miscellaneous
Additional line items
Extra Items
Description
SKU
Qty
Unit Price
Total
Proposal Builder
Customize and generate your customer proposal
Proposal Sections
Drag to reorder. Toggle visibility. Click Edit to customize content.
Add Section
Proposal Settings
Image Library
Upload images to Supabase Storage for use in proposals.
Connect Supabase to load image library.
Bill of Materials
Complete hardware & pricing breakdown
Project Handoff
Information for the delivery team
Documents & Drawings
Handoff Notes
Pipeline Dashboard
Live from Supabase
Total Proposals
โ
Pipeline Value
โ
Accepted Value
โ
Avg Deal Size
โ
Est. MRR
โ
Total Rooms
โ
Revenue by Status
Pipeline by AE
Monthly Closed Value
Status Breakdown
All Proposals
HubSpot CRM
Search deals and import contact & company info directly into this project
HubSpot Connection
HubSpot token is configured via the HUBSPOT_ACCESS_TOKEN Netlify environment variable.
Search Deals
Deal Details
Cin7 Core Invoicing
Create sales orders and invoices directly in Cin7 Core from this proposal
Cin7 Core Connection
Cin7 credentials are configured via CIN7_ACCOUNT_ID and CIN7_APP_KEY Netlify environment variables.
Product SKU Mapping
Map Growlink product SKUs to your Cin7 product codes. These are used when creating line items on the invoice.
Category
Growlink Product
Growlink SKU
Cin7 Product Code
Tax Rule
Account Code
Invoice Settings
Invoice Preview
Review what will be sent to Cin7 before creating the invoice.
Click "Preview Invoice" to see the line items that will be sent to Cin7.