/// API-related constants for the Worker app /// /// This file contains all API endpoints, timeouts, and network-related configurations. /// Base URLs should be configured per environment (dev, staging, production). class ApiConstants { // Private constructor to prevent instantiation ApiConstants._(); // ============================================================================ // Base URLs // ============================================================================ /// Base URL for all APIs (Frappe/ERPNext) static const String baseUrl = 'https://land.dbiz.com'; /// Full API base URL (no version prefix, using Frappe endpoints) static String get apiBaseUrl => baseUrl; // ============================================================================ // Timeout Configurations // ============================================================================ /// Connection timeout in milliseconds (30 seconds) static const Duration connectionTimeout = Duration(milliseconds: 30000); /// Receive timeout in milliseconds (30 seconds) static const Duration receiveTimeout = Duration(milliseconds: 30000); /// Send timeout in milliseconds (30 seconds) static const Duration sendTimeout = Duration(milliseconds: 30000); // ============================================================================ // Retry Configurations // ============================================================================ /// Maximum number of retry attempts for failed requests static const int maxRetryAttempts = 3; /// Initial retry delay in milliseconds static const Duration initialRetryDelay = Duration(milliseconds: 1000); /// Maximum retry delay in milliseconds static const Duration maxRetryDelay = Duration(milliseconds: 5000); /// Retry delay multiplier for exponential backoff static const double retryDelayMultiplier = 2.0; // ============================================================================ // Cache Configurations // ============================================================================ /// Default cache duration (1 hour) static const Duration defaultCacheDuration = Duration(hours: 1); /// Products cache duration (24 hours) static const Duration productsCacheDuration = Duration(hours: 24); /// Profile cache duration (1 hour) static const Duration profileCacheDuration = Duration(hours: 1); /// Categories cache duration (48 hours) static const Duration categoriesCacheDuration = Duration(hours: 48); /// Maximum cache size in bytes (50 MB) static const int maxCacheSize = 50 * 1024 * 1024; // ============================================================================ // Request Headers // ============================================================================ /// Content-Type header for JSON requests static const String contentTypeJson = 'application/json'; /// Accept header for JSON responses static const String acceptJson = 'application/json'; /// Accept-Language header for Vietnamese static const String acceptLanguageVi = 'vi'; /// Accept-Language header for English static const String acceptLanguageEn = 'en'; // ============================================================================ // Authentication Endpoints // ============================================================================ /// Request OTP for phone number login /// POST /auth/request-otp /// Body: { "phone": "+84912345678" } static const String requestOtp = '/auth/request-otp'; /// Verify OTP code /// POST /auth/verify-otp /// Body: { "phone": "+84912345678", "otp": "123456" } static const String verifyOtp = '/auth/verify-otp'; /// Register new user /// POST /auth/register /// Body: { "name": "...", "phone": "...", "email": "...", "userType": "..." } static const String register = '/auth/register'; /// Refresh access token /// POST /auth/refresh-token /// Headers: { "Authorization": "Bearer {refreshToken}" } static const String refreshToken = '/auth/refresh-token'; /// Logout user /// POST /auth/logout static const String logout = '/auth/logout'; /// Get current user profile /// GET /auth/me static const String getCurrentUser = '/auth/me'; // ============================================================================ // Loyalty Program Endpoints // ============================================================================ /// Get loyalty points and tier information /// GET /loyalty/points static const String getLoyaltyPoints = '/loyalty/points'; /// Get loyalty points transaction history /// GET /loyalty/transactions?page={page}&limit={limit} static const String getPointsHistory = '/loyalty/transactions'; /// Get available rewards for redemption /// GET /loyalty/rewards?category={category} static const String getRewards = '/loyalty/rewards'; /// Redeem a reward /// POST /loyalty/rewards/{rewardId}/redeem static const String redeemReward = '/loyalty/rewards'; /// Get user's redeemed gifts /// GET /loyalty/gifts?status={active|used|expired} static const String getGifts = '/loyalty/gifts'; /// Get referral information /// GET /loyalty/referral static const String getReferralInfo = '/loyalty/referral'; /// Share referral link /// POST /loyalty/referral/share /// Body: { "method": "whatsapp|telegram|sms" } static const String shareReferral = '/loyalty/referral/share'; // ============================================================================ // Favorites/Wishlist Endpoints (Frappe ERPNext) // ============================================================================ /// Get favorite/wishlist items for current user /// POST /api/method/building_material.building_material.api.item_wishlist.get_list /// Body: { "limit_start": 0, "limit_page_length": 0 } static const String getFavorites = '/building_material.building_material.api.item_wishlist.get_list'; /// Add item to wishlist /// POST /api/method/building_material.building_material.api.item_wishlist.add_to_wishlist /// Body: { "item_id": "GIB20 G04" } static const String addToFavorites = '/building_material.building_material.api.item_wishlist.add_to_wishlist'; /// Remove item from wishlist /// POST /api/method/building_material.building_material.api.item_wishlist.remove_from_wishlist /// Body: { "item_id": "GIB20 G04" } static const String removeFromFavorites = '/building_material.building_material.api.item_wishlist.remove_from_wishlist'; // ============================================================================ // Product Endpoints // ============================================================================ /// Get all products with pagination /// GET /products?page={page}&limit={limit}&category={categoryId} static const String getProducts = '/products'; /// Get product details by ID /// GET /products/{productId} static const String getProductDetails = '/products'; /// Search products /// GET /products/search?q={query}&page={page}&limit={limit} static const String searchProducts = '/products/search'; /// Get product categories /// GET /categories static const String getCategories = '/categories'; /// Get products by category /// GET /categories/{categoryId}/products static const String getProductsByCategory = '/categories'; // ============================================================================ // Cart Endpoints (Frappe ERPNext) // ============================================================================ /// Add items to cart /// POST /api/method/building_material.building_material.api.user_cart.add_to_cart /// Body: { "items": [{ "item_id": "...", "amount": 0, "quantity": 0 }] } static const String addToCart = '/building_material.building_material.api.user_cart.add_to_cart'; /// Remove items from cart /// POST /api/method/building_material.building_material.api.user_cart.remove_from_cart /// Body: { "item_ids": ["item_id1", "item_id2"] } static const String removeFromCart = '/building_material.building_material.api.user_cart.remove_from_cart'; /// Get user's cart items /// POST /api/method/building_material.building_material.api.user_cart.get_user_cart /// Body: { "limit_start": 0, "limit_page_length": 0 } static const String getUserCart = '/building_material.building_material.api.user_cart.get_user_cart'; // ============================================================================ // Order Endpoints // ============================================================================ /// Get order status list (requires sid and csrf_token) /// POST /api/method/building_material.building_material.api.sales_order.get_order_status_list /// Returns: { "message": [{ "status": "...", "label": "...", "color": "...", "index": 0 }] } static const String getOrderStatusList = '/building_material.building_material.api.sales_order.get_order_status_list'; /// Create new order (requires sid and csrf_token) /// POST /api/method/building_material.building_material.api.sales_order.save /// Body: { "transaction_date": "...", "delivery_date": "...", "items": [...], ... } static const String createOrder = '/building_material.building_material.api.sales_order.save'; /// Generate QR code for payment (requires sid and csrf_token) /// POST /api/method/building_material.building_material.api.v1.qrcode.generate /// Body: { "order_id": "SAL-ORD-2025-00048" } /// Returns: { "message": { "qr_code": "...", "amount": null, "transaction_id": "...", "bank_info": {...} } } static const String generateQrCode = '/building_material.building_material.api.v1.qrcode.generate'; /// Upload file (bill/invoice/attachment) (requires sid and csrf_token) /// POST /api/method/upload_file /// Form-data: { "file": File, "is_private": "1", "folder": "Home/Attachments", "doctype": "Sales Order", "docname": "SAL-ORD-2025-00058-1", "optimize": "true" } /// Returns: { "message": { "file_url": "...", "file_name": "...", ... } } static const String uploadFile = '/upload_file'; /// Get list of orders (requires sid and csrf_token) /// POST /api/method/building_material.building_material.api.sales_order.get_list /// Body: { "limit_start": 0, "limit_page_length": 0 } /// Returns: { "message": [...] } static const String getOrdersList = '/building_material.building_material.api.sales_order.get_list'; /// Get order details (requires sid and csrf_token) /// POST /api/method/building_material.building_material.api.sales_order.get_detail /// Body: { "name": "SAL-ORD-2025-00058-1" } /// Returns: { "message": {...} } static const String getOrderDetail = '/building_material.building_material.api.sales_order.get_detail'; /// Update order address (requires sid and csrf_token) /// POST /api/method/building_material.building_material.api.sales_order.update /// Body: { "name": "SAL-ORD-2025-00053", "shipping_address_name": "...", "customer_address": "..." } static const String updateOrder = '/building_material.building_material.api.sales_order.update'; /// Cancel order (requires sid and csrf_token) /// POST /api/method/building_material.building_material.api.sales_order.cancel /// Body: { "name": "SAL-ORD-2025-00054" } static const String cancelOrder = '/building_material.building_material.api.sales_order.cancel'; /// Get user's orders (legacy endpoint - may be deprecated) /// GET /orders?status={status}&page={page}&limit={limit} static const String getOrders = '/orders'; /// Get order details by ID /// GET /orders/{orderId} static const String getOrderDetails = '/orders'; /// Get payment transactions /// GET /payments?page={page}&limit={limit} static const String getPayments = '/payments'; /// Get payment details /// GET /payments/{paymentId} static const String getPaymentDetails = '/payments'; // ============================================================================ // Project Endpoints (Frappe ERPNext) // ============================================================================ /// Get project status list (requires sid and csrf_token) /// POST /api/method/building_material.building_material.api.project.get_project_status_list /// Body: { "limit_start": 0, "limit_page_length": 0 } /// Returns: { "message": [{ "status": "...", "label": "...", "color": "...", "index": 0 }] } static const String getProjectStatusList = '/building_material.building_material.api.project.get_project_status_list'; /// Get list of project submissions (requires sid and csrf_token) /// POST /api/method/building_material.building_material.api.project.get_list /// Body: { "limit_start": 0, "limit_page_length": 0 } /// Returns: { "message": [{ "name": "...", "designed_area": "...", "design_area": 0, ... }] } static const String getProjectList = '/building_material.building_material.api.project.get_list'; /// Save (create/update) project submission /// POST /api/method/building_material.building_material.api.project.save /// Body: { /// "name": "...", // optional for new, required for update /// "designed_area": "Project Name", /// "address_of_project": "...", /// "project_owner": "...", /// "design_firm": "...", /// "contruction_contractor": "...", /// "design_area": 350.5, /// "products_included_in_the_design": "...", /// "project_progress": "progress_id", // from ProjectProgress.id /// "expected_commencement_date": "2026-01-15", /// "description": "...", /// "request_date": "2025-11-26 09:30:00" /// } static const String saveProject = '/building_material.building_material.api.project.save'; /// Get project detail (requires sid and csrf_token) /// POST /api/method/building_material.building_material.api.project.get_detail /// Body: { "name": "#DA00011" } /// Returns: Full project detail with all fields static const String getProjectDetail = '/building_material.building_material.api.project.get_detail'; /// Delete project file/attachment (requires sid and csrf_token) /// POST /api/method/frappe.desk.form.utils.remove_attach /// Form-data: { "fid": "file_id", "dt": "Architectural Project", "dn": "project_name" } static const String removeProjectFile = '/frappe.desk.form.utils.remove_attach'; // ============================================================================ // Sample Project / Model House Endpoints (Frappe ERPNext) // ============================================================================ /// Get list of sample/model house projects (requires sid and csrf_token) /// POST /api/method/building_material.building_material.api.sample_project.get_list /// Body: { "limit_start": 0, "limit_page_length": 0 } /// Returns: { "message": [{ "name": "...", "project_name": "...", "notes": "...", "link": "...", "thumbnail": "..." }] } static const String getSampleProjectList = '/building_material.building_material.api.sample_project.get_list'; /// Get detail of a sample/model house project (requires sid and csrf_token) /// POST /api/method/building_material.building_material.api.sample_project.get_detail /// Body: { "name": "PROJ-0001" } /// Returns: { "message": { "name": "...", "project_name": "...", "notes": "...", "link": "...", "thumbnail": "...", "files_list": [...] } } static const String getSampleProjectDetail = '/building_material.building_material.api.sample_project.get_detail'; // ============================================================================ // Design Request Endpoints (Frappe ERPNext) // ============================================================================ /// Get list of design requests (requires sid and csrf_token) /// POST /api/method/building_material.building_material.api.design_request.get_list /// Body: { "limit_start": 0, "limit_page_length": 0 } /// Returns: { "message": [{ "name": "...", "subject": "...", "description": "...", "dateline": "...", "status": "...", "status_color": "..." }] } static const String getDesignRequestList = '/building_material.building_material.api.design_request.get_list'; /// Get detail of a design request (requires sid and csrf_token) /// POST /api/method/building_material.building_material.api.design_request.get_detail /// Body: { "name": "ISS-2025-00005" } /// Returns: { "message": { "name": "...", "subject": "...", "description": "...", "dateline": "...", "status": "...", "status_color": "...", "files_list": [...] } } static const String getDesignRequestDetail = '/building_material.building_material.api.design_request.get_detail'; /// Create new project (legacy endpoint - may be deprecated) /// POST /projects static const String createProject = '/projects'; /// Get user's projects (legacy endpoint - may be deprecated) /// GET /projects?status={status}&page={page}&limit={limit} static const String getProjects = '/projects'; /// Get project details by ID (legacy endpoint - may be deprecated) /// GET /projects/{projectId} static const String getProjectDetails = '/projects'; /// Update project (legacy endpoint - may be deprecated) /// PUT /projects/{projectId} static const String updateProject = '/projects'; /// Update project progress (legacy endpoint - may be deprecated) /// PATCH /projects/{projectId}/progress /// Body: { "progress": 75 } static const String updateProjectProgress = '/projects'; /// Delete project (legacy endpoint - may be deprecated) /// DELETE /projects/{projectId} static const String deleteProject = '/projects'; // ============================================================================ // Quote Endpoints // ============================================================================ /// Create new quote /// POST /quotes static const String createQuote = '/quotes'; /// Get user's quotes /// GET /quotes?status={status}&page={page}&limit={limit} static const String getQuotes = '/quotes'; /// Get quote details by ID /// GET /quotes/{quoteId} static const String getQuoteDetails = '/quotes'; /// Update quote /// PUT /quotes/{quoteId} static const String updateQuote = '/quotes'; /// Send quote to client /// POST /quotes/{quoteId}/send /// Body: { "email": "client@example.com" } static const String sendQuote = '/quotes'; /// Convert quote to order /// POST /quotes/{quoteId}/convert static const String convertQuoteToOrder = '/quotes'; // ============================================================================ // Chat Endpoints // ============================================================================ /// WebSocket endpoint for real-time chat static const String chatWebSocket = '/ws/chat'; /// Get chat messages /// GET /chat/messages?roomId={roomId}&before={messageId}&limit={limit} static const String getChatMessages = '/chat/messages'; /// Send chat message /// POST /chat/messages /// Body: { "roomId": "...", "text": "...", "attachments": [...] } static const String sendChatMessage = '/chat/messages'; /// Mark messages as read /// POST /chat/messages/read /// Body: { "messageIds": [...] } static const String markMessagesAsRead = '/chat/messages/read'; // ============================================================================ // Account & Profile Endpoints // ============================================================================ /// Get user profile /// GET /profile static const String getProfile = '/profile'; /// Update user profile /// PUT /profile static const String updateProfile = '/profile'; /// Upload avatar /// POST /profile/avatar /// Form-data: { "avatar": File } static const String uploadAvatar = '/profile/avatar'; /// Change password /// POST /profile/change-password /// Body: { "currentPassword": "...", "newPassword": "..." } static const String changePassword = '/profile/change-password'; /// Get user addresses /// GET /addresses static const String getAddresses = '/addresses'; /// Add new address /// POST /addresses static const String addAddress = '/addresses'; /// Update address /// PUT /addresses/{addressId} static const String updateAddress = '/addresses'; /// Delete address /// DELETE /addresses/{addressId} static const String deleteAddress = '/addresses'; /// Set default address /// POST /addresses/{addressId}/set-default static const String setDefaultAddress = '/addresses'; // ============================================================================ // Promotion Endpoints // ============================================================================ /// Get active promotions /// GET /promotions?category={category} static const String getPromotions = '/promotions'; /// Get promotion details /// GET /promotions/{promotionId} static const String getPromotionDetails = '/promotions'; /// Claim promotion /// POST /promotions/{promotionId}/claim static const String claimPromotion = '/promotions'; // ============================================================================ // Frappe/ERPNext API Endpoints // ============================================================================ /// Frappe API method prefix static const String frappeApiMethod = '/api/method'; /// Get Frappe session (public API, no auth required) /// POST /api/method/dbiz_common.dbiz_common.api.auth.get_session /// Returns: { "message": { "data": { "sid": "...", "csrf_token": "..." } }, "full_name": "..." } static const String frappeGetSession = '/dbiz_common.dbiz_common.api.auth.get_session'; /// Login with phone (requires session sid and csrf_token) /// POST /api/method/building_material.building_material.api.auth.login /// Body: { "username": "phone", "googleid": null, "facebookid": null, "zaloid": null } /// Returns: { "message": { "data": { "sid": "...", "csrf_token": "..." } }, "full_name": "..." } static const String frappeLogin = '/building_material.building_material.api.auth.login'; /// Frappe client get_list (requires sid and csrf_token) /// POST /api/method/frappe.client.get_list static const String frappeGetList = '/frappe.client.get_list'; /// Frappe client get (requires sid and csrf_token) /// POST /api/method/frappe.client.get static const String frappeGet = '/frappe.client.get'; /// Register user (requires session sid and csrf_token) /// POST /api/method/building_material.building_material.api.user.register static const String frappeRegister = '/building_material.building_material.api.user.register'; /// Frappe public API user ID static const String frappePublicUserId = 'public_api@dbiz.com'; // ============================================================================ // Product/Item Endpoints (Frappe ERPNext) // ============================================================================ /// Get product/item list (requires sid and csrf_token) /// POST /api/method/building_material.building_material.api.item.get_list /// Body: { "limit_start": 0, "limit_page_length": 0 } static const String frappeGetItems = '/building_material.building_material.api.item.get_list'; /// Get product/item detail (requires sid and csrf_token) /// POST /api/method/building_material.building_material.api.item.get_detail /// Body: { "name": "item_code" } static const String frappeGetItemDetail = '/building_material.building_material.api.item.get_detail'; /// Get item attributes list (requires sid and csrf_token) /// POST /api/method/building_material.building_material.api.item_attribute.get_list /// Body: { "filters": {"is_group": 0}, "limit_page_length": 0 } static const String frappeGetItemAttributes = '/building_material.building_material.api.item_attribute.get_list'; // ============================================================================ // Review/Feedback Endpoints (Frappe ERPNext) // ============================================================================ /// Get list of reviews for a product (requires sid and csrf_token) /// POST /api/method/building_material.building_material.api.item_feedback.get_list /// Body: { "limit_page_length": 10, "limit_start": 0, "item_id": "GIB20 G04" } static const String frappeGetReviews = '/building_material.building_material.api.item_feedback.get_list'; /// Create or update a review (requires sid and csrf_token) /// POST /api/method/building_material.building_material.api.item_feedback.update /// Body: { "item_id": "...", "rating": 0.5, "comment": "...", "name": "..." } /// Note: rating is 0-1 scale (0.5 = 50% or 2.5 stars out of 5) /// Note: name is optional - if provided, updates existing review static const String frappeUpdateReview = '/building_material.building_material.api.item_feedback.update'; /// Delete a review (requires sid and csrf_token) /// POST /api/method/building_material.building_material.api.item_feedback.delete /// Body: { "name": "ITEM-{item_id}-{user_email}" } static const String frappeDeleteReview = '/building_material.building_material.api.item_feedback.delete'; // ============================================================================ // Notification Endpoints // ============================================================================ /// Get notifications /// GET /notifications?type={type}&page={page}&limit={limit} static const String getNotifications = '/notifications'; /// Mark notification as read /// POST /notifications/{notificationId}/read static const String markNotificationAsRead = '/notifications'; /// Mark all notifications as read /// POST /notifications/read-all static const String markAllNotificationsAsRead = '/notifications/read-all'; /// Clear all notifications /// DELETE /notifications static const String clearAllNotifications = '/notifications'; /// Register FCM token for push notifications /// POST /notifications/fcm-token /// Body: { "token": "..." } static const String registerFcmToken = '/notifications/fcm-token'; // ============================================================================ // Helper Methods // ============================================================================ /// Build full URL for endpoint /// /// Example: /// ```dart /// final url = ApiConstants.buildUrl('/api/method/frappe.client.get_list', {'doctype': 'Item'}); /// // Returns: https://land.dbiz.com/api/method/frappe.client.get_list?doctype=Item /// ``` static String buildUrl(String endpoint, [Map? queryParams]) { final uri = Uri.parse('$apiBaseUrl$endpoint'); if (queryParams != null && queryParams.isNotEmpty) { return uri.replace(queryParameters: queryParams).toString(); } return uri.toString(); } /// Build URL with path parameters /// /// Example: /// ```dart /// final url = ApiConstants.buildUrlWithParams('/api/resource/Item/{id}', {'id': '123'}); /// // Returns: https://land.dbiz.com/api/resource/Item/123 /// ``` static String buildUrlWithParams( String endpoint, Map params, ) { String url = endpoint; params.forEach((key, value) { url = url.replaceAll('{$key}', value); }); return '$apiBaseUrl$url'; } }