mirror of
http://forgejo-oa09toasww4dgii9cj3gpzda.187.127.164.61.sslip.io/iamcoolvivek007/bharath.git
synced 2026-06-11 00:06:51 +00:00
- Govt-app styled freight marketplace - Role-based auth (driver/shipper/broker/admin) - Load board with bidding system - Trip tracking with status flow - In-app messaging - Admin panel - Mobile bottom nav + PWA - Docker + Coolify ready
365 lines
12 KiB
Markdown
365 lines
12 KiB
Markdown
# BharathTrucks — Database Schema Design
|
|
|
|
**Version:** 1.0
|
|
**Date:** 2026-05-31
|
|
**Database:** Supabase PostgreSQL
|
|
|
|
---
|
|
|
|
## 1. Schema Overview
|
|
|
|
```
|
|
┌──────────┐ ┌──────────┐ ┌──────────┐
|
|
│ Users │────▶│ Profiles │────▶│ Loads │
|
|
│(Supabase)│ │ │ │ │
|
|
└──────────┘ └──────────┘ └────┬─────┘
|
|
│ │
|
|
│ ┌────▼─────┐
|
|
│ │ Bids │
|
|
│ └────┬─────┘
|
|
│ │
|
|
┌────▼─────┐ ┌────▼─────┐
|
|
│ Trucks │ │ Trips │
|
|
└──────────┘ └────┬─────┘
|
|
│
|
|
┌────▼─────┐
|
|
│ Payments │
|
|
└──────────┘
|
|
```
|
|
|
|
---
|
|
|
|
## 2. Tables
|
|
|
|
### 2.1 profiles
|
|
Extends Supabase auth.users with app-specific data.
|
|
|
|
```sql
|
|
CREATE TABLE profiles (
|
|
id UUID PRIMARY KEY REFERENCES auth.users(id) ON DELETE CASCADE,
|
|
role TEXT NOT NULL CHECK (role IN ('driver', 'shipper', 'broker', 'admin')),
|
|
full_name TEXT NOT NULL,
|
|
phone TEXT UNIQUE NOT NULL,
|
|
email TEXT,
|
|
avatar_url TEXT,
|
|
language TEXT DEFAULT 'hi' CHECK (language IN ('en', 'hi')),
|
|
city TEXT,
|
|
state TEXT,
|
|
is_verified BOOLEAN DEFAULT FALSE,
|
|
is_premium BOOLEAN DEFAULT FALSE,
|
|
is_active BOOLEAN DEFAULT TRUE,
|
|
onboarding_complete BOOLEAN DEFAULT FALSE,
|
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ DEFAULT NOW()
|
|
);
|
|
|
|
CREATE INDEX idx_profiles_role ON profiles(role);
|
|
CREATE INDEX idx_profiles_city ON profiles(city);
|
|
```
|
|
|
|
### 2.2 driver_profiles
|
|
Additional driver-specific info.
|
|
|
|
```sql
|
|
CREATE TABLE driver_profiles (
|
|
id UUID PRIMARY KEY REFERENCES profiles(id) ON DELETE CASCADE,
|
|
license_number TEXT,
|
|
license_expiry DATE,
|
|
experience_years INTEGER DEFAULT 0,
|
|
routes_preferred TEXT[], -- ['Mumbai-Delhi', 'Chennai-Bangalore']
|
|
truck_id UUID REFERENCES trucks(id),
|
|
availability_status TEXT DEFAULT 'available' CHECK (availability_status IN ('available', 'on_trip', 'offline')),
|
|
total_trips INTEGER DEFAULT 0,
|
|
rating NUMERIC(2,1) DEFAULT 0.0,
|
|
bids_today INTEGER DEFAULT 0,
|
|
bids_today_date DATE DEFAULT CURRENT_DATE
|
|
);
|
|
```
|
|
|
|
### 2.3 shipper_profiles
|
|
|
|
```sql
|
|
CREATE TABLE shipper_profiles (
|
|
id UUID PRIMARY KEY REFERENCES profiles(id) ON DELETE CASCADE,
|
|
business_name TEXT,
|
|
gst_number TEXT,
|
|
business_type TEXT,
|
|
shipping_frequency TEXT CHECK (shipping_frequency IN ('daily', 'weekly', 'monthly', 'occasional')),
|
|
total_loads_posted INTEGER DEFAULT 0,
|
|
rating NUMERIC(2,1) DEFAULT 0.0
|
|
);
|
|
```
|
|
|
|
### 2.4 broker_profiles
|
|
|
|
```sql
|
|
CREATE TABLE broker_profiles (
|
|
id UUID PRIMARY KEY REFERENCES profiles(id) ON DELETE CASCADE,
|
|
agency_name TEXT,
|
|
experience_years INTEGER DEFAULT 0,
|
|
network_size INTEGER DEFAULT 0,
|
|
operating_regions TEXT[],
|
|
total_deals INTEGER DEFAULT 0,
|
|
total_commission NUMERIC(12,2) DEFAULT 0,
|
|
rating NUMERIC(2,1) DEFAULT 0.0
|
|
);
|
|
```
|
|
|
|
### 2.5 trucks
|
|
|
|
```sql
|
|
CREATE TABLE trucks (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
owner_id UUID NOT NULL REFERENCES profiles(id) ON DELETE CASCADE,
|
|
registration_number TEXT UNIQUE NOT NULL,
|
|
truck_type TEXT NOT NULL CHECK (truck_type IN ('open', 'closed', 'container', 'flatbed', 'tanker', 'refrigerated', 'mini')),
|
|
capacity_tons NUMERIC(5,1) NOT NULL,
|
|
make TEXT, -- Tata, Ashok Leyland, etc.
|
|
model TEXT,
|
|
year INTEGER,
|
|
is_active BOOLEAN DEFAULT TRUE,
|
|
created_at TIMESTAMPTZ DEFAULT NOW()
|
|
);
|
|
|
|
CREATE INDEX idx_trucks_owner ON trucks(owner_id);
|
|
CREATE INDEX idx_trucks_type ON trucks(truck_type);
|
|
```
|
|
|
|
### 2.6 loads
|
|
|
|
```sql
|
|
CREATE TABLE loads (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
posted_by UUID NOT NULL REFERENCES profiles(id),
|
|
shipper_id UUID REFERENCES profiles(id), -- if broker posts on behalf
|
|
origin_city TEXT NOT NULL,
|
|
origin_state TEXT NOT NULL,
|
|
destination_city TEXT NOT NULL,
|
|
destination_state TEXT NOT NULL,
|
|
weight_tons NUMERIC(5,1) NOT NULL,
|
|
truck_type_required TEXT NOT NULL,
|
|
material_type TEXT,
|
|
budget NUMERIC(10,2),
|
|
pickup_date DATE NOT NULL,
|
|
description TEXT,
|
|
is_urgent BOOLEAN DEFAULT FALSE,
|
|
status TEXT DEFAULT 'open' CHECK (status IN ('open', 'booked', 'in_transit', 'delivered', 'cancelled')),
|
|
bid_count INTEGER DEFAULT 0,
|
|
accepted_bid_id UUID,
|
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ DEFAULT NOW()
|
|
);
|
|
|
|
CREATE INDEX idx_loads_status ON loads(status);
|
|
CREATE INDEX idx_loads_origin ON loads(origin_city);
|
|
CREATE INDEX idx_loads_destination ON loads(destination_city);
|
|
CREATE INDEX idx_loads_posted_by ON loads(posted_by);
|
|
CREATE INDEX idx_loads_pickup_date ON loads(pickup_date);
|
|
CREATE INDEX idx_loads_truck_type ON loads(truck_type_required);
|
|
```
|
|
|
|
### 2.7 bids
|
|
|
|
```sql
|
|
CREATE TABLE bids (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
load_id UUID NOT NULL REFERENCES loads(id) ON DELETE CASCADE,
|
|
driver_id UUID NOT NULL REFERENCES profiles(id),
|
|
amount NUMERIC(10,2) NOT NULL,
|
|
estimated_delivery DATE,
|
|
note TEXT,
|
|
status TEXT DEFAULT 'pending' CHECK (status IN ('pending', 'accepted', 'rejected', 'withdrawn')),
|
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ DEFAULT NOW(),
|
|
UNIQUE(load_id, driver_id) -- one bid per driver per load
|
|
);
|
|
|
|
CREATE INDEX idx_bids_load ON bids(load_id);
|
|
CREATE INDEX idx_bids_driver ON bids(driver_id);
|
|
CREATE INDEX idx_bids_status ON bids(status);
|
|
```
|
|
|
|
### 2.8 trips
|
|
|
|
```sql
|
|
CREATE TABLE trips (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
load_id UUID NOT NULL REFERENCES loads(id),
|
|
driver_id UUID NOT NULL REFERENCES profiles(id),
|
|
shipper_id UUID NOT NULL REFERENCES profiles(id),
|
|
bid_id UUID NOT NULL REFERENCES bids(id),
|
|
status TEXT DEFAULT 'confirmed' CHECK (status IN ('confirmed', 'picked_up', 'in_transit', 'delivered', 'cancelled')),
|
|
picked_up_at TIMESTAMPTZ,
|
|
delivered_at TIMESTAMPTZ,
|
|
driver_rating INTEGER CHECK (driver_rating BETWEEN 1 AND 5),
|
|
shipper_rating INTEGER CHECK (shipper_rating BETWEEN 1 AND 5),
|
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ DEFAULT NOW()
|
|
);
|
|
|
|
CREATE INDEX idx_trips_driver ON trips(driver_id);
|
|
CREATE INDEX idx_trips_shipper ON trips(shipper_id);
|
|
CREATE INDEX idx_trips_status ON trips(status);
|
|
```
|
|
|
|
### 2.9 messages
|
|
|
|
```sql
|
|
CREATE TABLE messages (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
load_id UUID REFERENCES loads(id),
|
|
sender_id UUID NOT NULL REFERENCES profiles(id),
|
|
receiver_id UUID NOT NULL REFERENCES profiles(id),
|
|
content TEXT NOT NULL,
|
|
is_read BOOLEAN DEFAULT FALSE,
|
|
created_at TIMESTAMPTZ DEFAULT NOW()
|
|
);
|
|
|
|
CREATE INDEX idx_messages_receiver ON messages(receiver_id, is_read);
|
|
CREATE INDEX idx_messages_load ON messages(load_id);
|
|
```
|
|
|
|
### 2.10 payments
|
|
|
|
```sql
|
|
CREATE TABLE payments (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
trip_id UUID NOT NULL REFERENCES trips(id),
|
|
payer_id UUID NOT NULL REFERENCES profiles(id),
|
|
payee_id UUID NOT NULL REFERENCES profiles(id),
|
|
amount NUMERIC(10,2) NOT NULL,
|
|
method TEXT DEFAULT 'upi' CHECK (method IN ('upi', 'cash', 'bank_transfer')),
|
|
status TEXT DEFAULT 'pending' CHECK (status IN ('pending', 'completed', 'disputed')),
|
|
upi_reference TEXT,
|
|
notes TEXT,
|
|
created_at TIMESTAMPTZ DEFAULT NOW()
|
|
);
|
|
|
|
CREATE INDEX idx_payments_trip ON payments(trip_id);
|
|
CREATE INDEX idx_payments_payee ON payments(payee_id);
|
|
```
|
|
|
|
### 2.11 broker_commissions
|
|
|
|
```sql
|
|
CREATE TABLE broker_commissions (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
broker_id UUID NOT NULL REFERENCES profiles(id),
|
|
trip_id UUID NOT NULL REFERENCES trips(id),
|
|
load_id UUID NOT NULL REFERENCES loads(id),
|
|
amount NUMERIC(10,2) NOT NULL,
|
|
status TEXT DEFAULT 'pending' CHECK (status IN ('pending', 'received')),
|
|
created_at TIMESTAMPTZ DEFAULT NOW()
|
|
);
|
|
|
|
CREATE INDEX idx_commissions_broker ON broker_commissions(broker_id);
|
|
```
|
|
|
|
### 2.12 notifications
|
|
|
|
```sql
|
|
CREATE TABLE notifications (
|
|
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
user_id UUID NOT NULL REFERENCES profiles(id),
|
|
type TEXT NOT NULL CHECK (type IN ('bid_received', 'bid_accepted', 'bid_rejected', 'trip_update', 'payment', 'system')),
|
|
title TEXT NOT NULL,
|
|
body TEXT,
|
|
reference_id UUID, -- load_id, bid_id, trip_id
|
|
is_read BOOLEAN DEFAULT FALSE,
|
|
created_at TIMESTAMPTZ DEFAULT NOW()
|
|
);
|
|
|
|
CREATE INDEX idx_notifications_user ON notifications(user_id, is_read);
|
|
```
|
|
|
|
---
|
|
|
|
## 3. Row Level Security (RLS) Policies
|
|
|
|
```sql
|
|
-- Profiles: users can read all, update own
|
|
ALTER TABLE profiles ENABLE ROW LEVEL SECURITY;
|
|
CREATE POLICY "Public profiles readable" ON profiles FOR SELECT USING (true);
|
|
CREATE POLICY "Users update own profile" ON profiles FOR UPDATE USING (auth.uid() = id);
|
|
|
|
-- Loads: all can read open loads, owners can update
|
|
ALTER TABLE loads ENABLE ROW LEVEL SECURITY;
|
|
CREATE POLICY "Open loads readable" ON loads FOR SELECT USING (true);
|
|
CREATE POLICY "Owners manage loads" ON loads FOR ALL USING (auth.uid() = posted_by);
|
|
|
|
-- Bids: load owner + bidder can see
|
|
ALTER TABLE bids ENABLE ROW LEVEL SECURITY;
|
|
CREATE POLICY "Bid participants can view" ON bids FOR SELECT
|
|
USING (auth.uid() = driver_id OR auth.uid() IN (SELECT posted_by FROM loads WHERE id = load_id));
|
|
CREATE POLICY "Drivers create bids" ON bids FOR INSERT WITH CHECK (auth.uid() = driver_id);
|
|
|
|
-- Messages: sender and receiver only
|
|
ALTER TABLE messages ENABLE ROW LEVEL SECURITY;
|
|
CREATE POLICY "Message participants" ON messages FOR SELECT
|
|
USING (auth.uid() = sender_id OR auth.uid() = receiver_id);
|
|
CREATE POLICY "Users send messages" ON messages FOR INSERT WITH CHECK (auth.uid() = sender_id);
|
|
```
|
|
|
|
---
|
|
|
|
## 4. Database Functions
|
|
|
|
```sql
|
|
-- Auto-update updated_at timestamp
|
|
CREATE OR REPLACE FUNCTION update_updated_at()
|
|
RETURNS TRIGGER AS $$
|
|
BEGIN
|
|
NEW.updated_at = NOW();
|
|
RETURN NEW;
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|
|
|
|
CREATE TRIGGER profiles_updated_at BEFORE UPDATE ON profiles
|
|
FOR EACH ROW EXECUTE FUNCTION update_updated_at();
|
|
CREATE TRIGGER loads_updated_at BEFORE UPDATE ON loads
|
|
FOR EACH ROW EXECUTE FUNCTION update_updated_at();
|
|
CREATE TRIGGER bids_updated_at BEFORE UPDATE ON bids
|
|
FOR EACH ROW EXECUTE FUNCTION update_updated_at();
|
|
CREATE TRIGGER trips_updated_at BEFORE UPDATE ON trips
|
|
FOR EACH ROW EXECUTE FUNCTION update_updated_at();
|
|
|
|
-- Increment bid count on load when new bid placed
|
|
CREATE OR REPLACE FUNCTION increment_bid_count()
|
|
RETURNS TRIGGER AS $$
|
|
BEGIN
|
|
UPDATE loads SET bid_count = bid_count + 1 WHERE id = NEW.load_id;
|
|
RETURN NEW;
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|
|
|
|
CREATE TRIGGER bid_count_trigger AFTER INSERT ON bids
|
|
FOR EACH ROW EXECUTE FUNCTION increment_bid_count();
|
|
|
|
-- Reset daily bid count
|
|
CREATE OR REPLACE FUNCTION reset_daily_bids()
|
|
RETURNS void AS $$
|
|
BEGIN
|
|
UPDATE driver_profiles SET bids_today = 0, bids_today_date = CURRENT_DATE
|
|
WHERE bids_today_date < CURRENT_DATE;
|
|
END;
|
|
$$ LANGUAGE plpgsql;
|
|
```
|
|
|
|
---
|
|
|
|
## 5. Seed Data (Development)
|
|
|
|
```sql
|
|
-- Truck types reference
|
|
INSERT INTO trucks (owner_id, registration_number, truck_type, capacity_tons, make)
|
|
VALUES
|
|
-- Will be populated during testing
|
|
;
|
|
|
|
-- Sample cities for load testing
|
|
-- Mumbai, Delhi, Bangalore, Chennai, Hyderabad, Ahmedabad, Pune, Kolkata, Jaipur, Nagpur
|
|
```
|
|
|
|
---
|
|
|
|
*Schema designed for simplicity in Phase 1. Normalized where needed, denormalized (bid_count, total_trips) for read performance.*
|