Dashboard
Good day, Tushar — here's your app at a glance.
API 510 Helpmate · Pressure Vessel Inspector
Supabase
Checking…
Loading library
Database
Not connected
Configure in Settings
Total Users
Registered accounts
Active (Premium)
Valid subscription
Sections
Exam categories
Questions
Total in DB
Quick Connect
Users
Manage user accounts and subscriptions
API 510 Helpmate
Name Email Registered Sub Start Sub End Status Actions
Configure Supabase to load users
Sections
Manage exam sections and categories
API 510 Helpmate
SeqNameSecIDOB TimeCB TimeExamActiveActions
Configure Supabase to load sections
Questions
Manage exam questions
API 510 Helpmate
0 selected
No SecID Tag Question Ans 📎 Open Close Active Actions
Configure Supabase to load questions
Notifications
Send in-app notifications to all users
API 510 Helpmate
New Notification
Sent Notifications
MessageSent ByDateAction
No notifications
Reported Questions
Review and manage user-reported question issues
API 510 Helpmate
Question Reports
Loading reports…
Settings
Connection, testing, and configuration
Supabase Connection
Credentials saved per-app in localStorage. Switching apps loads that app's saved config.
Current App Config
App
Users Table
Sections Table
Questions Table
Notifications Table
Reported Table
Progress Table
Storage Bucket
Run in Supabase SQL Editor if you see RLS errors. Enables permissive policies without breaking existing behaviour.

            
Run in Supabase SQL Editor to create the helpmate_support table used for AI-assisted support replies.
-- ═══════════════════════════════════════════════════════════
-- TABLE: helpmate_support
-- Centralised support inbox across all three Helpmate apps.
-- Replaces Google Sheets as the primary store; sheet_row_index
-- preserved for parallel write-back during migration.
-- ═══════════════════════════════════════════════════════════

CREATE TABLE IF NOT EXISTS helpmate_support (
  id                    UUID          PRIMARY KEY DEFAULT gen_random_uuid(),

  -- Submission metadata
  timestamp             TIMESTAMPTZ   NOT NULL DEFAULT now(),
  name                  TEXT,
  email                 TEXT,
  app                   TEXT,                        -- 'API 510 Helpmate' | 'API 570 Helpmate' | 'API 653 Helpmate'
  device                TEXT,                        -- 'iOS' | 'Android'
  message               TEXT          NOT NULL,

  -- Response lifecycle
  status                TEXT          NOT NULL DEFAULT 'pending'
                          CHECK (status IN ('pending','responded','dismissed')),
  response              TEXT,                        -- final text that was sent to the user
  responded_at          TIMESTAMPTZ,                 -- set when response is actually sent

  -- AI draft (separate from sent response)
  ai_draft              TEXT,                        -- LLM-generated draft awaiting review
  ai_draft_generated_at TIMESTAMPTZ,                 -- when the draft was last generated

  -- Internal
  admin_notes           TEXT,                        -- private notes, never sent to user
  sheet_row_index       INTEGER,                     -- Google Sheets row ref for migration period

  -- Audit
  created_at            TIMESTAMPTZ   NOT NULL DEFAULT now(),
  updated_at            TIMESTAMPTZ   NOT NULL DEFAULT now()
);

-- ── Auto-update updated_at ───────────────────────────────────
CREATE OR REPLACE FUNCTION trg_set_updated_at()
RETURNS TRIGGER LANGUAGE plpgsql AS $$
BEGIN NEW.updated_at = now(); RETURN NEW; END;
$$;

DROP TRIGGER IF EXISTS trg_support_updated_at ON helpmate_support;
CREATE TRIGGER trg_support_updated_at
  BEFORE UPDATE ON helpmate_support
  FOR EACH ROW EXECUTE FUNCTION trg_set_updated_at();

-- ── Indexes ──────────────────────────────────────────────────
CREATE INDEX IF NOT EXISTS idx_support_status    ON helpmate_support(status);
CREATE INDEX IF NOT EXISTS idx_support_app       ON helpmate_support(app);
CREATE INDEX IF NOT EXISTS idx_support_email     ON helpmate_support(email);
CREATE INDEX IF NOT EXISTS idx_support_timestamp ON helpmate_support(timestamp DESC);

-- ── RLS ──────────────────────────────────────────────────────
ALTER TABLE helpmate_support ENABLE ROW LEVEL SECURITY;

DROP POLICY IF EXISTS "support_anon_insert"   ON helpmate_support;
DROP POLICY IF EXISTS "support_admin_all"     ON helpmate_support;

