Skip to content

Latest commit

 

History

History
450 lines (354 loc) · 13.9 KB

File metadata and controls

450 lines (354 loc) · 13.9 KB

🏢 Organization Feature — حِرفة

A complete organization management system that allows craftsmen to form teams, manage bookings under one brand, and operate as a professional services company.


Table of Contents

  1. Overview
  2. How It Works
  3. Database Schema
  4. Pages & Routes
  5. Booking Flow
  6. Payment Flow
  7. Rating System
  8. Worker Management
  9. Plan System
  10. RPC Functions
  11. Migrations

Overview

The Organization feature transforms Hirfa from a 1-to-1 worker-client platform into a scalable multi-worker business model. A worker can create an organization, recruit other workers under it, and manage all bookings, payments, and ratings through a centralized dashboard.

Key Entities

Entity Description
Organization A business entity (e.g., "ورشة حكيم الجن وأولاده") owned by a worker
Organization Worker A worker linked to an org (may or may not have a linked user account)
Org Booking A client booking that goes through the organization
Org Review A client review for the organization

How It Works

Lifecycle

1. Worker creates organization (becomes owner)
       ↓
2. Owner adds workers to the org
       ↓
3. Client browses orgs on home/services → views org profile
       ↓
4. Client books an org service → booking created with organization_id
       ↓
5. Org owner sees booking in dashboard → assigns a worker
       ↓
6. Worker completes job → confirmation code flow
       ↓
7. Payment credited to org owner's wallet
       ↓
8. Client rates → rating goes to both worker AND organization

Roles

Role Can Do
Org Owner Create/edit org, add/remove workers, assign workers to bookings, finish jobs, manage wallet
Org Worker Complete assigned jobs, see "rating as part of org" on profile
Client Browse orgs, view org profile, book org services, rate org

Database Schema

Tables

organizations

id              UUID PRIMARY KEY
owner_id        UUID NOT NULLauth.users
name            TEXT NOT NULL
description     TEXT
category        TEXT
city            TEXT
logo_url        TEXT
plan            TEXT DEFAULT 'free'     -- 'free' | 'pro'
is_active       BOOLEAN DEFAULT true
rating          NUMERIC(3,2) DEFAULT 0
reviews_count   INTEGER DEFAULT 0
worker_count    INTEGER DEFAULT 0
created_at      TIMESTAMPTZ
updated_at      TIMESTAMPTZ

organization_workers

id              UUID PRIMARY KEY
organization_id UUID NOT NULL → organizations (CASCADE)
linked_user_id  UUID → auth.users (SET NULL)
display_name    TEXT NOT NULL
avatar_url      TEXT
phone           TEXT
is_active       BOOLEAN DEFAULT true
created_at      TIMESTAMPTZ

organization_worker_portfolio

id                      UUID PRIMARY KEY
organization_worker_id  UUID NOT NULL → organization_workers (CASCADE)
image_url               TEXT NOT NULL
sort_order              SMALLINT DEFAULT 0
created_at              TIMESTAMPTZ

organization_services

id              UUID PRIMARY KEY
organization_id UUID NOT NULL → organizations (CASCADE)
name            TEXT NOT NULL
description     TEXT
price           NUMERIC
icon            TEXT
is_active       BOOLEAN DEFAULT true
created_at      TIMESTAMPTZ

organization_gallery

id              UUID PRIMARY KEY
organization_id UUID NOT NULL → organizations (CASCADE)
image_url       TEXT NOT NULL
sort_order      SMALLINT DEFAULT 0
created_at      TIMESTAMPTZ

organization_reviews

id              UUID PRIMARY KEY
organization_id UUID NOT NULL → organizations (CASCADE)
client_id       UUID NOT NULLauth.users (CASCADE)
booking_id      UUID → bookings
rating          SMALLINT NOT NULL CHECK (1-5)
text            TEXT
created_at      TIMESTAMPTZ

Bookings Table (Org Columns)

-- Added via migration
organization_id    UUID → organizations
assigned_worker_id UUID → organization_workers
worker_id          UUID → profiles (now nullable)
craftsman_id       UUID → profiles (now nullable)

Pages & Routes

Client Side

