### Install Development Tools (Bash)
Source: https://github.com/masteralan360/asaas/blob/main/docs/DEPLOYMENT.md
Installs Node.js, Rust, and the Tauri CLI required for development. Also lists platform-specific build tools for Windows, macOS, and Linux.
```bash
# Node.js 18+
node --version
# Rust (for Tauri)
rustc --version
# Tauri CLI
cargo install tauri-cli
# Platform-specific
# Windows: Visual Studio Build Tools
# macOS: Xcode Command Line Tools
# Linux: webkit2gtk, libayatana-appindicator
```
--------------------------------
### Clone Repository and Install Dependencies (Bash)
Source: https://github.com/masteralan360/asaas/blob/main/docs/DEPLOYMENT.md
Clones the Asaas repository from GitHub and installs project dependencies using npm.
```bash
git clone https://github.com/masteralan360/Asaas.git
cd Asaas
npm install
```
--------------------------------
### Start Development Servers (Bash)
Source: https://github.com/masteralan360/asaas/blob/main/docs/DEPLOYMENT.md
Commands to start development servers for web and desktop applications. The web server runs on port 5173, while the Tauri command opens a native window with hot reloading.
```bash
# Web Development
npm run dev
# Opens http://localhost:5173
# Desktop Development (Tauri)
npm run tauri dev
# Opens native window with hot reload
```
--------------------------------
### Build for Production (Bash)
Source: https://github.com/masteralan360/asaas/blob/main/docs/DEPLOYMENT.md
Commands to build the application for different deployment targets: web (output to dist/), desktop (output to src-tauri/target/release/bundle/), and Android.
```bash
# Web Build
npm run build
# Output: dist/
# Desktop Build (Windows, macOS, Linux)
npm run tauri build
# Output: src-tauri/target/release/bundle/
# Android Build (Debug APK)
npm run android:build
# Android Build (Release AAB)
npm run android:build:release
```
--------------------------------
### Install Project Dependencies (Bash)
Source: https://github.com/masteralan360/asaas/blob/main/README.md
This snippet outlines the steps to clone the Asaas repository, navigate into the project directory, and install all necessary Node.js dependencies. It also includes instructions for copying and configuring the environment file.
```bash
git clone https://github.com/masteralan360/Asaas.git
cd Asaas
npm install
cp .env.example .env
# Edit .env with your Supabase credentials
```
--------------------------------
### Vercel Build Settings
Source: https://github.com/masteralan360/asaas/blob/main/docs/DEPLOYMENT.md
Configuration for deploying the Asaas web application to Vercel. Specifies the build command, output directory, and install command.
```text
Build Command: npm run build
Output Directory: dist
Install Command: npm install
```
--------------------------------
### Install PDF Generation and Viewer Dependencies
Source: https://github.com/masteralan360/asaas/blob/main/docs/PLAN-invoice-pdf-storage.md
Installs necessary npm packages for generating PDFs using React Native and for viewing PDFs within the application.
```bash
npm install @react-pdf/renderer @pdf-viewer/react
```
--------------------------------
### Configure Environment Variables (.env)
Source: https://github.com/masteralan360/asaas/blob/main/docs/DEPLOYMENT.md
Sets up environment variables for Supabase configuration and an optional API proxy for exchange rates. These are crucial for connecting to backend services.
```env
# Supabase Configuration
VITE_SUPABASE_URL=https://your-project.supabase.co
VITE_SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
# Optional: API Proxy for exchange rates (web only)
VITE_API_PROXY_URL=https://your-proxy.vercel.app/api
```
--------------------------------
### Example Usage of Data Access Hooks (TypeScript)
Source: https://github.com/masteralan360/asaas/blob/main/docs/DATABASE.md
Demonstrates how to use the data access hooks within a React component. It shows fetching products, handling creation, and updating the UI.
```typescript
function ProductList() {
const { user } = useAuth()
const products = useProducts(user?.workspaceId)
const handleCreate = async (data: ProductFormData) => {
await createProduct(user.workspaceId, data)
// Local update is immediate, sync happens in background
}
return (
{products.map(p => - {p.name}
)}
)
}
```
--------------------------------
### Example Registry Entry for Notification Popups
Source: https://github.com/masteralan360/asaas/blob/main/docs/NOTIFICATION_POPUP_SYSTEM.md
This example demonstrates how to register a new modal component within the POPUP_REGISTRY. It includes the component, a unique ID, and rules for its activation, such as subject matching and enablement.
```typescript
{
id: 'roadmap-v1',
component: RoadmapModal,
rules: {
enabled: true,
subjectMatch: "New Roadmap",
contentMatch: "Roadmap",
preventOutsideClose: true // UI-specific flag
}
}
```
--------------------------------
### Example File Structure (Project)
Source: https://github.com/masteralan360/asaas/blob/main/docs/PLAN-suppliers-customers-orders.md
Illustrates the project's file structure for the Asaas ERP system, highlighting new and modified files for local database, UI components, synchronization logic, and application entry points.
```bash
src/
├── local-db/
│ ├── models.ts [MODIFY] Add Supplier, update Customer/Order interfaces
│ ├── database.ts [MODIFY] Add suppliers, purchaseOrders tables
│ └── hooks.ts [MODIFY] Add supplier/order hooks
│
├── ui/
│ ├── pages/
│ │ ├── Suppliers.tsx [NEW] Supplier management page
│ │ ├── Customers.tsx [NEW] Customer management page
│ │ ├── Orders.tsx [NEW] Unified orders page (purchase + sales)
│ │ └── index.ts [MODIFY] Export new pages
│ │
│ └── components/
│ ├── orders/ [NEW] Order-related components
│ │ ├── OrderForm.tsx
│ │ ├── OrderDetails.tsx
│ │ ├── OrderStatusBadge.tsx
│ │ └── CurrencyConverter.tsx
│ │
│ ├── suppliers/ [NEW] Supplier components
│ │ ├── SupplierForm.tsx
│ │ └── SupplierCard.tsx
│ │
│ └── customers/ [NEW] Customer components
│ ├── CustomerForm.tsx
│ └── CustomerCard.tsx
│
├── sync/
│ └── syncEngine.ts [MODIFY] Add suppliers, purchaseOrders sync
│
└── App.ts [MODIFY] Add routes
```
--------------------------------
### Run Supabase Migrations (SQL/Bash)
Source: https://github.com/masteralan360/asaas/blob/main/docs/DEPLOYMENT.md
SQL commands and bash scripts to apply database schema, RLS policies, and feature migrations to a Supabase project. Alternatively, the Supabase CLI can be used for pushing migrations.
```sql
-- Core schema
psql < supabase/schema.sql
-- RLS policies
psql < supabase/rls-policies.sql
-- Feature additions
psql < supabase/categories_migration.sql
psql < supabase/multi-currency-migration.sql
psql < supabase/payment-method-migration.sql
-- etc.
```
```bash
supabase db push
```
--------------------------------
### Run Development Server (Bash)
Source: https://github.com/masteralan360/asaas/blob/main/README.md
Commands to start the development server for different platforms. Includes options for web, desktop (Tauri), and Android development environments. These commands are essential for iterative development and testing.
```bash
# Web development
npm run dev
# Desktop (Tauri)
npm run tauri dev
# Android
npm run android:dev
```
--------------------------------
### Create Supabase Storage Bucket (SQL)
Source: https://github.com/masteralan360/asaas/blob/main/docs/DEPLOYMENT.md
SQL statement to create a private storage bucket named 'p2p-sync' within Supabase for file synchronization.
```sql
INSERT INTO storage.buckets (id, name, public)
VALUES ('p2p-sync', 'p2p-sync', false);
```
--------------------------------
### Local Database Setup with Dexie.js
Source: https://context7.com/masteralan360/asaas/llms.txt
Sets up the local IndexedDB database using Dexie.js for offline-first data access. It defines tables for products, categories, sales, invoices, and workspaces, including sync status and deletion flags.
```typescript
import Dexie, { type EntityTable } from 'dexie'
import type { Product, Category, Sale, SaleItem, Invoice, Workspace } from './models'
class AsaasDatabase extends Dexie {
products!: EntityTable
categories!: EntityTable
sales!: EntityTable
sale_items!: EntityTable
invoices!: EntityTable
workspaces!: EntityTable
offline_mutations!: EntityTable
constructor() {
super('AsaasDatabase')
this.version(29).stores({
products: 'id, sku, name, categoryId, workspaceId, syncStatus, isDeleted',
categories: 'id, name, workspaceId, syncStatus, isDeleted',
sales: 'id, cashierId, workspaceId, settlementCurrency, syncStatus, createdAt',
sale_items: 'id, saleId, productId',
invoices: 'id, invoiceid, workspaceId, syncStatus, origin, sequenceId',
workspaces: 'id, name, code, syncStatus, isDeleted',
offline_mutations: 'id, workspaceId, entityType, entityId, status, [entityType+entityId+status]'
})
}
}
export const db = new AsaasDatabase()
```
--------------------------------
### Supabase Migration File (SQL)
Source: https://github.com/masteralan360/asaas/blob/main/docs/PLAN-suppliers-customers-orders.md
Example SQL file name for Supabase database migrations, indicating the creation of new tables for suppliers, customers, and orders.
```sql
supabase/
├── migrations/
│ └── YYYYMMDD_suppliers_customers_orders.sql [NEW]
```
--------------------------------
### Dockerfile for Asaas Web App
Source: https://github.com/masteralan360/asaas/blob/main/docs/DEPLOYMENT.md
A Dockerfile to containerize the Asaas web application using a multi-stage build. It first builds the application with Node.js and then serves the static files using Nginx.
```dockerfile
FROM node:18-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM nginx:alpine
COPY --from=builder /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/nginx.conf
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
```
--------------------------------
### Supabase Realtime: Subscribe to Database Changes (TypeScript)
Source: https://context7.com/masteralan360/asaas/llms.txt
Examples of subscribing to PostgreSQL database changes using Supabase Realtime. Includes subscribing to inserts in a 'sync_queue' table and any changes in a 'products' table, both filtered by workspace ID.
```typescript
const subscribeSyncQueue = (workspaceId: string, onNewFile: (file: any) => void) => {
const channel = supabase
.channel('sync-queue')
.on(
'postgres_changes',
{
event: 'INSERT',
schema: 'public',
table: 'sync_queue',
filter: `workspace_id=eq.${workspaceId}`
},
(payload) => {
console.log('New file to sync:', payload.new)
onNewFile(payload.new)
}
)
.subscribe()
return () => supabase.removeChannel(channel)
}
const subscribeProducts = (workspaceId: string, onUpdate: (product: any) => void) => {
return supabase
.channel('products-live')
.on('postgres_changes', {
event: '*',
schema: 'public',
table: 'products',
filter: `workspace_id=eq.${workspaceId}`
}, (payload) => onUpdate(payload.new))
.subscribe()
}
```
--------------------------------
### Nginx Configuration for Asaas Web App
Source: https://github.com/masteralan360/asaas/blob/main/docs/DEPLOYMENT.md
Nginx configuration file to serve the static files built by the Asaas application. It directs all requests to the root directory and handles routing for single-page applications.
```nginx
server {
listen 80;
location / {
root /usr/share/nginx/html;
try_files $uri $uri/ /index.html;
}
}
```
--------------------------------
### Generate Tauri Signing Keys and Set Environment Variable (Bash)
Source: https://github.com/masteralan360/asaas/blob/main/docs/DEPLOYMENT.md
Commands to generate signing keys for Tauri applications and set the private key as an environment variable for build processes. This is essential for code signing on macOS and Windows.
```bash
tauri signer generate -w ~/.tauri/asaas.key
export TAURI_SIGNING_PRIVATE_KEY=$(cat ~/.tauri/asaas.key)
```
--------------------------------
### Supabase Storage: Upload, Download, and Get Public URL (TypeScript)
Source: https://context7.com/masteralan360/asaas/llms.txt
Functions for uploading files to Supabase Storage, downloading them, and retrieving their public URLs. Requires Supabase client initialization and a 'p2p-sync' bucket.
```typescript
const uploadFile = async (workspaceId: string, fileName: string, file: File) => {
const { data, error } = await supabase.storage
.from('p2p-sync')
.upload(`${workspaceId}/${fileName}`, file)
if (error) throw error
return data.path
}
const downloadFile = async (storagePath: string) => {
const { data, error } = await supabase.storage
.from('p2p-sync')
.download(storagePath)
if (error) throw error
return data // Blob
}
const getPublicUrl = (storagePath: string) => {
const { data } = supabase.storage
.from('p2p-sync')
.getPublicUrl(storagePath)
return data.publicUrl
}
```
--------------------------------
### Get Cashier Performance RPC Function
Source: https://github.com/masteralan360/asaas/blob/main/docs/API_REFERENCE.md
Retrieves performance metrics for each cashier within a specified workspace and date range using the `get_cashier_performance` RPC function. Requires workspace ID, start date, and end date.
```typescript
const { data } = await supabase.rpc('get_cashier_performance', {
p_workspace_id: workspaceId,
p_start_date: '2024-01-01',
p_end_date: '2024-12-31'
})
```
--------------------------------
### Get Sales Summary and Cashier Performance (TypeScript)
Source: https://context7.com/masteralan360/asaas/llms.txt
Fetches aggregated sales data for revenue analytics and cashier performance metrics. It requires workspace ID, start date, and end date as input. The functions interact with Supabase RPC endpoints.
```typescript
const getSalesSummary = async (workspaceId: string, startDate: string, endDate: string) => {
const { data, error } = await supabase.rpc('get_sales_summary', {
p_workspace_id: workspaceId,
p_start_date: startDate,
p_end_date: endDate
})
if (error) throw error
// Returns: { total_sales: number, total_revenue: number, total_cost: number, net_profit: number, sales_count: number }
return data
}
// Get per-cashier performance metrics
const getCashierPerformance = async (workspaceId: string, startDate: string, endDate: string) => {
const { data, error } = await supabase.rpc('get_cashier_performance', {
p_workspace_id: workspaceId,
p_start_date: startDate,
p_end_date: endDate
})
if (error) throw error
// Returns array of: { cashier_id, cashier_name, total_sales, total_revenue, average_order_value }
return data
}
// Example: Generate monthly report
const report = await getSalesSummary(workspaceId, '2024-01-01', '2024-01-31')
console.log(`Revenue: $${report.total_revenue}, Profit: $${report.net_profit}, Margin: ${((report.net_profit / report.total_revenue) * 100).toFixed(1)}%`)
```
--------------------------------
### Get Sales Summary RPC Function
Source: https://github.com/masteralan360/asaas/blob/main/docs/API_REFERENCE.md
Fetches aggregated sales data for reporting purposes using the `get_sales_summary` RPC function. Requires workspace ID and a date range (start and end dates). Returns total sales, revenue, cost, profit, and sales count.
```typescript
const { data } = await supabase.rpc('get_sales_summary', {
p_workspace_id: workspaceId,
p_start_date: '2024-01-01',
p_end_date: '2024-12-31'
})
```
--------------------------------
### Configure Tauri Auto-Updates (JSON)
Source: https://github.com/masteralan360/asaas/blob/main/docs/DEPLOYMENT.md
Configuration for the Tauri updater plugin, specifying active status, endpoints for update checks, dialog behavior, and a public key for signature verification.
```json
{
"plugins": {
"updater": {
"active": true,
"endpoints": [
"https://github.com/masteralan360/Asaas/releases/latest/download/latest.json"
],
"dialog": true,
"pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6..."
}
}
}
```
--------------------------------
### Build Production Application (Bash)
Source: https://github.com/masteralan360/asaas/blob/main/README.md
Instructions for creating production-ready builds of the Asaas application for web, desktop, and Android platforms. These commands compile and package the application for deployment.
```bash
# Web
npm run build
# Desktop
npm run tauri build
# Android
npm run android:build:release
```
--------------------------------
### Create Product (Direct Table Operation)
Source: https://github.com/masteralan360/asaas/blob/main/docs/API_REFERENCE.md
Inserts a new product into the 'products' table. Requires workspace ID, SKU, name, price, quantity, and currency.
```typescript
await supabase.from('products').insert({
workspace_id: workspaceId,
sku: 'PROD-001',
name: 'Widget',
price: 9.99,
quantity: 100,
currency: 'usd'
})
```
--------------------------------
### Get Workspace Members (Direct Table Operation)
Source: https://github.com/masteralan360/asaas/blob/main/docs/API_REFERENCE.md
Retrieves a list of members for a specific workspace from the 'profiles' table. Selects user ID, name, email, role, and profile URL.
```typescript
const { data } = await supabase
.from('profiles')
.select('id, name, email, role, profile_url')
.eq('workspace_id', workspaceId)
```
--------------------------------
### Product Management Hook (React/TypeScript)
Source: https://context7.com/masteralan360/asaas/llms.txt
Provides React hooks for managing products, including live data retrieval with Dexie.js and automatic synchronization with Supabase when online. It handles both reading and creating products, with offline support.
```typescript
import { useLiveQuery } from 'dexie-react-hooks'
import { db } from './database'
import { supabase } from '@/auth/supabase'
import { useNetworkStatus } from '@/hooks/useNetworkStatus'
// Read products with live updates
export function useProducts(workspaceId: string | undefined) {
const isOnline = useNetworkStatus()
const products = useLiveQuery(
() => workspaceId
? db.products.where('workspaceId').equals(workspaceId).and(p => !p.isDeleted).toArray()
: [],
[workspaceId]
)
// Background sync when online
useEffect(() => {
if (isOnline && workspaceId) {
supabase.from('products')
.select('*')
.eq('workspace_id', workspaceId)
.eq('is_deleted', false)
.then(({ data }) => {
if (data) {
db.transaction('rw', db.products, async () => {
for (const item of data) {
await db.products.put({ ...toCamelCase(item), syncStatus: 'synced' })
}
})
}
})
}
}, [isOnline, workspaceId])
return products ?? []
}
// Create product (works offline)
export async function createProduct(workspaceId: string, data: ProductFormData): Promise {
const now = new Date().toISOString()
const id = crypto.randomUUID()
const product: Product = {
...data,
id,
workspaceId,
createdAt: now,
updatedAt: now,
syncStatus: navigator.onLine ? 'synced' : 'pending',
version: 1,
isDeleted: false
}
if (navigator.onLine) {
const { error } = await supabase.from('products').insert(toSnakeCase(product))
if (error) throw error
await db.products.add(product)
} else {
await db.products.add(product)
await db.offline_mutations.add({
id: crypto.randomUUID(),
workspaceId,
entityType: 'products',
entityId: id,
operation: 'create',
payload: product,
createdAt: now,
status: 'pending'
})
}
return product
}
```
--------------------------------
### Initialize Supabase Client with Encrypted Storage (TypeScript)
Source: https://context7.com/masteralan360/asaas/llms.txt
Configures the Supabase client using environment variables for URL and anon key. It implements a custom storage adapter with AES-256 encryption for session persistence and automatic token refresh. Dependencies include '@supabase/supabase-js' and local encryption/decryption functions.
```typescript
import { createClient } from '@supabase/supabase-js'
import { decrypt, encrypt } from '@/lib/encryption'
// Custom storage adapter with AES-256 encryption
const EncryptedStorage = {
getItem: (key: string): string | null => {
const value = localStorage.getItem(key)
return value ? decrypt(value) : null
},
setItem: (key: string, value: string): void => {
localStorage.setItem(key, encrypt(value))
},
removeItem: (key: string): void => {
localStorage.removeItem(key)
}
}
const supabaseUrl = import.meta.env.VITE_SUPABASE_URL
const supabaseAnonKey = import.meta.env.VITE_SUPABASE_ANON_KEY
export const supabase = createClient(supabaseUrl, supabaseAnonKey, {
auth: {
persistSession: true,
autoRefreshToken: true,
detectSessionInUrl: true,
storage: EncryptedStorage
}
})
// Check if Supabase is properly configured
export const isSupabaseConfigured = Boolean(
supabaseUrl?.startsWith('https://') &&
supabaseAnonKey?.length > 50
)
```
--------------------------------
### Get Next Sequence ID RPC Function
Source: https://github.com/masteralan360/asaas/blob/main/docs/API_REFERENCE.md
Retrieves the next sequential invoice or sale number using the `get_next_sequence_id` RPC function. Requires the workspace ID and the type of document ('sale' or 'invoice').
```typescript
const { data } = await supabase.rpc('get_next_sequence_id', {
p_workspace_id: workspaceId,
p_type: 'sale' // or 'invoice'
})
```
--------------------------------
### Supabase Schema SQL for Row Level Security (SQL)
Source: https://github.com/masteralan360/asaas/blob/main/docs/DATABASE.md
Provides an example of a Row Level Security (RLS) policy in SQL for Supabase. This policy ensures that users can only access data belonging to their associated workspace.
```sql
-- Example policy
CREATE POLICY "Users can only view their workspace products"
ON products FOR SELECT
USING (
workspace_id = (
SELECT workspace_id FROM profiles WHERE id = auth.uid()
)
);
```
--------------------------------
### Run Verification Scripts
Source: https://github.com/masteralan360/asaas/blob/main/docs/PLAN-suppliers-customers-orders.md
Provides bash commands to execute the full verification script or individual linting, type checking, and build commands. It also includes a command for a security scan.
```bash
# Full verification
python .agent/scripts/verify_all.py . --url http://localhost:5173
# Or individually:
npm run lint && npx tsc --noEmit
npm run build
python .agent/skills/vulnerability-scanner/scripts/security_scan.py .
```
--------------------------------
### Manage Workspaces via Supabase RPC (TypeScript)
Source: https://context7.com/masteralan360/asaas/llms.txt
Provides functions to interact with Supabase RPC for workspace management. This includes creating new workspaces, joining existing ones using a code and passkey, and locking/unlocking workspaces. It requires the initialized Supabase client and specific RPC functions defined on the Supabase server.
```typescript
// Create a new workspace
const createWorkspace = async (name: string, adminPasskey: string, memberPasskey: string) => {
const { data, error } = await supabase.rpc('create_workspace', {
p_name: name,
p_admin_passkey: adminPasskey,
p_member_passkey: memberPasskey
})
if (error) throw error
// Returns: { workspace_id: string, code: string }
console.log('Workspace created:', data.workspace_id, 'Code:', data.code)
return data
}
// Join an existing workspace
const joinWorkspace = async (workspaceCode: string, passkey: string, userId: string) => {
const { data, error } = await supabase.rpc('join_workspace', {
p_workspace_code: workspaceCode,
p_passkey: passkey,
p_user_id: userId
})
if (error) throw error
// Returns: { success: boolean, role: 'admin' | 'staff' }
return data
}
// Lock/unlock workspace (admin only)
const lockWorkspace = async (workspaceId: string, locked: boolean) => {
const { error } = await supabase.rpc('lock_workspace', {
p_workspace_id: workspaceId,
p_locked: locked
})
if (error) throw error
}
```
--------------------------------
### Data Access Hooks Pattern (TypeScript)
Source: https://github.com/masteralan360/asaas/blob/main/docs/DATABASE.md
Illustrates the pattern for data access hooks in TypeScript, including functions for reading data with live updates, fetching single items, and performing CRUD operations.
```typescript
// Read with live updates
useProducts(workspaceId) // Returns Product[]
// Single item
useProduct(id) // Returns Product | undefined
// CRUD operations
createProduct(workspaceId, data)
updateProduct(id, data)
deleteProduct(id) // Soft delete
```
--------------------------------
### Workspace Context Provider in TypeScript
Source: https://context7.com/masteralan360/asaas/llms.txt
Provides React context for workspace feature flags, settings, and real-time updates using Supabase. It fetches initial features and subscribes to real-time updates for the 'workspaces' table. Dependencies include React, Supabase, and a custom AuthContext.
```typescript
import { createContext, useContext, useEffect, useState } from 'react'
import { supabase } from '@/auth/supabase'
import { useAuth } from '@/auth/AuthContext'
interface WorkspaceFeatures {
allow_pos: boolean
allow_invoices: boolean
allow_whatsapp: boolean
default_currency: 'usd' | 'eur' | 'iqd' | 'try'
max_discount_percent: number
locked_workspace: boolean
}
const defaultFeatures: WorkspaceFeatures = {
allow_pos: false,
allow_invoices: false,
allow_whatsapp: false,
default_currency: 'iqd',
max_discount_percent: 0,
locked_workspace: false
}
const WorkspaceContext = createContext<{ features: WorkspaceFeatures; hasFeature: (feature: keyof WorkspaceFeatures) => boolean } | undefined>(undefined)
export function WorkspaceProvider({ children }: { children: React.ReactNode }) {
const { user } = useAuth()
const [features, setFeatures] = useState(defaultFeatures)
useEffect(() => {
if (!user?.workspaceId) return
// Initial fetch
supabase.rpc('get_workspace_features').single().then(({ data }) => {
if (data) setFeatures(data)
})
// Real-time updates
const channel = supabase
.channel(`workspace-${user.workspaceId}`)
.on('postgres_changes', {
event: 'UPDATE',
schema: 'public',
table: 'workspaces',
filter: `id=eq.${user.workspaceId}`
}, (payload) => {
setFeatures(prev => ({ ...prev, ...payload.new }))
})
.subscribe()
return () => { supabase.removeChannel(channel) }
}, [user?.workspaceId])
const hasFeature = (feature: keyof WorkspaceFeatures) => features[feature] === true
return (
{children}
)
}
export const useWorkspace = () => {
const context = useContext(WorkspaceContext)
if (context === undefined) {
throw new Error('useWorkspace must be used within a WorkspaceProvider')
}
return context
}
```
--------------------------------
### Generate Invoice PDF using @react-pdf/renderer
Source: https://github.com/masteralan360/asaas/blob/main/docs/PLAN-invoice-pdf-storage.md
Function signature for generating a PDF blob from universal invoice data using the '@react-pdf/renderer' library.
```typescript
generateInvoicePdf(data: UniversalInvoice, format: 'a4' | 'receipt'): Promise
```
--------------------------------
### Create Point of Sale Sale with Items (TypeScript)
Source: https://context7.com/masteralan360/asaas/llms.txt
Atomically creates a sales transaction including line items, exchange rate snapshot, and inventory tracking using a Supabase RPC function. It takes workspace and cashier IDs, cart items with detailed pricing and currency information, payment method, and exchange rates as input. The function returns the sale ID and its sequential invoice number.
```typescript
// Create a complete sale transaction
const completeSale = async (
workspaceId: string,
cashierId: string,
cartItems: CartItem[],
paymentMethod: 'cash' | 'fib' | 'qicard' | 'zaincash' | 'fastpay',
exchangeRates: ExchangeRates
) => {
const { data, error } = await supabase.rpc('create_sale_with_items', {
p_workspace_id: workspaceId,
p_cashier_id: cashierId,
p_items: cartItems.map(item => ({
product_id: item.productId,
quantity: item.quantity,
unit_price: item.unitPrice,
cost_price: item.costPrice,
original_currency: item.currency,
converted_unit_price: item.convertedPrice,
inventory_snapshot: item.currentStock
})),
p_total_amount: cartItems.reduce((sum, i) => sum + i.convertedPrice * i.quantity, 0),
p_payment_method: paymentMethod,
p_exchange_rate: exchangeRates.usd_iqd,
p_exchange_source: exchangeRates.source,
p_settlement_currency: 'iqd'
})
if (error) throw error
// Returns: { sale_id: string, sequence_id: number }
// sequence_id is the server-assigned sequential invoice number
return data
}
// Example usage with cart
const cart = [
{ productId: 'abc123', quantity: 2, unitPrice: 25.00, costPrice: 15.00, currency: 'usd', convertedPrice: 37000, currentStock: 50 },
{ productId: 'def456', quantity: 1, unitPrice: 10.00, costPrice: 5.00, currency: 'usd', convertedPrice: 14800, currentStock: 100 }
]
const result = await completeSale(workspaceId, userId, cart, 'cash', { usd_iqd: 1480, source: 'xeiqd' })
console.log(`Sale #${result.sequence_id} created successfully`)
```
--------------------------------
### WhatsApp Webview Manager Logic (TypeScript)
Source: https://github.com/masteralan360/asaas/blob/main/docs/PLAN-hr-whatsapp-hotkey.md
This snippet details the logic for managing WhatsApp webviews. It includes a new `openChat` method to sanitize phone numbers, construct the WhatsApp chat URL, and handle webview creation or redirection. It also updates the `createWebview` method to manage pending URLs.
```typescript
class WhatsappWebviewManager {
pendingUrl: string | null = null;
openChat(phone: string): void {
const sanitizedPhone = phone.replace(/[^0-9]/g, '');
const url = `https://web.whatsapp.com/send?phone=${sanitizedPhone}&text&type=phone_number&app_absent=0`;
if (this.webviewExists()) {
this.executeJs(`window.location.href = '${url}'`);
this.show();
} else {
this.pendingUrl = url;
}
}
createWebview(): void {
if (this.pendingUrl) {
super.loadURL(this.pendingUrl);
this.pendingUrl = null;
} else {
super.loadURL('https://web.whatsapp.com');
}
}
// Placeholder methods for actual webview interaction
private webviewExists(): boolean {
// Implementation to check if webview exists
return false;
}
private executeJs(script: string): void {
// Implementation to execute JavaScript in webview
console.log(`Executing JS: ${script}`);
}
private show(): void {
// Implementation to show webview
console.log('Showing webview');
}
}
```