# GITHUB REPO

*Complete Developer Onboarding & Development Guide*

## 🎯 Quick Start (5-minute setup)

```bash
gh repo clone Min11Benja/alertagas-twilio-sms
cd alertagas-twilio-sms
npm install
cp .env.example .env.local
# Fill environment variables (ask team lead)
npm run dev
```

***

## 📖 Table of Contents

1. [Architecture Overview](#-architecture-overview)
2. [Prerequisites](#-prerequisites)
3. [Environment Setup](#-environment-setup)
4. [Database Setup](#-database-setup)
5. [Twilio Configuration](#-twilio-configuration)
6. [Development Workflow](#-development-workflow)
7. [API Reference](#-api-reference)
8. [Deployment](#-deployment)
9. [Troubleshooting](#-troubleshooting)

***

## 🏗️ Architecture Overview

**Tech Stack**

* **Frontend:** Next.js 14+ (TypeScript) + Tailwind CSS
* **Backend:** Next.js API Routes + Server Components
* **Database:** Supabase (PostgreSQL) with Row Level Security
* **SMS:** Twilio Programmable SMS
* **Auth:** Supabase Authentication
* **Deployment:** Google Cloud Run + GitHub Actions

**Data Flow**

```
Twilio SMS → Webhook API → Supabase DB → Next.js Frontend → Real-time Updates
```

**Core Features**

* ✅ Secure Twilio webhook validation with signature verification
* ✅ SMS parsing and automatic sensor/client linking
* ✅ Real-time dashboard with gas level monitoring
* ✅ Multi-channel notifications (SMS + Email)
* ✅ Multi-tenant architecture with RLS
* ✅ Automated CI/CD with quality gates

***

## ⚙️ Prerequisites

### Required Tools

* **Node.js 18+** (`nvm use` automatically selects correct version)
* **npm** or **pnpm** (project uses npm by default)
* **Git** + GitHub CLI (recommended)
* **VS Code** with extensions:
  * ESLint, Prettier, TypeScript Hero
  * Tailwind CSS IntelliSense
  * Thunder Client (API testing)

### Accounts Needed

* **Twilio Account** with SMS-capable phone number
* **Supabase Project** for database and auth
* **Google Cloud Project** for deployment (production)

***

## 🔧 Environment Setup

### 1. Clone & Install

```bash
# Using GitHub CLI (recommended)
gh repo clone Min11Benja/alertagas-twilio-sms
cd alertagas-twilio-sms

# Using Git
git clone https://github.com/Min11Benja/alertagas-twilio-sms.git
cd alertagas-twilio-sms

# Install dependencies
npm install
```

### 2. Environment Configuration

Copy the environment template and fill with your credentials:

```bash
cp .env.example .env.local
```

**Required Variables** (get these from your team lead):

```env
# Twilio (get from Twilio Console)
TWILIO_ACCOUNT_SID=ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
TWILIO_AUTH_TOKEN=your_auth_token_here

# Supabase (get from Project Settings → API)
NEXT_PUBLIC_SUPABASE_URL=https://your-project.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJ0eXAiOiJKV1QiLCJhbGciOi...
SUPABASE_SERVICE_ROLE_KEY=eyJ0eXAiOiJKV1QiLCJhbGciOi...  # 🚨 SERVER ONLY

# Resend (Email Notifications - Optional)
RESEND_API_KEY=re_xxxxxxxxxxxxxxxxxxxx

# Optional: Google Maps
NEXT_PUBLIC_GOOGLE_MAPS_API_KEY=your_maps_api_key
```

> 🔒 **Security Note:** Never commit `.env.local` or expose service role keys in client code.

***

## 🗃️ Database Setup

### Automated Schema Setup

1. Go to your Supabase project → **SQL Editor**
2. Run the complete schema script from the expanded section below
3. Verify setup with the provided validation query

<details>

<summary><strong>📋 Click to view complete SQL schema</strong></summary>

```sql
-- ============================================
-- ALERTAGAS — CLEAN DATABASE SCHEMA SETUP
-- For a new Supabase project (no prior objects)
-- ============================================

-- Extensions (for UUIDs, text search)
create extension if not exists pgcrypto;
create extension if not exists pg_trgm;

-- ============================================
-- ENUMS
-- ============================================
do $$ begin
  create type public.message_type as enum ('reading','heartbeat','battery','alert','other');
exception when duplicate_object then null; end $$;

do $$ begin
  create type public.battery_status as enum ('UNKNOWN','LOW','MEDIUM','HIGH','CHARGING');
exception when duplicate_object then null; end $$;

do $$ begin
  create type public.sms_status as enum ('received','parsed','linked','ignored','error');
exception when duplicate_object then null; end $$;

-- ============================================
-- TABLE: clients
-- ============================================
create table if not exists public.clients (
  id uuid primary key default gen_random_uuid(),
  name text not null,
  phone_number text,
  email text,
  contact_number text,
  address text,
  latitude numeric,
  longitude numeric,
  created_at timestamptz default now()
);

-- ============================================
-- TABLE: profiles
-- ============================================
create table if not exists public.profiles (
  user_id uuid primary key references auth.users(id) on delete cascade,
  name text,
  email text unique,
  client_id uuid references public.clients(id) on delete set null,
  role text default 'user',
  created_at timestamptz default now(),
  updated_at timestamptz default now()
);
create index if not exists idx_profiles_client_id on public.profiles(client_id);

-- ============================================
-- TABLE: sensors
-- ============================================
create table if not exists public.sensors (
  id uuid primary key default gen_random_uuid(),
  sensor_name text,
  phone_number text not null unique,
  normalized_phone text,
  client_id uuid references public.clients(id) on delete set null,
  created_at timestamptz default now(),
  fill_level numeric,
  status text default 'active',
  last_updated timestamptz default now(),
  address text,
  threshold_percent integer default 20
);
create unique index if not exists uidx_sensors_normalized_phone
  on public.sensors (coalesce(normalized_phone, phone_number));
create index if not exists idx_sensors_client_id on public.sensors(client_id);

-- ============================================
-- TABLE: sms_messages
-- ============================================
create table if not exists public.sms_messages (
  id uuid primary key default gen_random_uuid(),
  from_number text not null,
  normalized_from text,
  body text not null,
  received_at timestamptz default now(),
  sensor_id uuid references public.sensors(id) on delete set null,
  client_id uuid references public.clients(id) on delete set null,
  status public.sms_status default 'received',
  message_type public.message_type default 'other',
  gas_level integer,
  battery public.battery_status default 'UNKNOWN',
  raw_payload jsonb,
  is_linked boolean default false,
  dedupe_key text,
  dedupe_hash text
);
create index if not exists idx_sms_messages_received_at on public.sms_messages(received_at desc);
create index if not exists idx_sms_messages_sensor_id   on public.sms_messages(sensor_id);
create index if not exists idx_sms_messages_client_id   on public.sms_messages(client_id);
create index if not exists idx_sms_messages_from_trgm   on public.sms_messages using gin (from_number gin_trgm_ops);
create unique index if not exists uidx_sms_dedupe_key   on public.sms_messages(dedupe_key);
create unique index if not exists uidx_sms_dedupe_hash  on public.sms_messages(dedupe_hash);

-- ============================================
-- TABLE: gas_readings
-- ============================================
create table if not exists public.gas_readings (
  id uuid primary key default gen_random_uuid(),
  sensor_id uuid not null references public.sensors(id) on delete cascade,
  client_id uuid not null references public.clients(id) on delete cascade,
  gas_level integer not null check (gas_level between 0 and 100),
  status text not null check (status in ('Low','Normal','High')),
  location jsonb,
  reading numeric check (reading is null or reading >= 0),
  recorded_at timestamptz default now()
);
create index if not exists idx_readings_sensor_time on public.gas_readings(sensor_id, recorded_at desc);
create index if not exists idx_readings_client_time on public.gas_readings(client_id, recorded_at desc);

-- ============================================
-- TABLE: alerts
-- ============================================
create table if not exists public.alerts (
  id uuid primary key default gen_random_uuid(),
  sensor_id uuid not null references public.sensors(id) on delete cascade,
  alert_type text not null,
  message text not null,
  status text default 'new',
  created_at timestamptz default now(),
  resolved_at timestamptz
);
create index if not exists idx_alerts_sensor_time on public.alerts(sensor_id, created_at desc);

-- ============================================
-- ROW LEVEL SECURITY (RLS)
-- ============================================
alter table public.clients      enable row level security;
alter table public.sensors      enable row level security;
alter table public.sms_messages enable row level security;
alter table public.gas_readings enable row level security;
alter table public.alerts       enable row level security;
alter table public.profiles     enable row level security;

-- Drop any existing policies to avoid duplicates
do $$
declare p record;
begin
  for p in select policyname, tablename from pg_policies where schemaname='public' loop
    execute format('drop policy if exists %I on public.%I;', p.policyname, p.tablename);
  end loop;
end $$;

-- ============================================
-- POLICIES (RLS)
-- ============================================
create policy profiles_self_select
  on public.profiles for select
  using (auth.uid() = user_id);

create policy profiles_self_update
  on public.profiles for update
  using (auth.uid() = user_id);

create policy clients_read_own
  on public.clients for select
  using (id = (select client_id from public.profiles where user_id = auth.uid()));

create policy sensors_read_own
  on public.sensors for select
  using (client_id = (select client_id from public.profiles where user_id = auth.uid()));

create policy sms_read_own
  on public.sms_messages for select
  using (client_id = (select client_id from public.profiles where user_id = auth.uid()));

create policy readings_read_own
  on public.gas_readings for select
  using (client_id = (select client_id from public.profiles where user_id = auth.uid()));

create policy alerts_read_own
  on public.alerts for select
  using (sensor_id in (
    select id from public.sensors
    where client_id = (select client_id from public.profiles where user_id = auth.uid())
  ));

-- ============================================
-- ✅ FINISHED
-- Verify everything
-- ============================================
select '✅ AlertaGas schema created successfully!' as status;
```

</details>

### Post-Setup Verification

```sql
-- Check tables were created
SELECT table_name FROM information_schema.tables 
WHERE table_schema = 'public' 
ORDER BY table_name;

-- Verify RLS is enabled
SELECT tablename, rowsecurity 
FROM pg_tables 
WHERE schemaname = 'public';
```

### Enable Real-time (Optional)

For live SMS updates in the dashboard:

1. Supabase Dashboard → **Database → Replication**
2. Enable **Realtime** for `public.sms_messages` table

***

## 📞 Twilio Configuration

### 1. Get Twilio Credentials

* Visit [Twilio Console](https://console.twilio.com)
* Copy **Account SID** and **Auth Token** to your `.env.local`

### 2. Buy a Phone Number

* In Twilio Console: **Phone Numbers → Manage → Buy a Number**
* Choose an SMS-capable number in your target region

### 3. Configure Webhook in Development

```bash
# Start development server
npm run dev

# Expose localhost (in separate terminal)
npx ngrok http 3000
```

**Twilio Webhook Setup:**

* Phone Numbers → Manage Numbers → \[Your Number]
* **A message comes in**: `POST` → `https://your-ngrok-url.ngrok.io/api/receive-sms`
* **Status callback**: (Optional) Same URL

***

## 🛠️ Development Workflow

### Starting Development

```bash
# Start development server
npm run dev

# Run in different terminals as needed
npm run lint:watch    # Watch mode for ESLint
npm run typecheck     # TypeScript validation
```

### Code Quality & Git Hooks

The project uses automated quality checks:

**Pre-commit (automatically runs):**

* ESLint on staged files
* TypeScript type checking
* Prettier code formatting
* Environment variable validation

**Pre-push (automatically runs):**

* Full test suite
* Build verification

**CI/CD Pipeline (on every PR):**

* Full linting and type checking
* Test suite execution
* Build verification
* Schema consistency checks

### Branch & Commit Convention

```bash
# Branch naming
git checkout -b feature/agt-123-add-sensor-search
git checkout -b bugfix/agt-456-fix-auth-redirect
git checkout -b hotfix/agt-789-critical-production-fix

# Commit messages
git commit -m "feat(agt-123): add sensor search functionality"
git commit -m "fix(agt-456): handle null sensor data gracefully"
git commit -m "chore: update dependencies to latest versions"
```

### Testing Your Setup

1. **Start services:** `npm run dev` + `ngrok http 3000`
2. **Update Twilio** with new ngrok URL
3. **Send test SMS** to your Twilio number
4. **Verify:**
   * SMS appears in Supabase `sms_messages` table
   * Message displays on <http://localhost:3000>
   * (If enabled) Real-time updates work automatically

***

## 🌐 API Reference

### Core Endpoints

| Endpoint                | Method | Auth             | Purpose                       |
| ----------------------- | ------ | ---------------- | ----------------------------- |
| `POST /api/receive-sms` | POST   | Twilio Signature | Receive & process inbound SMS |
| `POST /api/send-sms`    | POST   | Service Role     | Send outbound SMS             |
| `GET /api/clients`      | GET    | Browser (RLS)    | List user's clients           |
| `GET /api/sensors`      | GET    | Browser (RLS)    | List sensors with search      |
| `GET /api/sms/messages` | GET    | Browser (RLS)    | View SMS history              |
| `GET /api/gas_readings` | GET    | Browser (RLS)    | Gas level time series         |
| `GET /api/settings`     | GET    | Browser (RLS)    | Get global settings           |
| `PATCH /api/settings`   | PATCH  | Browser (RLS)    | Update global settings        |
| `PATCH /api/alerts/:id` | PATCH  | Browser (RLS)    | Resolve an alert              |

### Webhook Example

```typescript
// POST /api/receive-sms
// Headers: x-twilio-signature, Content-Type: application/x-www-form-urlencoded
// Body: From=+1234567890&Body=Gas%20level%3A%2075%25&To=+1987654320
```

### Client-Side Query Example

```typescript
// Using useFetchData hook (RLS-enforced)
const { data: sensors, loading } = useFetchData(
  '/api/sensors',
  { search: 'tank-1' }
);
```

***

## 📧 Notifications System

### Multi-Channel Alerts

Alertagas supports multiple notification channels for low tank alerts:

* **SMS** (Twilio) - Enabled by default
* **Email** (Resend) - Optional
* **In-App** (Real-time toasts) - Always enabled

### Email Setup (Optional)

1. Sign up at [resend.com](https://resend.com)
2. Create an API key
3. Add to `.env.local`:

```env
RESEND_API_KEY=re_xxxxxxxxxxxxxxxxxxxx
```

4. Enable email in Settings: `/dashboard/settings/globales`

### Settings Configuration

Configure notifications at `/dashboard/settings/globales`:

* Enable/disable SMS and email notifications
* Set default alert threshold (default: 20%)
* Configure email from address
* Enable auto-resolution of alerts

### Documentation

See [docs/alertagas-context/007-notifications-system.md](https://github.com/Min11Benja/alertagas-twilio-sms/blob/main/docs/alertagas-context/007-notifications-system.md) for complete documentation.

***

## 🚀 Deployment

### Production Deployment (Google Cloud Run)

**Prerequisites:**

* GCP Project with Cloud Run, Artifact Registry, and IAM APIs enabled
* Service account with Cloud Run Admin and Service Account User roles
* Workload Identity Federation configured for GitHub Actions

**GitHub Secrets Required:**

```env
GCP_WORKLOAD_IDENTITY_PROVIDER
GCP_SERVICE_ACCOUNT
NEXT_PUBLIC_SUPABASE_URL
NEXT_PUBLIC_SUPABASE_ANON_KEY
SUPABASE_SERVICE_ROLE_KEY
TWILIO_ACCOUNT_SID
TWILIO_AUTH_TOKEN
# ... other environment variables
```

**Deployment Process:**

* Automatic on push to `main` branch
* Manual trigger via GitHub Actions UI
* Health checks and rollback on failure

### Getting Service URL

```bash
gcloud run services describe nextjs-cloud-run \
  --region us-central1 \
  --format 'value(status.url)'
```

***

## 🐛 Troubleshooting

### Common Issues & Solutions

| Problem                               | Solution                                                           |
| ------------------------------------- | ------------------------------------------------------------------ |
| **Twilio 403 Signature Invalid**      | Verify `TWILIO_AUTH_TOKEN` matches Twilio console, check ngrok URL |
| **SMS Not Saving to Database**        | Check service role key, verify API route uses admin client         |
| **No Real-time Updates**              | Enable replication on `sms_messages` table in Supabase             |
| **Environment Variables Not Loading** | Restart dev server after editing `.env.local`                      |
| **RLS Blocking Data Access**          | Verify user has profile with client\_id, check policy conditions   |

### Debug Tools

```bash
# Check environment variables are loaded
npm run validate:env

# Inspect ngrok traffic
http://127.0.0.1:4040  # ngrok web interface

# Database inspection
npx supabase status    # Verify local Supabase connection
```

### Getting Help

1. Check application logs in browser console and terminal
2. Examine Supabase logs in dashboard
3. Use ngrok inspector to see webhook requests

## Documentation

> [**👉 Commander's Manual: How to Instruct Agents**](https://github.com/Min11Benja/alertagas-twilio-sms/blob/main/docs/USER_MANUAL.md) (Start Here!)

For full documentation, see [docs/README.md](https://github.com/Min11Benja/alertagas-twilio-sms/blob/main/docs/README.md).

* [Project Context](https://github.com/Min11Benja/alertagas-twilio-sms/blob/main/docs/project-context/README.md)
* [Agents](https://github.com/Min11Benja/alertagas-twilio-sms/blob/main/docs/agents/README.md)
* [Workflows](https://github.com/Min11Benja/alertagas-twilio-sms/blob/main/docs/workflows/README.md)

***

## 🔒 Security Notes

* **Service Role Key**: Never use in browser code - API routes only
* **Twilio Signatures**: Always validate in webhook endpoints
* **RLS Policies**: All tables have row-level security enabled
* **Environment Variables**: Keep secure variables in `.env.local` only

***

## 📞 Support & Resources

**Project Maintainer:** Jossue Benjamin Martínez Juárez\
📧 **Email:** <contacto@alertagas.com>\
🐙 **GitHub:** [@min11benja](https://github.com/Min11Benja)

**Useful Links:**

* [Project Repository](https://github.com/Min11Benja/alertagas-twilio-sms)
* [Supabase Documentation](https://supabase.com/docs)
* [Twilio SMS Guide](https://www.twilio.com/docs/sms)
* [Next.js Documentation](https://nextjs.org/docs)

***

## 📄 License

MIT License - See `LICENSE` file for details.

***

### 🎯 Quick Reference

```bash
# Daily development commands
npm run dev          # Start development
npm run lint         # Check code quality  
npm run typecheck    # Verify types
npm run build        # Production build test

# Deployment
git push origin main # Triggers auto-deploy

# Testing
curl -X POST https://your-app.ngrok.io/api/receive-sms \
  -d "From=+1234567890&Body=Test%20Message"
```