Route Page Description
/client/home Home Shows orgs alongside workers in "حرفيين متميزين"
/client/services/[categoryId] Services Lists orgs and workers for a category
/client/organization/[id] Org Profile Services catalog, portfolio, workers, reviews, booking CTA
/client/booking/[workerId] Booking Supports org mode (detects organization_id param)
/client/booking/confirm Confirm Confirms booking (org bookings don't need worker profile)
/client/orders Orders Shows org name + assigned worker name for org bookings
/client/order/invoice Invoice Shows org branding for org bookings
/client/rate-review/[bookingId] Rating Rates org (title: "تقييم المؤسسة") for org bookings

Organization Owner Side

Route Page Description
/organization/home Dashboard Stats (bookings, revenue, pending, completed), recent orders, rating, gallery, quick actions
/organization/orders Orders Full order list with worker assignment UI
/organization/workers Workers Add/remove workers, search linked accounts, portfolio upload
/organization/messages Messages Chat with clients
/organization/wallet Wallet Earnings, transactions, withdrawals
/organization/settings Settings Org settings, services management

Navigation (Bottom Tab Bar)

المؤسسة | الطلبات | العمال | الرسائل | المحفظة | الإعدادات
Home     | Orders  | Workers| Messages| Wallet  | Settings

Booking Flow

1. Client Books an Organization

Client selects org service on /client/organization/[id]
    ↓
Client taps "احجز الآن" → /client/booking/[workerId]?organization_id=[orgId]
    ↓
Client selects date, time, address, payment method
    ↓
Client confirms → booking created with:
  - organization_id = org.id
  - worker_id = null (no specific worker yet)
  - service_name = selected service
  - status = 'pending'

2. Org Owner Assigns Worker

Owner sees new booking in /organization/orders
    ↓
Owner taps "تعيين عامل للحجز"
    ↓
Owner searches org workers (from organization_workers table)
    ↓
Owner selects worker → booking updated with:
  - assigned_worker_id = org_worker.id
  - worker_id = linked_user_id (if worker has a linked account)
    ↓
Notification sent to assigned worker

3. Worker Completes Job

Assigned worker (or org owner) updates tracking status
    ↓
Worker taps "إنهاء العمل" → finish_job RPC called
    ↓
6-digit confirmation code generated → status = 'awaiting_code_entry'
    ↓
Client shares code with worker → verify_confirmation_code RPC
    ↓
Status = 'completed' → processBookingCompletion() runs

4. Job Completion

processBookingCompletion(bookingId)
    ↓
Detects organization_id → finds org owner via organizations.owner_id
    ↓
Credits org owner's wallet_balance and total_earnings
    ↓
Inserts transaction with "(مؤسسة)" label

Payment Flow

Direct Bookings (No Organization)

Booking has organization_id = null
    ↓
Payment goes to booking.worker_id's wallet
    ↓
Commission based on worker's subscription tier

Organization Bookings

Booking has organization_id set
    ↓
processBookingCompletion() detects org booking
    ↓
Fetches org owner: organizations.owner_id → profiles
    ↓
Credits OWNER's wallet_balance and total_earnings
    ↓
Commission based on OWNER's subscription tier
    ↓
Transaction logged: "أرباح الطلب #xxx (مؤسسة)"

Commission Rates

Plan Commission Description
Free (basic) 15% Default for new users
Pro 10% Professional plan
Master 5% Master plan
Premium 0% Premium plan (no commission)

Rating System

Worker Ratings

  • Stored in reviews table
  • Average computed via update_worker_rating() RPC
  • Stored on profiles.rating

Organization Ratings

  • Stored in organization_reviews table
  • Average auto-computed via trigger on_org_review_change
  • Stored on organizations.rating and organizations.reviews_count

Auto-Propagation Trigger

When a worker review is inserted, a database trigger (on_review_to_org) checks if the worker belongs to an organization:

-- Trigger: on_review_to_org (AFTER INSERT on reviews)
IF craftsman_id exists in organization_workers.linked_user_idINSERT INTO organization_reviews (same rating, text, client, booking)
  → Existing trigger on_organization_reviews auto-updates org.rating

This means one client rating automatically updates both the worker's profile AND the organization's rating.

Worker Profile Display

Workers who belong to an organization see on their profile:

┌─────────────────────────────────────────┐
│ 🏢 تقييم كجزء من المؤسسة               │
│ ورشة حكيم الجن وأولاده     ⭐ 4.8      │
└─────────────────────────────────────────┘

Shown on:

  • Worker's own profile (/worker/profile)
  • Worker profile summary (/worker/profile/summary)
  • Client craftsman page (/client/craftsman/[id])

Worker Management

Adding Workers

  1. Owner goes to /organization/workers
  2. Taps "إضافة عامل"
  3. Can search for existing Hirfa users by name or phone
  4. If found, links the account (linked_user_id)
  5. If not found, creates a worker entry with display_name only
  6. Worker limit enforced by plan (free: 5, pro: unlimited)

Worker Entry Structure

organization_workers:
  id: UUID
  organization_id: org.id
  linked_user_id: user.id (nullable — worker may not have an account yet)
  display_name: "أحمد محمد"
  avatar_url: null
  phone: "01xxxxxxxxx"
  is_active: true
  portfolio: [photo1, photo2, photo3, photo4]  -- max 4 photos

Assigning Workers to Bookings

In the OrderCard component (components/ui/orders/OrderCard.tsx):

  1. Org owner sees a "تعيين عامل للحجز" button for unassigned bookings
  2. Tapping opens a worker list fetched from organization_workers
  3. Owner selects a worker
  4. Booking is updated: assigned_worker_id set, worker_id set to linked user
  5. Notification sent to the assigned worker

Plan System

Plan Worker Limit Features
Free 5 workers Basic org features, 15% commission
Pro Unlimited All features, 10% commission

Plan Display

  • Org header card shows plan badge (مجانية / احترافية)
  • Worker count shows "X / ∞ عامل" for pro plans
  • Upgrade button links to settings

RPC Functions

finish_job(p_booking_id UUID)

  • Allows org owner to finish jobs (checks organizations.owner_id = auth.uid())
  • Generates 6-digit confirmation code
  • Sets status to awaiting_code_entry
  • Uses md5() for code generation

verify_confirmation_code(p_booking_id UUID, p_code TEXT)

  • Allows org owner to verify codes (same owner check pattern)
  • Sets status to completed
  • Returns { success: boolean, remaining: number }

update_worker_rating(p_worker_id UUID)

  • Recalculates worker average from reviews table
  • Updates profiles.rating

update_org_rating(p_org_id UUID)

  • Recalculates org average from organization_reviews table
  • Updates organizations.rating and organizations.reviews_count
  • Auto-triggered by on_org_review_change trigger

Migrations

Migration Purpose
20260712010000_org_services_gallery.sql Organization services, gallery tables, RLS policies
20260713010000_org_reviews.sql Organization reviews table, rating columns, auto-update trigger
20260713020000_make_worker_id_nullable.sql Make bookings.worker_id and craftsman_id nullable for org bookings
20260713030000_finish_job_org_owner.sql Allow org owner to call finish_job RPC
20260713040000_fix_finish_job_md5.sql Fix finish_job to use md5() instead of digest()
20260713050000_fix_verify_code_org_owner.sql Allow org owner to verify confirmation codes
20260713060000_review_to_org_trigger.sql Auto-propagate worker reviews to organization_reviews

Key Files

File Purpose
app/organization/home/page.tsx Org dashboard with stats, recent orders, gallery, workers
app/organization/orders/page.tsx Org orders page
app/organization/workers/page.tsx Workers management (add, search, portfolio)
app/organization/layout.tsx Org bottom nav (6 tabs)
app/client/organization/[id]/ClientPage.tsx Client-facing org profile
app/client/orders/page.tsx Client orders (shows org name + worker)
app/client/order/invoice/page.tsx Invoice (org branding for org bookings)
app/client/rate-review/[bookingId]/ClientPage.tsx Rating page (org or worker)
components/ui/orders/OrderCard.tsx Order card with worker assignment UI
hooks/useOrganization.ts Org CRUD, workers, portfolio, plan limits
hooks/useClientBooking.ts Booking creation (supports organization_id)
hooks/useOrders.ts Fetches both direct and org bookings
lib/supabase/booking-payments.ts Payment processing (org owner wallet)
lib/supabase/schema.sql Database schema
components/shared/CraftsmanCard.tsx Card component (shows orgs + workers)