-- Allow the app (anon key) to INSERT new submissions
CREATE POLICY "support_anon_insert" ON helpmate_support
  FOR INSERT TO anon WITH CHECK (true);

-- Allow service role full access (admin panel uses service role key)
CREATE POLICY "support_admin_all" ON helpmate_support
  FOR ALL TO service_role USING (true) WITH CHECK (true);

-- ── Grants ───────────────────────────────────────────────────
GRANT INSERT                          ON helpmate_support TO anon;
GRANT SELECT, INSERT, UPDATE, DELETE  ON helpmate_support TO authenticated;
GRANT ALL                             ON helpmate_support TO service_role;
🔐 Authentication Testing

Test user account operations: Create Account, Login, OTP Verification, and Password Reset.

⚠️ Database Setup Required
For user registration to work, ensure your api{n}_users table has an id UUID column matching the auth user ID and an email_confirmed BOOLEAN column (default false).
📎 File Attachment Setup — Storage bucket: 510_files
The image_url column already exists in your questions table — no SQL changes needed.
In Supabase Studio → Storage: confirm the bucket exists and is set to Public.

Create User Account

Creates the user in both Supabase Auth and your api{n}_users table.

Test User Login

OTP Verification

Password Reset

Update Password (after reset link)

✉️ OTP Email Customization

Customize the verification email sent to users on signup and OTP flows.

🔢 OTP Code Section
🔗 Activation Link Section
Additional Options
Current Session
No active session
📱 Frontend Integration Guide

Code snippets for integrating Supabase authentication into your Flutter/frontend app.

1. Initial Setup

Install Supabase Client
npm install @supabase/supabase-js
Initialize Supabase Client
import { createClient } from '@supabase/supabase-js'

const supabaseUrl = 'https://YOUR_PROJECT.supabase.co'
const supabaseAnonKey = 'YOUR_ANON_KEY'

const supabase = createClient(supabaseUrl, supabaseAnonKey)
export default supabase
⚠️ Important: Get your URL and anon key from Supabase Studio → Settings → API. Use port 8000 if self-hosting.

2. Create Account (Sign Up)

Creates user in Supabase Auth and adds them to api{n}_users table with default Trial values.
async function signUp(email, password, fullName) {
  const { data: authData, error: authError } = await supabase.auth.signUp({
    email, password, options: { data: { full_name: fullName } }
  })
  if (authError) throw authError

  await supabase.from('api510_users').insert([{
    id: authData.user.id,
    name: fullName || email.split('@')[0],
    email,
    status: 'Trial',
    register_date: new Date().toISOString().split('T')[0]
  }])
}

3. User Login

async function login(email, password) {
  const { data, error } = await supabase.auth.signInWithPassword({ email, password })
  if (error) throw error
  const { data: userData } = await supabase
    .from('api510_users').select('*').eq('email', email).single()
  return { session: data.session, userData }
}

async function logout() {
  await supabase.auth.signOut()
}

4. OTP Verification

async function verifyOTP(email, otpCode) {
  const { data, error } = await supabase.auth.verifyOtp({
    email, token: otpCode, type: 'email'
  })
  if (error) throw error
  return data.session
}

async function resendOTP(email) {
  const { error } = await supabase.auth.resend({ type: 'signup', email })
  if (error) throw error
}

5. Password Reset

// Step 1 — Request reset link
async function requestPasswordReset(email) {
  const { error } = await supabase.auth.resetPasswordForEmail(email, {
    redirectTo: 'https://yourapp.com/reset-password'
  })
  if (error) throw error
}

// Step 2 — Update password after redirect
async function updatePassword(newPassword) {
  const { error } = await supabase.auth.updateUser({ password: newPassword })
  if (error) throw error
}

6. Session Management

// Get current session
const { data: { session } } = await supabase.auth.getSession()

// Get current user + DB profile
async function getCurrentUser() {
  const { data: { user } } = await supabase.auth.getUser()
  if (!user) return null
  const { data: userData } = await supabase
    .from('api510_users').select('*').eq('id', user.id).single()
  return { authUser: user, dbUser: userData }
}

