-- ============================================================ -- BharathTrucks — FULL DATABASE SETUP v2.0 -- Run this ONCE in Supabase SQL Editor -- ============================================================ -- 1. USERS CREATE TABLE IF NOT EXISTS app_users ( id UUID DEFAULT gen_random_uuid() PRIMARY KEY, username TEXT UNIQUE NOT NULL, name TEXT NOT NULL, password_hash TEXT NOT NULL, role TEXT NOT NULL CHECK (role IN ('driver', 'shipper', 'broker', 'admin')), phone TEXT, city TEXT, state TEXT, is_verified BOOLEAN DEFAULT FALSE, is_premium BOOLEAN DEFAULT FALSE, is_active BOOLEAN DEFAULT TRUE, created_at TIMESTAMPTZ DEFAULT NOW() ); CREATE INDEX IF NOT EXISTS idx_app_users_role ON app_users(role); CREATE INDEX IF NOT EXISTS idx_app_users_username ON app_users(username); CREATE INDEX IF NOT EXISTS idx_app_users_active ON app_users(is_active); CREATE INDEX IF NOT EXISTS idx_app_users_created ON app_users(created_at DESC); CREATE INDEX IF NOT EXISTS idx_app_users_phone ON app_users(phone) WHERE phone IS NOT NULL; -- 2. LOADS CREATE TABLE IF NOT EXISTS loads ( id UUID DEFAULT gen_random_uuid() PRIMARY KEY, posted_by UUID NOT NULL REFERENCES app_users(id) ON DELETE CASCADE, origin_city TEXT NOT NULL, destination_city TEXT NOT NULL, weight_tons NUMERIC(5,1) NOT NULL, truck_type 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 IF NOT EXISTS idx_loads_status ON loads(status); CREATE INDEX IF NOT EXISTS idx_loads_origin ON loads(origin_city); CREATE INDEX IF NOT EXISTS idx_loads_destination ON loads(destination_city); CREATE INDEX IF NOT EXISTS idx_loads_posted_by ON loads(posted_by); CREATE INDEX IF NOT EXISTS idx_loads_status_created ON loads(status, created_at DESC); CREATE INDEX IF NOT EXISTS idx_loads_truck_type ON loads(truck_type); CREATE INDEX IF NOT EXISTS idx_loads_urgent ON loads(is_urgent) WHERE is_urgent = true; -- 3. BIDS CREATE INDEX IF NOT EXISTS idx_bids_load ON bids(load_id); CREATE INDEX IF NOT EXISTS idx_bids_driver ON bids(driver_id); CREATE INDEX IF NOT EXISTS idx_bids_status ON bids(status); CREATE INDEX IF NOT EXISTS idx_bids_load_driver ON bids(load_id, driver_id); -- 4. TRIPS CREATE INDEX IF NOT EXISTS idx_trips_driver ON trips(driver_id); CREATE INDEX IF NOT EXISTS idx_trips_shipper ON trips(shipper_id); CREATE INDEX IF NOT EXISTS idx_trips_status ON trips(status); CREATE INDEX IF NOT EXISTS idx_trips_driver_status ON trips(driver_id, status); CREATE INDEX IF NOT EXISTS idx_trips_shipper_status ON trips(shipper_id, status); CREATE INDEX IF NOT EXISTS idx_trips_created ON trips(created_at DESC); -- 5. MESSAGES CREATE INDEX IF NOT EXISTS idx_messages_receiver ON messages(receiver_id, is_read); CREATE INDEX IF NOT EXISTS idx_messages_sender ON messages(sender_id); CREATE INDEX IF NOT EXISTS idx_messages_conversation ON messages(sender_id, receiver_id, created_at DESC); CREATE INDEX IF NOT EXISTS idx_messages_load ON messages(load_id) WHERE load_id IS NOT NULL; -- 6. TRIGGERS 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; DROP TRIGGER IF EXISTS bid_count_trigger ON bids; CREATE TRIGGER bid_count_trigger AFTER INSERT ON bids FOR EACH ROW EXECUTE FUNCTION increment_bid_count(); CREATE OR REPLACE FUNCTION update_updated_at() RETURNS TRIGGER AS $$ BEGIN NEW.updated_at = NOW(); RETURN NEW; END; $$ LANGUAGE plpgsql; DROP TRIGGER IF EXISTS loads_updated_at ON loads; CREATE TRIGGER loads_updated_at BEFORE UPDATE ON loads FOR EACH ROW EXECUTE FUNCTION update_updated_at(); -- Add updated_at to trips if not exists DO $$ BEGIN IF NOT EXISTS (SELECT 1 FROM information_schema.columns WHERE table_name = 'trips' AND column_name = 'updated_at') THEN ALTER TABLE trips ADD COLUMN updated_at TIMESTAMPTZ DEFAULT NOW(); CREATE TRIGGER trips_updated_at BEFORE UPDATE ON trips FOR EACH ROW EXECUTE FUNCTION update_updated_at(); END IF; END $$; -- 7. RLS (open for now — tighten later) ALTER TABLE app_users ENABLE ROW LEVEL SECURITY; ALTER TABLE loads ENABLE ROW LEVEL SECURITY; ALTER TABLE bids ENABLE ROW LEVEL SECURITY; ALTER TABLE trips ENABLE ROW LEVEL SECURITY; ALTER TABLE messages ENABLE ROW LEVEL SECURITY; DROP POLICY IF EXISTS "open" ON app_users; DROP POLICY IF EXISTS "open" ON loads; DROP POLICY IF EXISTS "open" ON bids; DROP POLICY IF EXISTS "open" ON trips; DROP POLICY IF EXISTS "open" ON messages; CREATE POLICY "open" ON app_users FOR ALL USING (true) WITH CHECK (true); CREATE POLICY "open" ON loads FOR ALL USING (true) WITH CHECK (true); CREATE POLICY "open" ON bids FOR ALL USING (true) WITH CHECK (true); CREATE POLICY "open" ON trips FOR ALL USING (true) WITH CHECK (true); CREATE POLICY "open" ON messages FOR ALL USING (true) WITH CHECK (true); -- 8. SEED ADMIN USER (password: admin123) INSERT INTO app_users (username, name, password_hash, role) VALUES ('admin', 'Admin', '$2a$10$LAzb105XOhAqSXm1jMUQue0fp/lrUJBhsLgkobOQTeU.8Jh4rTaoq', 'admin') ON CONFLICT (username) DO NOTHING; -- ============================================================ -- DONE! Your database is ready. -- Admin login: username=admin, password=admin123 -- ============================================================