diff --git a/.stylelintrc.json b/.stylelintrc.json new file mode 100644 index 0000000..c873209 --- /dev/null +++ b/.stylelintrc.json @@ -0,0 +1,12 @@ +{ + "extends": ["stylelint-config-standard"], + "rules": { + "at-rule-no-unknown": [ + true, + { + "ignoreAtRules": ["tailwind", "apply", "variants", "responsive", "screen", "layer"] + } + ], + "declaration-property-value-no-unknown": null + } +} \ No newline at end of file diff --git a/Get Api's.txt b/Get Api's.txt new file mode 100644 index 0000000..ca70c6e --- /dev/null +++ b/Get Api's.txt @@ -0,0 +1,184 @@ +πŸ‘€ Users & Authorization GET APIs +http + + +# Get all users (Web) +https://fiesta.nearle.app/live/api/v1/web/users/getallusers?roleid=2&tenantid=8&pageno=1&pagesize=10&keyword=john +# Get a specific user profile by ID (Web) +https://fiesta.nearle.app/live/api/v1/web/users/getusers?userid=12 +# Get a specific user profile by ID (Mobile) +https://fiesta.nearle.app/live/api/v1/mob/users/getusers?userid=15 +πŸ‘₯ Customer Management GET APIs +http + + +# Fetch customer profile by ID or Contact (Mobile) +https://fiesta.nearle.app/live/api/v1/mob/customers/getbyid?customerid=4082&contactno=9876543210 +# Get customer saved address locations (Mobile) +https://fiesta.nearle.app/live/api/v1/mob/customers/getcustomerlocation?customerid=4082 +# Get customer logged support requests (Mobile) +https://fiesta.nearle.app/live/api/v1/mob/customers/getcustomerrequests?customerid=4082&pageno=1&pagesize=10 +# Search customer names under a tenant (Mobile) +https://fiesta.nearle.app/live/api/v1/mob/customers/search?keyword=Jane&tenantid=8 +# Retrieve customers linked to a tenant location (Mobile) +https://fiesta.nearle.app/live/api/v1/mob/customers/gettenantcustomers?tenantid=8&locationid=2&pageno=1&pagesize=10 +# Retrieve merchant customers list (Web) +https://fiesta.nearle.app/live/api/v1/web/customers/gettenantcustomers?tenantid=8&locationid=2&pageno=1&pagesize=10&keyword=jane +πŸ“¦ Product & Catalog GET APIs +http + + +# Get product subcategories (Web) +https://fiesta.nearle.app/live/api/v1/web/products/getproductsubcategories?categoryid=2&tenantid=8 +# Get products stock counts (Web) +https://fiesta.nearle.app/live/api/v1/web/products/getproductscount?tenantid=8&categoryid=2&subcategoryid=12&approve=1 +# Get all global categories (Web) +https://fiesta.nearle.app/live/api/v1/web/products/getproductcategories +# Get specific product variants (Web) +https://fiesta.nearle.app/live/api/v1/web/products/getproductvariants?tenantid=8&subcategoryid=12 +# Get master catalog listings (Web) +https://fiesta.nearle.app/live/api/v1/web/products/getcatalougeproducts?tenantid=8&locationid=2&subcategoryid=12&keyword=&pageno=1&pagesize=10 +# Get live stocks catalog (Web) +https://fiesta.nearle.app/live/api/v1/web/products/getproductstocks?tenantid=8&locationid=2 +# Get dynamic stock statement ledger (Web) +https://fiesta.nearle.app/live/api/v1/web/products/getstockstatement?tenantid=8&locationid=2&subcategoryid=12&pageno=1&pagesize=10&keyword= +# Get outlet geofenced inventory (Web) +https://fiesta.nearle.app/live/api/v1/web/products/getlocationproducts?tenantid=8&locationid=2&subcategoryid=12&pageno=1&pagesize=10 +# Get outlet category inventory summary (Web) +https://fiesta.nearle.app/live/api/v1/web/products/getlocationproductsummary?tenantid=8&locationid=2 +# Master products search board (Web) +https://fiesta.nearle.app/live/api/v1/web/products/getallproducts?tenantid=8&locationid=2&keyword=milk&pageno=1&pagesize=10 +# Get product details by variant ID (Mobile) +https://fiesta.nearle.app/live/api/v1/mob/products/getproductbyvariant?tenantid=8&variantid=4 +# Get product subcategories (Mobile) +https://fiesta.nearle.app/live/api/v1/mob/products/getproductsubcategories?categoryid=2&tenantid=8 +# Search product catalog (Mobile) +https://fiesta.nearle.app/live/api/v1/mob/products/getallproducts?keyword=milk&pageno=1&pagesize=10 +# Get structured home subcategory list (Mobile) +https://fiesta.nearle.app/live/api/v1/mob/products/getproductsbysubcategory?categoryid=2&tenantid=8&locationid=2 +# Get mobile geofenced outlet products (Mobile) +https://fiesta.nearle.app/live/api/v1/mob/products/getlocationproducts?tenantid=8&locationid=2&pageno=1&pagesize=20 +πŸ“‹ Order Orchestration GET APIs +http + + +# Filtered dynamic orders board (Web) +https://fiesta.nearle.app/live/api/v1/web/orders/getorders?tenantid=8&locationid=2&status=processing&pageno=1&pagesize=10 +# Tenant orders board (Web) +https://fiesta.nearle.app/live/api/v1/web/orders/tenant/getorders?tenantid=8&locationid=2&status=processing&pageno=1&pagesize=10 +# Partner fleet orders board (Web) +https://fiesta.nearle.app/live/api/v1/web/orders/partner/getorders?partnerid=1&status=processing&pageno=1&pagesize=10 +# Individual consumer invoice histories (Web) +https://fiesta.nearle.app/live/api/v1/web/orders/customer/getorders?customerid=4082&status=delivered&pageno=1&pagesize=10 +# Operator/User orders board (Web) +https://fiesta.nearle.app/live/api/v1/web/orders/user/getorders?appuserid=12&status=processing&pageno=1&pagesize=10 +# System Admin orders board (Web) +https://fiesta.nearle.app/live/api/v1/web/orders/admin/getorders?applocationid=1&status=processing&pageno=1&pagesize=10 +# Get order dashboard stats summary (Web) +https://fiesta.nearle.app/live/api/v1/web/orders/getordersummary?tenantid=8&fromdate=2026-05-01&todate=2026-05-20 +# Get location orders summary (Web) +https://fiesta.nearle.app/live/api/v1/web/orders/getlocationsummary?tenantid=8 +# Get annual orders insights analytics (Web) +https://fiesta.nearle.app/live/api/v1/web/orders/getorderinsight?tenantid=8 +# Get order detailed lines (Web) +https://fiesta.nearle.app/live/api/v1/web/orders/getorderdetails?orderheaderid=2099 +# Get customer order history (Mobile) +https://fiesta.nearle.app/live/api/v1/mob/orders/getcustomerorders?customerid=4082&pageno=1&pagesize=10 +# Get specific tenant store orders (Mobile) +https://fiesta.nearle.app/live/api/v1/mob/orders/tenant/getorders?tenantid=8&locationid=2&pageno=1&pagesize=10 +# Get mobile order detailed lines (Mobile) +https://fiesta.nearle.app/live/api/v1/mob/orders/getorderdetails?orderheaderid=2099 +🏍️ Partner & Rider GET APIs +http + + +# Get active riders (Web) +https://fiesta.nearle.app/live/api/v1/web/partners/getriders?partnerid=1&applocationid=1&userid=12&tenantid=8 +# Get partner profiles (Web) +https://fiesta.nearle.app/live/api/v1/web/partners/getpartners?partnerid=1&applocationid=1&userid=12 +# Get rider shifts (Web) +https://fiesta.nearle.app/live/api/v1/web/partners/getridershifts?applocationid=1 +# Get location configurations (Web) +https://fiesta.nearle.app/live/api/v1/web/partners/getlocations?userid=12&configid=1 +# Get rider log sheet (Web) +https://fiesta.nearle.app/live/api/v1/web/partners/getriderlogs?partnerid=1&applocationid=1&fromdate=2026-05-20&todate=2026-05-20 +# Get partner profiles (Mobile) +https://fiesta.nearle.app/live/api/v1/mob/partners/getpartners?partnerid=1&applocationid=1&userid=12 +# Get rider log sheet (Mobile) +https://fiesta.nearle.app/live/api/v1/mob/partners/getriderlogs?partnerid=1&applocationid=1&fromdate=2026-05-20&todate=2026-05-20 +# Get rider operational info (Mobile) +https://fiesta.nearle.app/live/api/v1/mob/partners/getriderinfo?userid=15 +# Get active riders list (Mobile) +https://fiesta.nearle.app/live/api/v1/mob/partners/getriders?partnerid=1&applocationid=1&userid=15&tenantid=8 +🚚 Logistics & Deliveries GET APIs +http + + +# Get deliveries performance summaries (Web) +https://fiesta.nearle.app/live/api/v1/web/deliveries/deliverysummary?tenantid=8&partnerid=1&userid=12&applocationid=1&locationid=2&fromdate=2026-05-20&todate=2026-05-20 +# Get daily delivery insights (Web) +https://fiesta.nearle.app/live/api/v1/web/deliveries/getdeliveryinsight?tenantid=8 +# Get location deliveries summary (Web) +https://fiesta.nearle.app/live/api/v1/web/deliveries/getlocationsummary?tenantid=8 +# Get deliveries financial report summary (Web) +https://fiesta.nearle.app/live/api/v1/web/deliveries/getreportsummary?tenantid=8&partnerid=1&userid=12&applocationid=1&fromdate=2026-05-01&todate=2026-05-20 +# Get fleet rider summary metrics (Web) +https://fiesta.nearle.app/live/api/v1/web/deliveries/getridersummary?applocationid=1&partnerid=1&tenantid=8&fromdate=2026-05-20&todate=2026-05-20 +# Get master deliveries board (Web) +https://fiesta.nearle.app/live/api/v1/web/deliveries/getdeliveries?tenantid=8&fromdate=2026-05-20&todate=2026-05-20 +# Get mobile dispatch summaries (Mobile) +https://fiesta.nearle.app/live/api/v1/mob/deliveries/deliverysummary?userid=15&fromdate=2026-05-20&todate=2026-05-20 +# Get mobile deliveries board (Mobile) +https://fiesta.nearle.app/live/api/v1/mob/deliveries/getdeliveries?userid=15&status=assigned +# Fetch rider active shift deliveries queue (Mobile) +https://fiesta.nearle.app/live/api/v1/mob/deliveries/getdeliveryqueues?userid=15&fromdate=2026-05-20&todate=2026-05-20 +πŸͺ Tenant & Outlet Location GET APIs +http + + +# Search registered tenants (Web) +https://fiesta.nearle.app/live/api/v1/web/tenants/search?status=Active&keyword=Fresh +# Search tenants by keyword (Web) +https://fiesta.nearle.app/live/api/v1/web/tenants/searchbykeyword?keyword=daily +# Get all active tenants catalog (Web) +https://fiesta.nearle.app/live/api/v1/web/tenants/getalltenants?applocationid=1&status=Active&pageno=1&pagesize=10 +# Get outlet locations assigned to a tenant (Web) +https://fiesta.nearle.app/live/api/v1/web/tenants/gettenantlocations?tenantid=8 +# Retrieve delivery time slots config (Mobile) +https://fiesta.nearle.app/live/api/v1/mob/tenants/gettenantslot +# Search tenants by keyword (Mobile) +https://fiesta.nearle.app/live/api/v1/mob/tenants/searchbykeyword?keyword=grocery +# Retrieve tenants associated with a customer (Mobile) +https://fiesta.nearle.app/live/api/v1/mob/tenants/getcustomertenants?customerid=4082&tenant=0 +# Get outlet locations linked to a tenant (Mobile) +https://fiesta.nearle.app/live/api/v1/mob/tenants/gettenantlocations?tenantid=8 +# Get logistics pricing slabs for a tenant (Mobile) +https://fiesta.nearle.app/live/api/v1/mob/tenants/gettenantpricing?tenantid=8&applocationid=1 +# Get staff members under a tenant store (Mobile) +https://fiesta.nearle.app/live/api/v1/mob/tenants/getstaffs?tenantid=8 +# Get tenant detailed profile info (Mobile) +https://fiesta.nearle.app/live/api/v1/mob/tenants/gettenantinfo?tenantid=8&locationid=2 +βš™οΈ Utilities & General Configurations GET APIs +http + + +# Fetch application categories by tag (Web) +https://fiesta.nearle.app/live/api/v1/web/utils/getapptypes?tag=customer +# Resolve subcategories (Web) +https://fiesta.nearle.app/live/api/v1/web/utils/getsubcategories?moduleid=1&categoryid=2 +# Fetch system active geofence details (Web) +https://fiesta.nearle.app/live/api/v1/web/utils/getapplocations?applocationid=1 +# Fetch global system categories configurations (Web) +https://fiesta.nearle.app/live/api/v1/web/utils/getappcategories +# Get mobile geofence configuration details (Mobile) +https://fiesta.nearle.app/live/api/v1/mob/utils/getapplocationconfig?applocationid=1 +# Get mobile active geofence details (Mobile) +https://fiesta.nearle.app/live/api/v1/mob/utils/getapplocations?applocationid=1 +# Fetch mobile app types by tag (Mobile) +https://fiesta.nearle.app/live/api/v1/mob/utils/getapptypes?tag=rider +# Fetch global payment & geofence configs (Mobile) +https://fiesta.nearle.app/live/api/v1/mob/utils/getappconfig?configid=1 +# Get mobile category subcategories list (Mobile) +https://fiesta.nearle.app/live/api/v1/mob/utils/getsubcategories?moduleid=1&categoryid=2 +# Fetch mobile app categories configurations (Mobile) +https://fiesta.nearle.app/live/api/v1/mob/utils/getappcategories diff --git a/Post, put and delete Api's.txt b/Post, put and delete Api's.txt new file mode 100644 index 0000000..3aa7077 --- /dev/null +++ b/Post, put and delete Api's.txt @@ -0,0 +1,652 @@ +1. User & Authentication APIs +πŸ’» Web Portal +POST Tenant Web Panel Login +URL: https://fiesta.nearle.app/live/api/v1/web/users/tenant/weblogin +Payload: +json + + +{ + "authname": "merchant_admin_01", + "password": "PasswordSecurityHash99!" +} +POST General Application Login +URL: https://fiesta.nearle.app/live/api/v1/web/users/applogin +Payload: +json + + +{ + "authname": "system_operator", + "password": "OperatorSafePasswordSecret", + "deviceid": "device_uuid_8828b", + "devicetype": "android" +} +POST Register New Web Staff Account +URL: https://fiesta.nearle.app/live/api/v1/web/users/create +Payload: +json + + +{ + "authname": "tenant_staff_steve", + "firstname": "Steve", + "lastname": "Rogers", + "password": "SteveSecurePassword12", + "email": "steve.rogers@merchant.com", + "dialcode": "+61", + "contactno": "412345678", + "roleid": 3, + "pin": 1234, + "address": "100 Flinders St", + "suburb": "Melbourne", + "city": "Melbourne", + "state": "VIC", + "postcode": "3000", + "tenantid": 8, + "locationid": 2, + "applocationid": 1, + "status": "active" +} +PUT Update Web Staff User Details +URL: https://fiesta.nearle.app/live/api/v1/web/users/update +Payload: +json + + +{ + "userid": 15, + "firstname": "Steve", + "lastname": "Captain", + "email": "steve.captain@merchant.com", + "contactno": "412345678", + "address": "200 Flinders St", + "suburb": "Melbourne", + "city": "Melbourne", + "state": "VIC", + "postcode": "3000", + "status": "active" +} +πŸ“± Mobile App +POST Rider/Merchant Mobile App Login +URL: https://fiesta.nearle.app/live/api/v1/mob/users/tenant/login +Payload: +json + + +{ + "authname": "rider_john_01", + "password": "RiderSecretKey" +} +POST Create Mobile Staff User +URL: https://fiesta.nearle.app/live/api/v1/mob/users/create +Payload: (Uses the same schema as Web Register New Web Staff Account) +PUT Update Mobile Staff Details +URL: https://fiesta.nearle.app/live/api/v1/mob/users/update +Payload: (Uses the same schema as Web Update Web Staff User Details) +2. Customer Management APIs +πŸ“± Mobile App +POST Passwordless OTP Login (via Phone) +URL: https://fiesta.nearle.app/live/api/v1/mob/customers/login +Payload: +json + + +{ + "contactno": "0499888777" +} +POST Register Customer Account +URL: https://fiesta.nearle.app/live/api/v1/mob/customers/create +Payload: +json + + +{ + "firstname": "Jane", + "lastname": "Smith", + "profileimage": "https://storage.nearle.app/avatars/jane_smith.jpg", + "gender": "Female", + "dob": "1994-11-20", + "dialcode": "+61", + "contactno": "499888777", + "email": "jane.smith@gmail.com", + "deviceid": "uuid_7728b991a", + "devicetype": "ios", + "authmode": 1, + "customertoken": "fcm_token_device_hash_xyz", + "address": "456 Oak Avenue", + "suburb": "Richmond", + "city": "Melbourne", + "state": "VIC", + "postcode": "3121", + "latitude": "-37.8212", + "longitude": "144.9984", + "applocationid": 1, + "status": 1, + "intro": "Regular subscriber of organic morning deliveries." +} +POST Save New Geofenced Location Address +URL: https://fiesta.nearle.app/live/api/v1/mob/customers/createlocations +Payload: +json + + +{ + "customerid": 4082, + "address": "123 High Street", + "suburb": "Prahran", + "city": "Melbourne", + "state": "VIC", + "postcode": "3181", + "latitude": "-37.8502", + "longitude": "144.9924", + "primaryaddress": 1, + "status": 1 +} +POST Log Customer Support Ticket Request +URL: https://fiesta.nearle.app/live/api/v1/mob/customers/createcustomerrequest +Payload: +json + + +{ + "customerid": 4082, + "tenantid": 8, + "apptypeid": 1, + "locationid": 2, + "subject": "Delay in Morning Milk Delivery", + "remarks": "Order scheduled for 7:00 AM hasn't arrived.", + "status": 1 +} +PUT Update Customer Profile Details +URL: https://fiesta.nearle.app/live/api/v1/mob/customers/update +Payload: +json + + +{ + "customerid": 4082, + "firstname": "Jane", + "lastname": "Miller", + "email": "jane.miller@gmail.com", + "contactno": "499888777", + "address": "789 Pine Road", + "suburb": "Hawthorn", + "city": "Melbourne", + "state": "VIC", + "postcode": "3122", + "status": 1 +} +3. Catalog & Product Inventory APIs +πŸ’» Web Portal +POST Add Multi-Product Stock Entry +URL: https://fiesta.nearle.app/live/api/v1/web/products/createproductstock +Payload: +json + + +[ + { + "tenantid": 8, + "locationid": 2, + "productid": 105, + "quantity": 150, + "stocktype": "credit", + "status": "active" + }, + { + "tenantid": 8, + "locationid": 2, + "productid": 109, + "quantity": 80, + "stocktype": "credit", + "status": "active" + } +] +POST Create Master Product Catalog Item +URL: https://fiesta.nearle.app/live/api/v1/web/products/create +Payload: +json + + +{ + "applocationid": 1, + "tenantid": 8, + "categoryid": 2, + "subcategoryid": 12, + "productname": "Fresh Cow Milk 1L", + "productimage": "https://storage.nearle.app/products/cow_milk_1l.png", + "productdesc": "Pure farm fresh cow milk pasteurized.", + "productsku": "MILK-COW-1L", + "productunit": "Litre", + "unitvalue": "1", + "productcost": 1.80, + "retailprice": 3.50, + "taxpercent": 5.00, + "productstock": 100, + "productstatus": "available", + "approve": 1 +} +POST Map Product to Specific Outlet Location +URL: https://fiesta.nearle.app/live/api/v1/web/products/createproductlocation +Payload: +json + + +[ + { + "tenantid": 8, + "locationid": 2, + "productid": 105, + "catlougeid": 1, + "minquantity": 5, + "maxquantity": 200, + "price": 3.75, + "status": "Active" + } +] +POST Create Product Variant Metadata +URL: https://fiesta.nearle.app/live/api/v1/web/products/createproductvariant +Payload: +json + + +{ + "tenantid": 8, + "variantname": "1.5 Liters Bottle", + "categoryid": 2, + "subcategoryid": 12, + "status": "active" +} +PUT Update Master Product Details +URL: https://fiesta.nearle.app/live/api/v1/web/products/update +Payload: +json + + +{ + "productid": 105, + "productname": "Organic Farm Cow Milk 1L", + "productcost": 1.95, + "retailprice": 3.75, + "productstock": 120, + "productstatus": "available" +} +PUT Update Product Outlet Constraints +URL: https://fiesta.nearle.app/live/api/v1/web/products/updateproductlocation +Payload: +json + + +{ + "productlocationid": 25, + "tenantid": 8, + "locationid": 2, + "productid": 105, + "minquantity": 10, + "maxquantity": 150, + "price": 3.99, + "status": "Active" +} +DELETE Purge Master Product Catalog Entry +URL: https://fiesta.nearle.app/live/api/v1/web/products/delete?productid=105 +Parameters: +productid (int, required): Unique ID of the catalog item to be purged. +πŸ“± Mobile App +PUT Mobile Update Product Details +URL: https://fiesta.nearle.app/live/api/v1/mob/products/update +Payload: (Uses the same schema as Web Update Master Product Details) +PUT Mobile Update Product Outlet Config +URL: https://fiesta.nearle.app/live/api/v1/mob/products/updateproductlocation +Payload: (Uses the same schema as Web Update Product Outlet Constraints) +4. Order Orchestration APIs +πŸ’» Web Portal +POST Create New Web Order (Flat JSON Format) +URL: https://fiesta.nearle.app/live/api/v1/web/orders/createorder +Payload: +json + + +{ + "tenantid": 8, + "locationid": 2, + "applocationid": 1, + "moduleid": 1, + "customerid": 4082, + "orderstatus": "pending", + "deliverytype": "standard", + "deliverytime": "2026-05-20 18:00:00", + "pickupaddress": "Shop 4, Central Plaza, Melbourne", + "pickuplat": "-37.8136", + "pickuplong": "144.9631", + "pickupcustomer": "Central Plaza Merchant", + "pickupcontactno": "399887766", + "deliveryaddress": "Apt 4B, Sunset Boulevard, Richmond", + "deliverylat": "-37.8212", + "deliverylong": "144.9984", + "orderamount": 11.48, + "taxamount": 1.10, + "ordervalue": 12.58, + "itemcount": 2, + "paymenttype": 1, + "paymentstatus": 0, + "deliverycharge": 3.00, + "ordernotes": "Please ring doorbell twice.", + "items": [ + { + "productid": 105, + "productname": "Organic Whole Milk 1L", + "orderqty": 2, + "price": 3.99, + "taxpercentage": 10.00, + "taxamount": 0.80, + "productsumprice": 7.98 + }, + { + "productid": 109, + "productname": "Salted Butter 250g", + "orderqty": 1, + "price": 3.50, + "taxpercentage": 10.00, + "taxamount": 0.35, + "productsumprice": 3.50 + } + ] +} +PUT Update Order Status & Financial Flag +URL: https://fiesta.nearle.app/live/api/v1/web/orders/updateorder +Payload: +json + + +{ + "orderheaderid": 2099, + "orderstatus": "ready", + "paymentstatus": 1, + "remarks": "Order packed and waiting for rider assignment." +} +πŸ“± Mobile App +POST Create Mobile Order (Wrapped JSON Format) +URL: https://fiesta.nearle.app/live/api/v1/mob/orders/createorder +Payload: +json + + +{ + "orders": { + "tenantid": 8, + "locationid": 2, + "applocationid": 1, + "moduleid": 1, + "customerid": 4082, + "orderstatus": "pending", + "deliverytype": "standard", + "deliverytime": "2026-05-20 18:00:00", + "pickupaddress": "Shop 4, Central Plaza, Melbourne", + "pickuplat": "-37.8136", + "pickuplong": "144.9631", + "pickupcustomer": "Central Plaza Merchant", + "pickupcontactno": "399887766", + "deliveryaddress": "Apt 4B, Sunset Boulevard, Richmond", + "deliverylat": "-37.8212", + "deliverylong": "144.9984", + "orderamount": 11.48, + "taxamount": 1.10, + "ordervalue": 12.58, + "itemcount": 2, + "paymenttype": 1, + "paymentstatus": 0, + "deliverycharge": 3.00, + "ordernotes": "Leave in parcel locker.", + "items": [ + { + "productid": 105, + "productname": "Organic Whole Milk 1L", + "orderqty": 2, + "price": 3.99, + "taxpercentage": 10.00, + "taxamount": 0.80, + "productsumprice": 7.98 + }, + { + "productid": 109, + "productname": "Salted Butter 250g", + "orderqty": 1, + "price": 3.50, + "taxpercentage": 10.00, + "taxamount": 0.35, + "productsumprice": 3.50 + } + ] + } +} +PUT Mobile Update Order Status +URL: https://fiesta.nearle.app/live/api/v1/mob/orders/updateorder +Payload: (Uses the same schema as Web Update Order Status & Financial Flag) +5. Delivery & Rider Logistics APIs +πŸ’» Web Portal +POST Initialize Logistics Delivery Dispatch (Assign Rider) +URL: https://fiesta.nearle.app/live/api/v1/web/deliveries/createdeliveries +Payload: +json + + +[ + { + "orderheaderid": 2100, + "applocationid": 1, + "partnerid": 1, + "tenantid": 8, + "moduleid": 1, + "locationid": 2, + "userid": 15, + "orderid": "ORD-19028-4", + "deliverydate": "2026-05-20", + "orderstatus": "assigned", + "assigntime": "2026-05-20 12:10:00", + "itemcount": 2, + "orderamount": 12.58, + "customerid": 4082, + "pickupcustomer": "Central Merchant Warehouse", + "pickupcontactno": "987654321", + "pickuplocationid": 2, + "pickupaddress": "Shop 4, Central Plaza, Melbourne", + "pickuplat": "-37.8136", + "pickuplon": "144.9631", + "deliverycustomerid": 4082, + "deliverylocationid": 554, + "deliverycustomer": "Jane Smith", + "deliverycontactno": "499888777", + "deliveryaddress": "456 Oak Avenue, Richmond, VIC, 3121", + "droplat": "-37.8212", + "droplon": "144.9984", + "deliverycharges": 3.00, + "deliveryamt": 15.58, + "deliverytype": "standard", + "paymenttype": 1 + } +] +PUT Update Rider Pickup Status +URL: https://fiesta.nearle.app/live/api/v1/web/deliveries/updatedelivery +Payload: +json + + +{ + "deliveryid": 8871, + "orderheaderid": 2100, + "userid": 15, + "orderstatus": "picked", + "pickuptime": "2026-05-20 12:20:00", + "riderslat": "-37.8140", + "riderslon": "144.9640" +} +πŸ“± Mobile App +POST Initialize Mobile Logistics Delivery +URL: https://fiesta.nearle.app/live/api/v1/mob/deliveries/createdeliveries +Payload: (Uses the same schema as Web Initialize Logistics Delivery Dispatch) +PUT Rider Update Dispatch Status (Delivered & GPS Tracking) +URL: https://fiesta.nearle.app/live/api/v1/mob/deliveries/updatedelivery +Payload: +json + + +{ + "deliveryid": 8871, + "orderheaderid": 2100, + "userid": 15, + "orderstatus": "delivered", + "deliverytime": "2026-05-20 12:45:00", + "riderslat": "-37.8210", + "riderslon": "144.9980", + "actualkms": "4.2", + "feedback": "Handed over safely.", + "smsdelivery": 1 +} +6. Tenant & Location Configuration APIs +πŸ’» Web Portal +POST Link Customer Profile to Tenant Store +URL: https://fiesta.nearle.app/live/api/v1/web/tenants/createtenantcustomer +Payload: +json + + +{ + "moduleid": 1, + "tenantid": 8, + "locationid": 2, + "customerid": 4082, + "customerlocationid": 554, + "status": 1 +} +POST Create New Geofenced Store Location +URL: https://fiesta.nearle.app/live/api/v1/web/tenants/createlocation +Payload: +json + + +{ + "tenantid": 8, + "applocationid": 1, + "moduleid": 1, + "locationname": "Hawthorn Daily Fresh Store", + "email": "hawthorn.store@merchant.com", + "contactno": "399443322", + "latitude": "-37.8222", + "longitude": "145.0384", + "address": "12 Glenferrie Rd", + "suburb": "Hawthorn", + "city": "Melbourne", + "state": "VIC", + "postcode": "3122", + "opentime": "07:00:00", + "closetime": "22:00:00", + "partnerid": 1, + "deliveryradius": 5000, + "deliverymins": 30, + "cancelsecs": 60, + "status": "Active" +} +POST Map Tenant Location Linkage Entry +URL: https://fiesta.nearle.app/live/api/v1/web/tenants/createtenantlocation +Payload: (Uses the same schema as Create New Geofenced Store Location) +PUT Update Store Location Configurations +URL: https://fiesta.nearle.app/live/api/v1/web/tenants/updatelocation +Payload: +json + + +{ + "locationid": 2, + "tenantid": 8, + "applocationid": 1, + "locationname": "Richmond Daily Fresh Store", + "email": "richmond.store@merchant.com", + "contactno": "399887766", + "latitude": "-37.8212", + "longitude": "144.9984", + "address": "Shop 4, 100 Church St", + "suburb": "Richmond", + "city": "Melbourne", + "state": "VIC", + "postcode": "3121", + "opentime": "07:00:00", + "closetime": "22:00:00", + "deliveryradius": 8000, + "deliverymins": 45, + "cancelsecs": 120, + "status": "Active" +} +PUT Modify Tenant Location Linkage Config +URL: https://fiesta.nearle.app/live/api/v1/web/tenants/updatetenantlocation +Payload: (Uses the same schema as Update Store Location Configurations) +πŸ“± Mobile App +POST Mobile Link Customer Profile to Tenant Store +URL: https://fiesta.nearle.app/live/api/v1/mob/tenants/createtenantcustomer +Payload: (Uses the same schema as Web Link Customer Profile to Tenant Store) +POST Mobile Create Geofenced Store Location +URL: https://fiesta.nearle.app/live/api/v1/mob/tenants/createlocation +Payload: (Uses the same schema as Web Create New Geofenced Store Location) +POST Onboard/Create New Staff Member Account +URL: https://fiesta.nearle.app/live/api/v1/mob/tenants/createstaff +Payload: (Uses the same schema as Web Register New Web Staff Account) +POST Onboard New Tenant & Admin Profile (Joint Creation) +URL: https://fiesta.nearle.app/live/api/v1/mob/tenants/createtenantuser +Payload: +json + + +{ + "tenantname": "Fresh Organic Greens", + "configid": 1, + "partnerid": 1, + "moduleid": 1, + "tenanttype": "Enterprise", + "registrationno": "ABN-19028-299", + "tenanttoken": "tenant_fcm_token_hash_value_xyz", + "companyname": "Fresh Organic Greens Pty Ltd", + "devicetype": "web", + "firstname": "Arthur", + "primaryemail": "arthur@organicgreens.com", + "primarycontact": "488999000", + "categoryid": 2, + "subcategoryid": 12, + "address": "400 Chapel St", + "suburb": "South Yarra", + "city": "Melbourne", + "state": "VIC", + "postcode": "3141", + "latitude": "-37.8398", + "longitude": "144.9953", + "tenantimage": "https://storage.nearle.app/tenants/organic_greens.png", + "tenantinfo": "Direct farm-to-table organic vegetables delivery provider.", + "paymode1": 1, + "paymode2": 1, + "promotion": 0, + "minorder": 15, + "applocationid": 1, + "approved": 1, + "status": "Active", + "tenantlocations": { + "locationname": "South Yarra Main Warehouse", + "email": "southyarra@organicgreens.com", + "contactno": "488999000", + "latitude": "-37.8398", + "longitude": "144.9953", + "address": "400 Chapel St", + "suburb": "South Yarra", + "city": "Melbourne", + "state": "VIC", + "postcode": "3141", + "opentime": "06:00:00", + "closetime": "21:00:00", + "partnerid": 1, + "deliveryradius": 6000, + "deliverymins": 30, + "cancelsecs": 90, + "status": "Active" + } +} +PUT Mobile Update Store Location Configurations +URL: https://fiesta.nearle.app/live/api/v1/mob/tenants/updatelocation +Payload: (Uses the same schema as Web Update Store Location Configurations) diff --git a/combine_topics.cjs b/combine_topics.cjs new file mode 100644 index 0000000..fe9313e --- /dev/null +++ b/combine_topics.cjs @@ -0,0 +1,21 @@ +const fs = require('fs'); +const path = require('path'); +const { existingTopics } = require('./original_topics.cjs'); + +// Read current topics.js which has the new REST endpoints. +// We'll just import it using dynamic import since it's an ES module. +(async () => { + const topicsModule = await import('file:///' + path.join(__dirname, 'src/data/topics.js').replace(/\\/g, '/')); + const restTopics = topicsModule.topics; + + const out = `export const LEGACY_BASE_URL = 'https://api.workolik.com'; +export const REST_BASE_URL = 'https://fiesta.nearle.app'; + +export const legacyTopics = ${JSON.stringify(existingTopics, null, 2)}; + +export const restTopics = ${JSON.stringify(restTopics, null, 2)}; +`; + + fs.writeFileSync(path.join(__dirname, 'src/data/topics.js'), out, 'utf8'); + console.log('Successfully combined legacy and rest topics into topics.js'); +})(); diff --git a/index.html b/index.html index 29a7f22..549b3b5 100644 --- a/index.html +++ b/index.html @@ -5,7 +5,7 @@ - EXpress Developer Docs + Nearle Express Developer Docs diff --git a/original_topics.cjs b/original_topics.cjs new file mode 100644 index 0000000..7f318eb --- /dev/null +++ b/original_topics.cjs @@ -0,0 +1,347 @@ +const existingTopics = [ + { + id: 'utils', + name: 'Utils', + description: 'Shared lookup endpoints roles, locations, configs, app types, pricing.', + endpoints: [ + { + name: 'getuserroles', + method: 'GET', + url: '/api/rest/Xpress/utils/getuserroles', + description: 'Retrieve all user roles available in the system.' + }, + { + name: 'getapptypes', + method: 'GET', + url: '/api/rest/xpress/utils/getapptypes?tag=DELIVERY', + description: 'List application types filtered by a tag (e.g. DELIVERY).', + _note: "Host typo in source ('workopi') corrected to api.workolik.com." + }, + { + name: 'getapplocations', + method: 'GET', + url: '/api/rest/xpress/utils/getapplocations?userid=1326', + description: 'List all application locations.', + _note: "Source name 'Gpplocation' assumed to be 'Getapplocations'." + }, + { + name: 'getapppricing', + method: 'GET', + url: '/api/rest/xpress/utils/getapppricing?applocationid=1', + description: 'Retrieve app pricing for a given app location.', + _note: "Source query 'getapppripplocationid=' reconstructed as 'getapppricing?applocationid='." + }, + { + name: 'getallpricing', + method: 'GET', + url: '/api/rest/xpress/utils/getallpricing?applocationid=1', + description: 'Retrieve all pricing rules for an app location.' + }, + { + name: 'getapplocationconfig', + method: 'GET', + url: '/api/rest/xpress/utils/getapplocationconfig', + description: 'Retrieve the configuration for application locations.', + _note: "Host typo in source ('workom') corrected to api.workolik.com." + }, + { + name: 'getsubcategories', + method: 'GET', + url: '/api/rest/xpress/utils/getsubcategories/?moduleid=6', + description: 'List all subcategories for a given module.' + } + ] + }, + { + id: 'users', + name: 'Users', + description: 'Manage users and roles across the Xpress platform.', + endpoints: [ + { + name: 'getallusers', + method: 'GET', + url: '/api/rest/xpress/users/getallusers?roleid=2&configid=1&tenantid=1079&status=Active&keyword=%john%&limit=10&offset=0', + description: 'List users filtered by role, config, tenant, status, and keyword.' + }, + { + name: 'getusersinfo', + method: 'GET', + url: '/api/rest/xpress/users/getusers?userid=1326', + description: 'Retrieve a single user by ID.' + } + ] + }, + { + id: 'partners', + name: 'Partners', + description: 'Partners, riders, locations, shifts, and rider pricing.', + endpoints: [ + { + name: 'getlocations (by user)', + method: 'GET', + url: '/api/rest/xpress/partners/getlocations?userid=1277&configid=9', + description: 'Get partner locations linked to a specific user.' + }, + { + name: 'gettenantlocations ', + method: 'GET', + url: '/api/rest/xpress/tenants/gettenantlocations?keyword=daily&tenantid=916', + description: 'Get partner locations linked to a specific tenant.' + }, + { + name: 'getpartners', + method: 'GET', + url: '/api/rest/xpress/partners/getpartners', + description: 'List all partners.', + _note: "Source URL had a double slash (/api/rest//partners/) β€” assumed /xpress/partners/." + }, + { + name: 'getriderpricing', + method: 'GET', + url: '/api/rest/xpress/partners/getriderpricing?applocationid=0', + description: 'Get rider pricing rules for an app location.' + }, + { + name: 'getallriders', + method: 'GET', + url: '/api/rest/xpress/partners/getallriders?partnerid=44&limit=10', + description: 'List all riders for a partner.' + }, + { + name: 'getallridersummary', + method: 'GET', + url: '/api/rest/xpress/partners/getallridersummary?applocationid=1', + description: 'Aggregated summary of all riders at a location.' + }, + { + name: 'getriderdetail', + method: 'GET', + url: '/api/rest/xpress/partners/getriderdetail?userid=1', + description: 'Detailed information for a specific rider.', + _note: "Source had 'htapi.workolik.com' (missing 'tps://') β€” restored to https://api.workolik.com." + }, + { + name: 'getridershifts', + method: 'GET', + url: '/api/rest/xpress/partners/getridershifts?applocationid=1', + description: 'Get rider shift records for an app location.', + _note: "Host typo in source ('workk.com') corrected to api.workolik.com." + } + ] + }, + { + id: 'tenants', + name: 'Tenants', + description: 'Tenant accounts info, locations, customers, orders, pricing, summary.', + endpoints: [ + { + name: 'gettenants', + method: 'GET', + url: '/api/rest/xpress/tenants/gettenants?applocationid=1&status=active&partnerid=44', + description: 'List tenants filtered by app location and status.', + params: [ + { name: 'applocationid', type: 'STRING', default: '1' }, + { name: 'status', type: 'STRING', default: 'active' }, + { name: 'partnerid', type: 'string', default: '44' } + ], + _note: "Source name 'Gettenan' truncated β€” assumed 'Gettenants'." + }, + { + name: 'getalltenants', + method: 'GET', + url: '/api/rest/xpress/tenants/getalltenants?applocationid=1&moduleid=6&tenanttype=E&keyword=%25%25&status=Active&limit=10&offset=0', + description: 'List all tenants with extensive filters.', + params: [ + { name: 'applocationid', type: 'STRING', default: '1' }, + { name: 'moduleid', type: 'STRING', default: '6' }, + { name: 'tenanttype', type: 'STRING', default: 'E' }, + { name: 'keyword', type: 'STRING', default: '%%' }, + { name: 'status', type: 'STRING', default: 'Active' }, + { name: 'limit', type: 'STRING', default: '10' }, + { name: 'offset', type: 'STRING', default: '0' } + ], + _note: "Source query 'status=Actilimit=10' reconstructed as 'status=Active&limit=10'. 'approved=1' param dropped per spec." + }, + { + name: 'gettenantinfo', + method: 'GET', + url: '/api/rest/xpress/tenants/gettenantinfo?tenantid=1', + description: 'Get basic information about a tenant.' + }, + { + name: 'gettenantlocations', + method: 'GET', + url: '/api/rest/xpress/tenants/gettenantlocations?tenantid=916&keyword=%%', + description: 'List physical locations for a tenant.' + }, + { + name: 'gettenantsummary', + method: 'GET', + url: '/api/rest/xpress/tenants/gettenantsummary?moduleid=6&applocationid=1&tenanttype=E&keyword=%%', + description: 'Aggregated summary for tenants under a module/location.', + _note: "Source 'tenettenantsummary' reconstructed as 'tenants/gettenantsummary'." + }, + { + name: 'getpricinglist', + method: 'GET', + url: '/api/rest/xpress/tenants/getpricinglist?tenantid=1087', + description: 'Retrieve the tenant’s pricing list.', + _note: "Source 'getpricingl' truncated β€” assumed 'getpricinglist'." + }, + { + name: 'tenantsearch', + method: 'GET', + url: '/api/rest/xpress/tenants/search?keyword=daily&status=active', + description: 'Full-text search for tenants by keyword.', + _note: "Source name 'Tennatsearch' assumed to be 'Tenantsearch'." + }, + { + name: 'getorders', + method: 'GET', + url: '/api/rest/xpress/tenant/getorders?applocationid=&tenantid=&locationid=&status=', + description: 'List orders scoped to a single tenant.', + _note: "Source uses /tenant/ (singular) here. Confirm whether this should be /tenants/ or kept as /tenant/." + } + ] + }, + { + id: 'customers', + name: 'Customers', + description: 'Customer accounts, lookups, and search.', + endpoints: [ + { + name: 'getallcustomers', + method: 'GET', + url: '/api/rest/xpress/customers/getallcustomers?applocationid=&keyword=&pageno=&pagesize=', + description: 'Paginated list of all customers under an app location.' + }, + { + name: 'gettenantcustomers', + method: 'GET', + url: '/api/rest/xpress/customers/gettenantcustomers?tenantid=1087&limit=10&offset=0', + description: 'List customers under a specific tenant.', + _note: "Source path 'xpresustomers' reconstructed as 'xpress/customers'." + }, + { + name: 'searchcustomers', + method: 'GET', + url: '/api/rest/xpress/customers/searchcustomers?tenantid=1087&keyword=%%', + description: 'Search customers by keyword within a tenant.', + _note: "Source name 'Searchcumer' assumed to be 'Searchcustomers'." + } + ] + }, + { + id: 'deliveries', + name: 'Deliveries', + description: 'Delivery records, queues, and rider-delivery joins.', + endpoints: [ + { + name: 'getdeliveries', + method: 'GET', + url: '/api/rest/xpress/deliveries/getdeliveries?tenantid=10&status=Delivered&fromdate=2026-05-01T00:00:00&todate=2026-05-05T23:59:59&keyword=%john%&limit=10&offset=0', + description: 'List deliveries with filters: tenant, status, date range, keyword.', + _note: "Source query 'tenantid=10=Delivered' reconstructed as 'tenantid=10&status=Delivered'." + }, + { + name: 'getdeliveryqueues', + method: 'GET', + url: '/api/rest/xpress/deliveries/getdeliveryqueues?userid=1277&fdate=2025-12-30T00:00:00 &tdate=2025-12-30T23:59:59', + description: 'Retrieve delivery queue snapshots for a user/date range.', + _note: "Source name 'tdeliveryqueues' truncated β€” assumed 'Getdeliveryqueues'." + }, + { + name: 'deliverysummary', + method: 'GET', + url: '/api/rest/xpress/deliveries/deliverysummary?appuserid=1&fromdate=2026-03-31T00:00:00&todate=2026-03-31T23:59:59&status=active&applocationid=1&locationid=1160&tenantid=916&userid=865', + description: 'Aggregated delivery summary for an app user.' + }, + { + name: 'getriderbydelivery', + method: 'GET', + url: '/api/rest/xpress/deliveries/getriderbydelivery/?applocationid=&tenantid=&locationid=&fromdate=&todate=&keyword=', + description: 'Map riders to deliveries within a location and date range.', + _note: "Source URL was missing 'https' scheme β€” restored." + } + ] + }, + { + id: 'orders', + name: 'Orders', + description: 'Order records, details, and aggregate summaries.', + endpoints: [ + { + name: 'getorders', + method: 'GET', + url: '/api/rest/xpress/orders/tenant/getorders?start=2026-05-01T00:00:00&end=2026-05-31T23:59:59&status=delivered&limit=10&offset=0', + description: 'List orders within a time frame and status.' + }, + { + name: 'getordersummary', + method: 'GET', + url: '/api/rest/getordersummary/?tenantid=1079&fromdate=2025-07-24&todate=2025-07-24&configid=9', + description: 'High-level summary of orders for a tenant/date range.' + }, + { + name: 'getorderdetails', + method: 'GET', + url: '/api/rest/xpress/orders/getorderdetails?orderheaderid=6562', + description: 'Get full details of an order by header ID.' + }, + { + name: 'getlocationsummary', + method: 'GET', + url: '/api/rest/xpress/orders/getlocationsummary?tenantid=916', + description: 'Per-location summary of orders.' + } + ] + }, + { + id: 'products', + name: 'Products', + description: 'Product catalog, categories, and subcategories.', + endpoints: [ + { + name: 'getproductcategories', + method: 'GET', + url: '/api/rest/xpress/products/getproductcategories?moduleid=1', + description: 'List root product categories for a module.' + }, + { + name: 'getproductsubcategories', + method: 'GET', + url: '/api/rest/xpress/products/getproductsubcategories?categoryid=1001', + description: 'List subcategories under a product category.' + } + ] + }, + { + id: 'invoice', + name: 'Invoice', + description: 'Invoice insights and billing analytics.', + endpoints: [ + { + name: 'getinvoiceinsight', + method: 'GET', + url: '/api/rest/xpress/invoice/getinvoiceinsight?tenantid=916', + description: 'Retrieve invoice insights and statistics for a tenant.', + _note: "Source name 'Getinvoinsights' assumed to be 'Getinvoiceinsight'." + } + ] + }, + { + id: 'payments', + name: 'Payments', + description: 'Payment requests and settlements.', + endpoints: [ + { + name: 'getpaymentrequest', + method: 'GET', + url: '/api/rest/xpress/payments/requests/getpaymentrequest?partnerid=44&status=1', + description: 'List payment requests for a partner by status.' + } + ] + } +]; + +module.exports = { existingTopics }; diff --git a/package-lock.json b/package-lock.json index 9bf76ae..96a37fd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -67,7 +67,6 @@ "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.29.0", "@babel/generator": "^7.29.0", @@ -1478,7 +1477,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.10.12", "caniuse-lite": "^1.0.30001782", @@ -2337,7 +2335,6 @@ "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", "dev": true, "license": "MIT", - "peer": true, "bin": { "jiti": "bin/jiti.js" } @@ -2694,7 +2691,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -2916,7 +2912,6 @@ "resolved": "https://registry.npmjs.org/react/-/react-19.2.6.tgz", "integrity": "sha512-sfWGGfavi0xr8Pg0sVsyHMAOziVYKgPLNrS7ig+ivMNb3wbCBw3KxtflsGBAwD3gYQlE/AEZsTLgToRrSCjb0Q==", "license": "MIT", - "peer": true, "engines": { "node": ">=0.10.0" } @@ -3405,7 +3400,6 @@ "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -3531,7 +3525,6 @@ "integrity": "sha512-2N/55r4JDJ4gdrCvGgINMy+HH3iRpNIz8K6SFwVsA+JbQScLiC+clmAxBgwiSPgcG9U15QmvqCGWzMbqda5zGQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", @@ -3625,7 +3618,6 @@ "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, diff --git a/parse_apis.cjs b/parse_apis.cjs new file mode 100644 index 0000000..5a3c67e --- /dev/null +++ b/parse_apis.cjs @@ -0,0 +1,182 @@ +const fs = require('fs'); +const path = require('path'); +const { existingTopics } = require('./original_topics.cjs'); + +const getApisContent = fs.readFileSync(path.join(__dirname, "Get Api's.txt"), 'utf8'); +const postApisContent = fs.readFileSync(path.join(__dirname, "Post, put and delete Api's.txt"), 'utf8'); + +const endpoints = []; + +// Parse GET APIs +let currentGet = null; +const getLines = getApisContent.split('\n'); +for (let line of getLines) { + const trimmed = line.trim(); + if (trimmed.startsWith('# ')) { + currentGet = { method: 'GET', description: trimmed.substring(2), name: trimmed.substring(2) }; + } else if (trimmed.startsWith('https://')) { + if (currentGet) { + currentGet.url = trimmed; + endpoints.push(currentGet); + currentGet = null; + } + } +} + +// Better Parse POST/PUT/DELETE APIs +const postLines = postApisContent.split('\n'); +let i = 0; + +while (i < postLines.length) { + let line = postLines[i].trim(); + + if (line.startsWith('POST ') || line.startsWith('PUT ') || line.startsWith('DELETE ')) { + const parts = line.split(' '); + const method = parts[0]; + const name = parts.slice(1).join(' '); + let url = ''; + let body = null; + let description = name; + + i++; + while (i < postLines.length) { + let l = postLines[i].trim(); + if (l.startsWith('POST ') || l.startsWith('PUT ') || l.startsWith('DELETE ') || l.startsWith('1. ') || l.startsWith('2. ') || l.startsWith('3. ') || l.startsWith('4. ') || l.startsWith('5. ') || l.startsWith('6. ') || l.startsWith('πŸ’»') || l.startsWith('πŸ“±')) { + break; // Next block starts + } + + if (l.startsWith('URL: ')) { + url = l.substring(5).trim(); + } else if (l.startsWith('Payload: (Uses the same schema')) { + description += ' ' + l; + } else if (l === 'json') { + // start capturing json + let jsonStr = ''; + i++; + while (i < postLines.length) { + let jl = postLines[i].trim(); + if (jl.startsWith('POST ') || jl.startsWith('PUT ') || jl.startsWith('DELETE ') || jl.startsWith('1. ') || jl.startsWith('2. ') || jl.startsWith('3. ') || jl.startsWith('4. ') || jl.startsWith('5. ') || jl.startsWith('6. ') || jl.startsWith('πŸ’»') || jl.startsWith('πŸ“±')) { + break; + } + jsonStr += postLines[i] + '\n'; + i++; + } + let rawBody = jsonStr.trim(); + try { + body = JSON.parse(rawBody); + } catch (e) { + body = rawBody; // fallback to string + } + continue; + } + i++; + } + + if (url) { + const ep = { method, name, description, url }; + if (body) { + ep.body = body; + } + endpoints.push(ep); + } + } else { + i++; + } +} + +// Second pass: Resolve "Uses the same schema as" +endpoints.forEach(ep => { + if (!ep.body && ep.description.includes('Uses the same schema as')) { + const match = ep.description.match(/Uses the same schema as(.*?)\)/); + if (match) { + let refText = match[1].trim().toLowerCase(); + // Try to find the referenced endpoint + let refEp = endpoints.find(e => { + if (!e.body) return false; + let eName = e.name.toLowerCase(); + // Remove 'web ' or 'mobile ' for looser matching + refText = refText.replace('web ', '').replace('mobile ', ''); + eName = eName.replace('web ', '').replace('mobile ', ''); + return refText.includes(eName) || eName.includes(refText); + }); + if (refEp) { + ep.body = refEp.body; + } + } + } +}); + +// Map to topics +const topicMapping = { + 'utils': ['utils', 'config', 'category'], + 'users': ['user'], + 'partners': ['partner', 'rider'], + 'tenants': ['tenant', 'location'], + 'customers': ['customer'], + 'deliveries': ['deliver'], + 'orders': ['order'], + 'products': ['product', 'catalog', 'stock'], + 'invoice': ['invoice'], + 'payments': ['payment'] +}; + +const mapToTopic = (url) => { + const urlLower = url.toLowerCase(); + for (const [topicId, keywords] of Object.entries(topicMapping)) { + if (urlLower.includes('/' + topicId + '/')) return topicId; + for (const kw of keywords) { + if (urlLower.includes('/' + kw)) return topicId; + } + } + return 'utils'; +}; + +const restTopicsMap = { + utils: { id: 'utils', name: 'Utils', description: 'Shared lookup endpoints roles, locations, configs, app types, pricing.', endpoints: [] }, + users: { id: 'users', name: 'Users', description: 'Manage users and roles across the Xpress platform.', endpoints: [] }, + partners: { id: 'partners', name: 'Partners', description: 'Partners, riders, locations, shifts, and rider pricing.', endpoints: [] }, + tenants: { id: 'tenants', name: 'Tenants', description: 'Tenant accounts info, locations, customers, orders, pricing, summary.', endpoints: [] }, + customers: { id: 'customers', name: 'Customers', description: 'Customer accounts, lookups, and search.', endpoints: [] }, + deliveries: { id: 'deliveries', name: 'Deliveries', description: 'Delivery records, queues, and rider-delivery joins.', endpoints: [] }, + orders: { id: 'orders', name: 'Orders', description: 'Order records, details, and aggregate summaries.', endpoints: [] }, + products: { id: 'products', name: 'Products', description: 'Product catalog, categories, and subcategories.', endpoints: [] }, + invoice: { id: 'invoice', name: 'Invoice', description: 'Invoice insights and billing analytics.', endpoints: [] }, + payments: { id: 'payments', name: 'Payments', description: 'Payment requests and settlements.', endpoints: [] } +}; + +endpoints.forEach(ep => { + let parsedUrl; + try { + parsedUrl = new URL(ep.url); + } catch (e) { + return; + } + const fullPath = parsedUrl.pathname + parsedUrl.search; + const topicId = mapToTopic(fullPath); + + if (restTopicsMap[topicId]) { + const newEp = { + name: ep.name, + method: ep.method, + url: fullPath, + description: ep.description + }; + if (ep.body) { + newEp.body = ep.body; + } + restTopicsMap[topicId].endpoints.push(newEp); + } +}); + +const restTopics = Object.values(restTopicsMap); + +const out = `export const LEGACY_BASE_URL = 'https://api.workolik.com'; +export const REST_BASE_URL = 'https://fiesta.nearle.app'; + +export const legacyTopics = ${JSON.stringify(existingTopics, null, 2)}; + +export const restTopics = ${JSON.stringify(restTopics, null, 2)}; +`; + +fs.writeFileSync(path.join(__dirname, 'src', 'data', 'topics.js'), out, 'utf8'); +console.log('Successfully updated topics.js with ' + endpoints.length + ' parsed endpoints (fixed payload parsing).'); diff --git a/server.js b/server.js index e04973f..0312892 100644 --- a/server.js +++ b/server.js @@ -19,7 +19,8 @@ import 'dotenv/config' const __dirname = path.dirname(fileURLToPath(import.meta.url)) const PORT = Number(process.env.PORT) || 3000 const SECRET = (process.env.HASURA_ADMIN_SECRET || '').trim() -const TARGET = 'https://api.workolik.com' +const TARGET_LEGACY = 'https://api.workolik.com' +const TARGET_REST = 'https://fiesta.nearle.app' if (!SECRET) { console.warn('[xpress-docs] WARNING: HASURA_ADMIN_SECRET is not set. Proxied API calls will be sent without auth.') @@ -27,13 +28,9 @@ if (!SECRET) { const app = express() -app.use('/api', createProxyMiddleware({ - target: TARGET, +const commonProxyOptions = { changeOrigin: true, secure: true, - // The mount strips '/api' from req.url; add it back so the target URL - // stays /api/rest/... - pathRewrite: (p) => '/api' + p, on: { proxyReq: (proxyReq) => { if (SECRET) proxyReq.setHeader('x-hasura-admin-secret', SECRET) @@ -46,6 +43,18 @@ app.use('/api', createProxyMiddleware({ res.end(JSON.stringify({ error: 'proxy_error', message: err.message })) } } +} + +app.use('/api', createProxyMiddleware({ + ...commonProxyOptions, + target: TARGET_LEGACY, + pathRewrite: (p) => '/api' + p +})) + +app.use('/live', createProxyMiddleware({ + ...commonProxyOptions, + target: TARGET_REST, + pathRewrite: (p) => '/live' + p })) // Built React app @@ -57,6 +66,7 @@ app.get('*', (_req, res) => { app.listen(PORT, () => { console.log(`[xpress-docs] listening on http://localhost:${PORT}`) - console.log(`[xpress-docs] proxying /api/* -> ${TARGET}/api/*`) + console.log(`[xpress-docs] proxying /api/* -> ${TARGET_LEGACY}/api/*`) + console.log(`[xpress-docs] proxying /live/* -> ${TARGET_REST}/live/*`) console.log(`[xpress-docs] admin secret: ${SECRET ? 'loaded' : 'NOT SET'}`) }) diff --git a/src/App.jsx b/src/App.jsx index 2913cb8..e2dbf5a 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -2,39 +2,67 @@ import { useMemo, useState } from 'react' import Sidebar from './components/Sidebar' import Introduction from './components/Introduction' import TopicView from './components/TopicView' -import { topics } from './data/topics' +import { legacyTopics, restTopics, LEGACY_BASE_URL, REST_BASE_URL } from './data/topics' + +const processTopics = (topics, baseUrl, type) => + topics.map(t => ({ ...t, baseUrl, type, uniqueId: `${type}-${t.id}` })) + +const allLegacy = processTopics(legacyTopics, LEGACY_BASE_URL, 'legacy') +const allRest = processTopics(restTopics, REST_BASE_URL, 'rest') + +function filterTopics(topics, q) { + if (!q) return topics + return topics.filter((t) => + t.name.toLowerCase().includes(q) || + (t.description || '').toLowerCase().includes(q) || + t.endpoints.some((e) => + e.name.toLowerCase().includes(q) || + e.url.toLowerCase().includes(q) || + (e.description || '').toLowerCase().includes(q) + ) + ) +} export default function App() { const [activeTopic, setActiveTopic] = useState(null) const [searchQuery, setSearchQuery] = useState('') - const visibleTopics = useMemo(() => { - const q = searchQuery.trim().toLowerCase() - if (!q) return topics - return topics.filter((t) => - t.name.toLowerCase().includes(q) || - (t.description || '').toLowerCase().includes(q) || - t.endpoints.some((e) => - e.name.toLowerCase().includes(q) || - e.url.toLowerCase().includes(q) || - (e.description || '').toLowerCase().includes(q) - ) - ) - }, [searchQuery]) + const visibleLegacy = useMemo(() => filterTopics(allLegacy, searchQuery.trim().toLowerCase()), [searchQuery]) + const visibleRest = useMemo(() => filterTopics(allRest, searchQuery.trim().toLowerCase()), [searchQuery]) return ( -
+
+ + {/* Ambient background glows */} +
+
+
+ -
+ +
{activeTopic - ? - : } + ? ( +
+ +
+ ) + : ( +
+ +
+ )}
) diff --git a/src/components/EndpointCard.jsx b/src/components/EndpointCard.jsx index db67e06..779b270 100644 --- a/src/components/EndpointCard.jsx +++ b/src/components/EndpointCard.jsx @@ -1,8 +1,7 @@ import { useMemo, useState } from 'react' import { - Play, Copy, Check, CheckCircle2, XCircle, Loader2 + Play, Copy, Check, CheckCircle2, AlertCircle, Server, FileJson, Loader2 } from 'lucide-react' -import { BASE_URL } from '../data/topics' import { highlightJSON } from '../lib/highlight' function safeDecode(v) { @@ -21,32 +20,25 @@ function parseUrl(url) { return { path, params } } -export default function EndpointCard({ - endpoint, - onSend, - result, - loading -}) { +export default function EndpointCard({ endpoint, baseUrl, onSend, result, loading }) { const { path, params: parsedParams } = useMemo(() => parseUrl(endpoint.url), [endpoint.url]) const paramDefs = endpoint.params || parsedParams const [values, setValues] = useState(() => Object.fromEntries(paramDefs.map((p) => [p.name, p.default ?? ''])) ) - const [copied, setCopied] = useState(false) + const [copied, setCopied] = useState(false) const [urlCopied, setUrlCopied] = useState(false) const composedUrl = useMemo(() => { - if (paramDefs.length === 0) return BASE_URL + path + if (paramDefs.length === 0) return baseUrl + path const qs = paramDefs .map((p) => `${p.name}=${encodeURIComponent(values[p.name] ?? '')}`) .join('&') - return `${BASE_URL}${path}?${qs}` - }, [path, paramDefs, values]) + return `${baseUrl}${path}?${qs}` + }, [path, paramDefs, values, baseUrl]) - const handleSend = () => { - onSend(endpoint, composedUrl) - } + const handleSend = () => onSend(endpoint, composedUrl) const copyResponse = async () => { if (!result || result.kind !== 'response') return @@ -66,160 +58,205 @@ export default function EndpointCard({ } catch {} } - return ( -
- {/* Header */} -
-
- - {endpoint.method} - -

{endpoint.name}

-
- {endpoint.description && ( -

{endpoint.description}

- )} -
+ const methodColor = { + GET: 'bg-emerald-100/50 text-emerald-700 border-emerald-200', + POST: 'bg-blue-100/50 text-blue-700 border-blue-200', + PUT: 'bg-amber-100/50 text-amber-700 border-amber-200', + DELETE: 'bg-red-100/50 text-red-700 border-red-200', + PATCH: 'bg-purple-100/50 text-purple-700 border-purple-200', + }[endpoint.method] || 'bg-slate-100/50 text-slate-700 border-slate-200' - {/* Live URL bar */} -
-
-
Request URL
- -
-
- {endpoint.method}{' '} - {BASE_URL} - {path} + const isOk = result?.kind === 'response' && result.ok + const isErr = result?.kind === 'response' && !result.ok + const isNet = result?.kind === 'network-error' + + return ( +
+
+ + {/* Left Column: Description & Parameters */} +
+
+
+ + {endpoint.method} + +

+ {endpoint.name} +

+
+ {endpoint.description && ( +

{endpoint.description}

+ )} +
+ + {/* URL bar */} +
+ + {baseUrl} + {path} +
+ + {/* Query Parameters */} {paramDefs.length > 0 && ( - <> - ? - {paramDefs.map((p, i) => ( - - {p.name} - = - {encodeURIComponent(values[p.name] ?? '')} - {i < paramDefs.length - 1 && &} - - ))} - - )} -
-
- - {/* Query Parameters */} - {paramDefs.length > 0 && ( -
-
- Query Parameters -
-
- {paramDefs.map((p) => ( -
-
- - - {p.type || 'STRING'} - -
- setValues((v) => ({ ...v, [p.name]: e.target.value }))} - className="mono text-sm px-3 py-1.5 w-full bg-white border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-brand-500/20 focus:border-brand-500 transition-all" - placeholder={p.default || ''} - /> +
+

+ Query Parameters +

+
+ {paramDefs.map((p) => ( +
+ + + setValues((v) => ({ ...v, [p.name]: e.target.value })) + } + /> +
+ ))}
- ))} -
-
- )} - - {/* Send button */} -
- -
- - {/* Response panel β€” only visible on the active endpoint */} - {result && } -
- ) -} - -function ResponsePanel({ result, onCopy, copied }) { - const isOk = result.kind === 'response' && result.ok - const isError = result.kind === 'response' && !result.ok - const isNet = result.kind === 'network-error' - const status = result.status - const headerBg = '#161922' - const bodyBg = '#0f1117' - - const StatusBadge = () => { - if (isOk) return ( - - {status} - - ) - if (isError) return ( - - {status} - - ) - if (isNet) return ( - - NETWORK - - ) - return null - } - - const body = isNet - ? { error: 'Network error', message: result.message } - : result.body - - const isString = typeof body === 'string' - - return ( -
-
-
- - Response - {result.ms != null && ( - {result.ms} ms +
)}
- + + {/* Right Column: Execution & Response */} +
+
+ + {/* macOS window dots */} +
+
+
+
+
Request Payload
+
+ + {/* Payload body or composed URL preview */} + {endpoint.body ? ( +
+
+ Request Body (JSON) +
+
+                  
+                
+
+ ) : ( +
+
+ Full Request URL +
+
+ {endpoint.method} + {baseUrl} + {path} + {paramDefs.length > 0 && ( + <> + ? + {paramDefs.map((p, i) => ( + + {p.name} + = + {encodeURIComponent(values[p.name] ?? '')} + {i < paramDefs.length - 1 && &} + + ))} + + )} +
+
+ )} + + {/* Execution Bar */} +
+
+ + Ready + + +
+ +
+ + {/* Response */} + {result && ( +
+
+ Response JSON +
+ {result.ms != null && ( + {result.ms} ms + )} + {isOk && ( + + {result.status} + + )} + {isErr && ( + + {result.status} + + )} + {isNet && ( + + NETWORK ERR + + )} + {result.kind === 'response' && ( + + )} +
+
+ {isNet ? ( +
+ Network error: {result.message} +
+ ) : typeof result.body === 'string' ? ( +
{result.body}
+ ) : ( +
+                )}
+              
+ )} + +
+
+
-
-        {isString
-          ? {body}
-          : }
-      
) } diff --git a/src/components/Introduction.jsx b/src/components/Introduction.jsx index 2b20bc5..d64e7c1 100644 --- a/src/components/Introduction.jsx +++ b/src/components/Introduction.jsx @@ -1,60 +1,99 @@ -import { Zap, BookOpen, Code2, Server } from 'lucide-react' -import { BASE_URL, topics } from '../data/topics' +import { ArrowRight, Code2, Zap, Truck, Users, Building2, Package, ShoppingCart, CreditCard, FileText, UserCircle, Bike, Wrench } from 'lucide-react' +import { LEGACY_BASE_URL, REST_BASE_URL } from '../data/topics' -export default function Introduction() { - const endpointCount = topics.reduce((n, t) => n + t.endpoints.length, 0) +const topicIcons = { + utils: Wrench, + users: Users, + partners: Bike, + tenants: Building2, + customers: UserCircle, + deliveries: Truck, + orders: ShoppingCart, + products: Package, + invoice: FileText, + payments: CreditCard, +} + +export default function Introduction({ allLegacy, allRest, setActiveTopic }) { + const allTopics = [...allLegacy, ...allRest] return ( -
-
-
- -
-
-

EXpress Developer Docs

-

Reference for the EXpress REST API.

+
+ +
+ + v1.0 Developer API +
+ +

+ Nearle Express API +

+ +

+ A comprehensive platform for managing tenants, users, partners, customers, + orders, deliveries, products, invoices, and payments across the Express network. +

+ +
+

Base URLs

+
+
+
Hasura API
+ {LEGACY_BASE_URL} +
+
+
REST API
+ {REST_BASE_URL} +
-

- Welcome to the EXpress API documentation. The EXpress API exposes the operations - you need to manage tenants, users, partners, customers, orders, deliveries, - products, invoices, and payments across the platform. -

+
+ {allTopics.map((topic, index) => { + const Icon = topicIcons[topic.id] || Zap + return ( +
setActiveTopic(topic)} + className="group relative bg-white p-6 rounded-2xl border border-slate-200/60 shadow-sm hover:shadow-xl hover:shadow-brand-500/5 hover:border-brand-200 transition-all duration-300 cursor-pointer overflow-hidden" + style={{ animationDelay: `${index * 100}ms` }} + > +
-
-
-
- Topics -
-
{topics.length}
-
-
-
- Endpoints -
-
{endpointCount}
-
-
-
- Style -
-
REST
-
+
+

+
+ +
+ {topic.name} + +

+
+ + {topic.type === 'legacy' ? 'Hasura' : 'REST'} + + {topic.endpoints.length} endpoint{topic.endpoints.length !== 1 ? 's' : ''} +
+

+ {topic.description} +

+
+
+ ) + })}
-

Base URL

-
-        {BASE_URL}
-      
+
+ +

+ Explore interactive endpoints for both Hasura and REST APIs. All requests route through the dev proxy which injects authentication headers securely. +

+
-

Reference layout

-

- Endpoints are grouped in the sidebar by their URL segment after - {' '}/api/rest/xpress/. - Pick a topic on the left to see all endpoints under it, each with its method, - full URL, and a short description. -

) } diff --git a/src/components/Sidebar.jsx b/src/components/Sidebar.jsx index 357c162..150fe0c 100644 --- a/src/components/Sidebar.jsx +++ b/src/components/Sidebar.jsx @@ -1,19 +1,58 @@ import { useState } from 'react' -import { Search, Zap, ChevronDown, ChevronRight, Terminal } from 'lucide-react' +import { Search, ChevronRight, ChevronDown, Layers, Terminal } from 'lucide-react' import { getTopicIcon } from '../lib/icons' -export default function Sidebar({ topics, activeTopic, setActiveTopic, searchQuery, setSearchQuery }) { - const [open, setOpen] = useState({ general: true, topics: true }) +export default function Sidebar({ legacyTopics, restTopics, activeTopic, setActiveTopic, searchQuery, setSearchQuery }) { + const [open, setOpen] = useState({ general: true, legacy: true, rest: true }) const toggle = (k) => setOpen((s) => ({ ...s, [k]: !s[k] })) + const renderTopicGroup = (topics, title, key) => ( +
+ +
+ {topics.map((t) => { + const isActive = activeTopic?.uniqueId === t.uniqueId + const Icon = getTopicIcon(t.id) + return ( + + ) + })} +
+
+ ) + return (
+ + {/* Brand Header */}
- +
- EXpress + Nearle Express
@@ -29,67 +68,34 @@ export default function Sidebar({ topics, activeTopic, setActiveTopic, searchQue
diff --git a/src/components/TopicView.jsx b/src/components/TopicView.jsx index bebe022..f838ab8 100644 --- a/src/components/TopicView.jsx +++ b/src/components/TopicView.jsx @@ -1,12 +1,15 @@ import { useEffect, useRef, useState } from 'react' import EndpointCard from './EndpointCard' import { getTopicIcon } from '../lib/icons' -import { BASE_URL } from '../data/topics' -// Strip the documented BASE_URL so fetches are same-origin and get -// proxied by Vite (dev) or Express (prod), which injects the secret header. -function toProxyPath(fullUrl) { - if (fullUrl.startsWith(BASE_URL)) return fullUrl.slice(BASE_URL.length) +import { LEGACY_BASE_URL } from '../data/topics' + +function toProxyPath(fullUrl, baseUrl) { + // Only proxy the legacy Hasura API (to inject the admin secret via server) + if (baseUrl === LEGACY_BASE_URL && fullUrl.startsWith(baseUrl)) { + return fullUrl.slice(baseUrl.length) + } + // Let the browser hit the new REST API directly (it supports CORS) return fullUrl } @@ -21,8 +24,6 @@ export default function TopicView({ topic, searchQuery }) { ) : topic.endpoints - // Only one endpoint's response is visible at a time. Sending on a new - // endpoint replaces the previous one (and cancels its in-flight fetch). const [active, setActive] = useState(null) const abortRef = useRef(null) @@ -41,12 +42,28 @@ export default function TopicView({ topic, searchQuery }) { setActive({ name: endpoint.name, result: null, loading: true }) const start = Date.now() + try { - const res = await fetch(toProxyPath(composedUrl), { + const fetchOptions = { method: endpoint.method, headers: { 'Content-Type': 'application/json' }, signal: controller.signal - }) + } + + if (['POST', 'PUT', 'PATCH'].includes(endpoint.method) && endpoint.body) { + if (typeof endpoint.body === 'string') { + try { + // Re-stringify in case it's a badly formatted JSON string, but mostly it's just raw string + fetchOptions.body = JSON.stringify(JSON.parse(endpoint.body)) + } catch { + fetchOptions.body = endpoint.body + } + } else { + fetchOptions.body = JSON.stringify(endpoint.body) + } + } + + const res = await fetch(toProxyPath(composedUrl, topic.baseUrl), fetchOptions) const ms = Date.now() - start const text = await res.text() let body @@ -71,23 +88,26 @@ export default function TopicView({ topic, searchQuery }) { } return ( -
-
-
- -
-
-

{topic.name}

-

{topic.description}

-
- {filtered.length} endpoint{filtered.length === 1 ? '' : 's'} -
+
+
+

{topic.name}

+

{topic.description}

+
+ + + {topic.type === 'legacy' ? 'Hasura API' : 'REST API'} + + {filtered.length} endpoint{filtered.length !== 1 ? 's' : ''}
{filtered.length === 0 ? ( -
+
No endpoints match "{searchQuery}".
) : ( @@ -95,8 +115,9 @@ export default function TopicView({ topic, searchQuery }) { const isActive = active?.name === e.name return ( { if (secret) proxyReq.setHeader('x-hasura-admin-secret', secret) }) } + }, + '/live': { + target: 'https://fiesta.nearle.app', + changeOrigin: true, + secure: true, + configure: (proxy) => { + proxy.on('proxyReq', (proxyReq) => { + if (secret) proxyReq.setHeader('x-hasura-admin-secret', secret) + }) + } } } }