// Listen to auth state changes
supabase.auth.onAuthStateChange((event, session) => {
  if (event === 'SIGNED_IN')  { /* update UI */ }
  if (event === 'SIGNED_OUT') { /* redirect to login */ }
  if (event === 'TOKEN_REFRESHED') { /* session refreshed */ }
})
✅ Quick Reference — Supabase Auth Methods
  • supabase.auth.signUp() — Create new account
  • supabase.auth.signInWithPassword() — Login
  • supabase.auth.signOut() — Logout
  • supabase.auth.verifyOtp() — Verify OTP
  • supabase.auth.resend() — Resend verification email
  • supabase.auth.resetPasswordForEmail() — Request password reset
  • supabase.auth.updateUser() — Update password/data
  • supabase.auth.getSession() — Get current session
  • supabase.auth.onAuthStateChange() — Listen to auth events
📧 Outbound Email — ZeptoMail
Emails are sent directly from your browser via the ZeptoMail REST API — no Apps Script proxy needed.
All credentials are stored in localStorage and never leave your browser.
Must match your verified domain in ZeptoMail (e.g. helpmateworld.com)
ZeptoMail → SMTP/API → API tab → Send Mail token 1. Starts with Zoho-enczapikey.
ZeptoMail API reference — what this panel sends
POST https://api.zeptomail.com.au/v1.1/email
Accept: application/json
Content-Type: application/json
Authorization: Zoho-enczapikey <your-token>

{
  "from": { "address": "noreply@helpmateworld.com", "name": "Helpmate Support" },
  "to": [{ "email_address": { "address": "user@example.com" } }],
  "subject": "Re: Your Helpmate Support Request",
  "htmlbody": "<div>Your reply here</div>"
}
⚠ CORS — Apps Script proxy required

Browsers block direct ZeptoMail calls (CORS). The admin panel automatically routes through your Support Apps Script URL — but your HelpmateSupportAPI.gs must handle action: "sendMail". Add the snippet below to your existing .gs file inside doPost().

// Add this inside your doPost(e) function in HelpmateSupportAPI.gs
if (data.action === 'sendMail') {
  var options = {
    method: 'post',
    contentType: 'application/json',
    headers: { 'Authorization': data.apiKey },
    payload: JSON.stringify({
      from: { address: data.fromEmail || 'noreply@helpmateworld.com', name: data.fromName || 'Helpmate Support' },
      to: [{ email_address: { address: data.to } }],
      subject: data.subject,
      htmlbody: '<div>' + data.body + '</div>'
    }),
    muteHttpExceptions: true
  };
  var resp = UrlFetchApp.fetch('https://api.zeptomail.com.au/v1.1/email', options);
  var code = resp.getResponseCode();
  if (code === 200 || code === 201) {
    return ContentService.createTextOutput(JSON.stringify({ ok: true }))
      .setMimeType(ContentService.MimeType.JSON);
  } else {
    return ContentService.createTextOutput(JSON.stringify({ ok: false, error: resp.getContentText() }))
      .setMimeType(ContentService.MimeType.JSON);
  }
}
Only fill this to use a different Apps Script URL for email sending.
✦ AI Config

Used for AI draft generation and rephrase in the Support Inbox. Keys are stored in localStorage only — never sent anywhere except the selected provider's API.

Get your key from platform.openai.com → API Keys. Starts with sk-.
✉ Email Templates

Create and manage reusable HTML email templates. Templates are stored in localStorage. Use the ✉ Email button on any user row to send.

No templates yet — click + New Template
Run this once in your Supabase SQL Editor to create the helpmate_email_templates table. Templates are shared across all three apps and include image attachments stored as base64.
-- ═══════════════════════════════════════════════════════════
-- TABLE: helpmate_email_templates
-- Reusable HTML email templates for the Helpmate Admin Panel.
-- Shared across API 510, 570 and 653 — scoped by the `app`
-- column (NULL = available to all apps).
-- Images are stored inline as base64 data URIs inside `html`.
-- ═══════════════════════════════════════════════════════════

CREATE TABLE IF NOT EXISTS helpmate_email_templates (
  id          UUID        PRIMARY KEY DEFAULT gen_random_uuid(),
  name        TEXT        NOT NULL,
  subject     TEXT,
  description TEXT,
  html        TEXT        NOT NULL DEFAULT '',
  app         TEXT,       -- NULL = all apps | 'API 510 Helpmate' | etc.
  created_at  TIMESTAMPTZ NOT NULL DEFAULT now(),
  updated_at  TIMESTAMPTZ NOT NULL DEFAULT now()
);

-- ── Auto-update updated_at ───────────────────────────────────
CREATE OR REPLACE FUNCTION trg_set_updated_at()
RETURNS TRIGGER LANGUAGE plpgsql AS $$
BEGIN NEW.updated_at = now(); RETURN NEW; END;
$$;

