| Name | Registered | Sub Start | Sub End | Status | Actions | |
|---|---|---|---|---|---|---|
| Configure Supabase to load users | ||||||
| Seq | Name | SecID | OB Time | CB Time | Exam | Active | Actions |
|---|---|---|---|---|---|---|---|
| Configure Supabase to load sections | |||||||
| No | SecID | Tag | Question | Ans | 📎 | Open | Close | Active | Actions | |
|---|---|---|---|---|---|---|---|---|---|---|
| Configure Supabase to load questions | ||||||||||
| Message | Sent By | Date | Action |
|---|---|---|---|
| No notifications | |||
| App | — |
| Users Table | — |
| Sections Table | — |
| Questions Table | — |
| Notifications Table | — |
| Reported Table | — |
| Progress Table | — |
| Storage Bucket | — |
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;
Test user account operations: Create Account, Login, OTP Verification, and Password Reset.
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).
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
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
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 supabase2. Create Account (Sign Up)
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 accountsupabase.auth.signInWithPassword()— Loginsupabase.auth.signOut()— Logoutsupabase.auth.verifyOtp()— Verify OTPsupabase.auth.resend()— Resend verification emailsupabase.auth.resetPasswordForEmail()— Request password resetsupabase.auth.updateUser()— Update password/datasupabase.auth.getSession()— Get current sessionsupabase.auth.onAuthStateChange()— Listen to auth events
All credentials are stored in localStorage and never leave your browser.
Zoho-enczapikey.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>"
}
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);
}
}
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.
sk-.Create and manage reusable HTML email templates. Templates are stored in localStorage. Use the ✉ Email button on any user row to send.
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;
Prioritise tasks by urgency and importance. Stored in Supabase — connect first. Click any task text to edit it inline.
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 $$;