DROP TRIGGER IF EXISTS trg_templates_updated_at ON helpmate_email_templates;
CREATE TRIGGER trg_templates_updated_at
  BEFORE UPDATE ON helpmate_email_templates
  FOR EACH ROW EXECUTE FUNCTION trg_set_updated_at();

-- ── Indexes ──────────────────────────────────────────────────
CREATE INDEX IF NOT EXISTS idx_templates_app
  ON helpmate_email_templates (app);
CREATE INDEX IF NOT EXISTS idx_templates_name
  ON helpmate_email_templates (name);

-- ── RLS (permissive — admin panel only) ──────────────────────
ALTER TABLE helpmate_email_templates ENABLE ROW LEVEL SECURITY;

DO $$
BEGIN
  IF NOT EXISTS (
    SELECT 1 FROM pg_policies
    WHERE tablename = 'helpmate_email_templates'
      AND policyname = 'allow_all_helpmate_email_templates'
  ) THEN
    CREATE POLICY allow_all_helpmate_email_templates
      ON helpmate_email_templates FOR ALL USING (true) WITH CHECK (true);
  END IF;
END $$;

-- ── Grants ───────────────────────────────────────────────────
GRANT ALL ON TABLE helpmate_email_templates TO anon;
GRANT ALL ON TABLE helpmate_email_templates TO authenticated;
GRANT ALL ON TABLE helpmate_email_templates TO service_role;
☑ Task List — Eisenhower Matrix

Prioritise tasks by urgency and importance. Stored in Supabase — connect first. Click any task text to edit it inline.

🔥Do First
Urgent + Important
📅Schedule
Not Urgent + Important
🤝Delegate
Urgent + Not Important
🗑Eliminate
Not Urgent + Not Important
Run this once in your Supabase SQL Editor to create the helpmate_tasks table. Includes the GRANT statements required for the anon key to access the table — RLS alone is not sufficient.
-- ═══════════════════════════════════════════════════════════
-- TABLE: helpmate_tasks
-- Eisenhower Matrix task list for the Helpmate Admin Panel.
-- Shared across all three apps; scoped by the `app` column.
-- ═══════════════════════════════════════════════════════════

CREATE TABLE IF NOT EXISTS helpmate_tasks (
  id          UUID        PRIMARY KEY DEFAULT gen_random_uuid(),
  app         TEXT        NOT NULL,   -- 'API 510 Helpmate' | 'API 570 Helpmate' | 'API 653 Helpmate'
  quadrant    TEXT        NOT NULL    -- 'do' | 'plan' | 'dele' | 'elim'
                CHECK (quadrant IN ('do','plan','dele','elim')),
  text        TEXT        NOT NULL,
  done        BOOLEAN     NOT NULL DEFAULT false,
  sort_order  INTEGER     NOT NULL DEFAULT 0,
  created_at  TIMESTAMPTZ NOT NULL DEFAULT now(),
  updated_at  TIMESTAMPTZ NOT NULL DEFAULT now()
);

-- Index for fast per-app lookups
CREATE INDEX IF NOT EXISTS idx_helpmate_tasks_app
  ON helpmate_tasks (app, quadrant, sort_order);

-- Grant table access to Supabase anon and authenticated roles
-- (RLS alone is not enough — Postgres-level GRANT is also required)
GRANT ALL ON TABLE helpmate_tasks TO anon;
GRANT ALL ON TABLE helpmate_tasks TO authenticated;
GRANT USAGE, SELECT ON SEQUENCE helpmate_tasks_id_seq TO anon;
GRANT USAGE, SELECT ON SEQUENCE helpmate_tasks_id_seq TO authenticated;

-- RLS (permissive — matches other Helpmate tables)
ALTER TABLE helpmate_tasks ENABLE ROW LEVEL SECURITY;

DO $$
BEGIN
  IF NOT EXISTS (
    SELECT 1 FROM pg_policies
    WHERE tablename = 'helpmate_tasks' AND policyname = 'allow_all_helpmate_tasks'
  ) THEN
    CREATE POLICY allow_all_helpmate_tasks
      ON helpmate_tasks FOR ALL USING (true) WITH CHECK (true);
  END IF;
END $$;
Support Inbox
Contact form submissions from all Helpmate apps
API 510 Helpmate
Total Messages
All time
Pending
Awaiting reply
Responded
With response
Last Entry
Most recent
Date ↕ Name Email App Device Message Response Status Action
Click "Refresh" to load from Supabase