v1.0.0 Open App

Saleman WMS

The complete Smart Warehouse Management System for Uzum and Yandex marketplace sellers. Manage your inventory in real-time, visualize your warehouse in 3D, sync orders automatically, and control every role in your team.

Three.js 3D Warehouse Firebase Realtime Uzum API RBAC Roles AI Features 3 Languages
Platform Overview

Saleman is a full-stack web application that runs entirely in the browser. It uses Firebase Firestore for persistent cloud storage, Firebase Auth for secure login, and the Uzum Seller API (routed through a Netlify proxy to avoid CORS) for marketplace synchronization.

Real-time Dashboard

Live KPIs, customizable widgets, Charts.js graphs, AI insights, and store-level filtering.

3D Warehouse Viewer

Drag-and-drop rack placement using Three.js. Full undo/redo stack, marquee selection, and AI layout commands.

Marketplace Sync

One-click sync of products, orders, invoices, and reviews from connected Uzum accounts.

Worker Management

Role-based access control. Create worker accounts, set permissions, and manage remote sessions.

Print Center

Generate and print barcodes and product labels with QR code support for picked items.

Multi-Language

Full UI translation in English, Uzbek (O'zbek), and Russian (Русский).


Quick Start

Get the app running locally in under 2 minutes using Netlify Dev (required for the Uzum API proxy).

1

Install Netlify CLI

Required to run the /uzum-proxy redirect locally. Without it, all Uzum API calls will fail with CORS errors.

2

Clone and open the project

The project is a single-page application. No build step. Open the folder in VS Code.

3

Run netlify dev

Starts a local dev server at http://localhost:8888 which activates the proxy. The app auto-detects whether it is on localhost and routes Uzum API calls accordingly.

4

Log in and connect Uzum

Create a Firebase account via the register page. Go to IntegrationsConnect New Store and paste your Bearer API key from Uzum Seller Panel.

5

Sync products

Click the Sync Uzum button in the Products tab to pull all SKUs and stock levels from your connected Uzum shops into Firestore.

Localhost Only: The proxy auto-detects localhost and routes to http://localhost:8888/uzum-proxy. On production (saleman.uz), it routes to /uzum-proxy via Netlify redirects.

Authentication

Authentication is handled entirely by Firebase Auth. The app supports email/password, Google Sign-In, and a special username-based login path for worker accounts.

Email / Password

Standard Firebase Auth flow with email verification required before app access.

Google Sign-In

One-click OAuth via firebase.auth.GoogleAuthProvider. Skips email verification.

Worker Username Login

Workers log in using a short username (e.g. john.doe). The app looks up their email in the public workerUsernames Firestore collection and then authenticates via Firebase.

Worker Login Flow: When a manager clicks Login on a worker row, a one-time token (email + password + timestamp, valid 8 s) is written to localStorage as warevis_worker_login_pending. A new tab is opened and auth.js picks up the token in onAuthStateChanged, signs in the worker, then deletes the token.
Firestore Documents Written on Worker Creation
PathPurpose
users/{workerUid}Worker's own profile — readable by the worker. Contains isWorker:true, managerId, role.
users/{managerId}/workers/{workerUid}Required by Firestore security rules. Document ID must be the Auth UID.
workerUsernames/{username}Public index used for username → email lookup during login. Contains only email + managerId, no passwords.

Roles & Permissions (RBAC)

Every user session has a role property. The RBAC system controls which navigation tabs are visible, which actions are available, and whether Uzum API calls are triggered. RBAC is enforced twice: immediately on login and again 800 ms later to catch any async renders that tried to add hidden elements back.

Role Dashboard Products Orders Finance Workers Integrations Racks 3D
Manager
Warehouse Worker
Accountant
Logistics / Driver
Print Manager
Worker API isolation: When currentUser.isWorker === true, initializeApplication() skips loadWorkers(), loadUzumAccounts(), and all Uzum API calls entirely. Workers only receive their own role-limited product data via getUserProductsRef().

Dashboard

The dashboard is the home screen for managers. It displays a fully customizable grid of widgets showing live KPIs, charts, and summaries. The layout is persisted to Firestore and remembered between sessions.

Screenshot: Dashboard Overview Place a full-width screenshot of the dashboard with all widgets visible here.
Available Widgets
Widget IDNameCategoryDescription
kpi-total-productsTotal ProductsInventoryCount of all products in Firestore.
kpi-low-stockLow Stock ItemsInventoryProducts with stock below the threshold.
kpi-orders-todayOrders TodayOrdersNumber of orders placed today.
revenue-chartRevenue Over TimeFinancialLine chart of revenue from synced Uzum orders.
top-productsTop 10 ProductsSalesBar chart of best-selling products by revenue. Filterable by Uzum account.
store-sales-barStore SalesSalesGrouped bar chart comparing sales across connected Uzum stores.
revenue-forecastStock by CategoryInventoryHorizontal bar chart (Chart.js) of stock levels grouped by category. Uses quantityFbs as the primary stock field.
api-statusAPI StatusSystemShows which Uzum accounts are connected and their shop list.
notesNotesUtilityFreeform text notes saved to Firestore per user.
ai-insightAI InsightsAIGPT-powered text summary of inventory status and recommendations.
Customizing the Layout: Go to Settings → Edit Dashboard to open the drag-and-drop widget editor. Widgets can be toggled, reordered, and saved. The layout is stored in users/{uid}/dashboardLayout in Firestore.
Screenshot: Dashboard Editor Modal Show the widget configuration/reorder modal here.

Products

The Products tab is the central inventory view. It supports local products added manually, and Uzum products synced from the marketplace API. Both product types are stored in separate Firestore sub-collections and rendered in separate table views.

Configurable Columns

Managers can show/hide columns via the Column Settings modal. Preferences are saved to Firestore.

Uzum Products View

Expandable rows show SKU-level data including price, FBS stock, images (540px high-res), status badges, and promo tags.

SKU Details Panel

Full details panel per SKU: barcode, IKPU, all stock quantities (Active, FBS, Sold, Returned, etc.), dimensions, rating, commissions, and blocking reasons.

Archived Toggle

Show/hide archived products. Archived products are filtered from the main view by default.

Search & Filter

Real-time search by name, SKU ID, or barcode. Filter by category, status, and Uzum account.

Sync Uzum

Fetches all shops for each connected API key, then imports all products and SKUs into Firestore's products_uzum collection. Stale locks and blacklists are cleared before each sync.

Screenshot: Products Table — Uzum View Show the expanded Uzum product rows with SKU details visible.
Product Image Quality

All product images use the Uzum CDN with the t_product_540_high.jpg suffix for full resolution. Clicking any image opens a full-screen lightbox (z-index: 999999, always rendering above all other panels).

Image URL Pattern const base = url.replace(/\/t_product_[^/]+$/, '');
const highRes = base + '/t_product_540_high.jpg';
Stock Quantity Priority Chain
For FBS stock, Saleman uses this fallback chain: quantityFbs ?? p.fbs ?? quantityActive ?? quantity. This is because quantityActive is often 0 for Uzum FBS sellers even when FBS stock exists.

Stock Management

The Stock tab provides a search-driven interface for updating inventory levels manually. Results appear as cards as you type — enter a product name, SKU, or barcode to find and adjust quantities.

Screenshot: Stock Management Search Show the search bar and product result cards with quantity controls.
FunctionDescription
window.renderStockSearch(query)Called on every keypress in the stock search input. Filters local products and renders result cards.
window.loadProducts()Loads all products from Firestore products_local collection into window.products.
window.saveStockChange(id, qty)Writes the updated quantity to Firestore and logs the change to the activity log.

Orders

The Orders tab shows all orders pulled from Uzum. Orders are organized by fulfilment type and support pagination.

All Orders

Full order list across all connected Uzum accounts, newest first.

FBO Orders

Fulfilled-by-Operator orders stored in Uzum's warehouse. Filter to see only FBO fulfilment.

Returns

Returned orders with reason codes, amount, and return date.

Pagination

Configurable rows-per-page (default 20). Page buttons are rendered dynamically. Resets to page 1 on each new sync.

Screenshot: Orders Tab with Pagination Show the orders list, tab switcher (All/FBO/FBS/DBS/Returns), and pagination controls.
FunctionDescription
window.syncOrders()Fetches all orders from the Uzum API for each connected account and caches them.
window.syncReturns()Fetches return orders specifically.
window.switchOrderTab(tab)Switches between 'all', 'FBO', 'returns' views.
window.changeOrdersPage(n)Navigates to page n. Re-slices the cached order array.
window.updateOrdersPerPage(n)Updates rows per page and re-renders from page 1.
window.renderOrdersPagination(total)Injects page number buttons into #ordersPaginationButtons.

Schedule / Delivery

The Schedule tab lets you plan and record product deliveries by vehicle. Select a vehicle, product, quantity, and date/time to create a delivery entry. Scheduled deliveries appear in a timeline view.

Screenshot: Schedule — Delivery Form Show the delivery creation form and timeline list.
Vehicles are managed in the Settings page. Each vehicle has a name, type, and capacity. The vehicle list is loaded into window.vehicles and injected into the schedule form dropdown.

Recommendations

The Recommendations module will surface AI-driven and rule-based suggestions for restocking, promotions, and warehouse optimization. This feature is currently in active development.

Coming Soon: The Recommendations page scaffolding is in place. Full logic will be published in a future release.

Racks & Warehouse Layout

The Racks tab is where you visualize and configure your physical warehouse in 3D. A 5-step setup wizard guides you through defining the area, assigning zones, boxes, and creating/placing racks.

Screenshot: 3D Warehouse View — Racks Placed Show the Three.js 3D scene with racks, floor grid, and toolbar visible.
Setup Wizard (5 Steps)
1

Area — Define Warehouse Size

Enter your warehouse dimensions in square meters (50–150 m² small, 150–500 m² medium, 500+ m² large). The 3D floor grid is scaled to match.

2

Assign — Zone Configuration

Define named zones within the warehouse (e.g. Inbound, Outbound, Storage). Used for product-to-zone assignment later.

3

Boxes — Box Type Setup

Configure box/bin types with size presets for accurate rack capacity calculations.

4

Racks — Rack Template Editor

Set shelf counts, dimensions, and visual color palette for rack types. A live 3D miniature preview renders in the side panel.

5

Launch — Generate 3D Scene

Generates the full Three.js warehouse scene. The layout is persisted to Firestore under users/{uid}/warehouseLayout.

Screenshot: Wizard Step View Show the 5-step wizard with progress pills at the top.

3D Engine — vision.js

vision.js is the "Eyes" module of Saleman — it owns all Three.js scene logic. It is loaded as an ES module and exports key functions used by app.js and the rack setup wizard.

Drag & Drop Racks

Racks can be freely dragged on the warehouse floor. Collision detection prevents overlapping placements.

Undo / Redo Stack

Full undo/redo history for all rack moves, additions, and deletions. Supports Ctrl+Z / Ctrl+Y keyboard shortcuts.

Multi-Select & Marquee

Hold Ctrl and click to add racks to selection. Draw a rectangle to marquee-select. Move selected racks as a group.

Grid Picker

Enters a crosshair mode to precisely place new racks at a grid-snapped position by clicking the floor.

Color Palette

Rack metal, shelf outer, shelf inner, and floor colors are configurable. Defaults are set to indigo metal with orange shelves.

Fullscreen Mode

The 3D viewport can expand to fill the browser window with a floating toolbar, close button, and maximize toggle.

Exported Functions
export functioninitWarehouse3D()
export functiongenerateWarehouse(config)
export functioncreateRack(options)
export functioninitRackPreview()
export functiongenerateRackPreview(cfg)
export functionsetBoxesVisibility(v)
export functioncreateBoxMaterial()
export functiongetRackColor(type)
export functioninitDragHelpers()
export functiondoUndo()
export functiondoRedo()
export functionsetSelectedRacks(arr)
export functionisCollidingAt(pos)
export functionapplyAIDesignCommand(cmd)
export functionshowAILoading()
export functionhideAILoading()
export functionsetGridPickerMode(active, cb)
Scene Architecture
VariableTypePurpose
sceneTHREE.SceneMain warehouse scene (viewer)
previewSceneTHREE.SceneIsolated rack preview (Setup Wizard Step 4)
cameraTHREE.PerspectiveCameraMain scene camera with OrbitControls
rendererTHREE.WebGLRendererMain WebGL renderer — anti-aliasing enabled
undoStack / redoStackArray<Action>Undo/redo history for rack operations
RACK_COLORSwindow objectCurrent active color palette applied to all newly created racks

3D Models — 3dmodels.js

3dmodels.js provides procedurally generated Three.js meshes for warehouse structures. All textures are created in-memory using Canvas API — no external image files needed.

Warehouse Door

Metal-look door with horizontal slat texture and caution stripe at the base. Procedurally generated texture (512×512 Canvas). Exposed as window.createWarehouseDoor(x,y,z,scene).

Storage Racks

Multi-shelf rack meshes with customizable shelf count, width, depth, and height. Metal uprights and shelf panels rendered as BoxGeometries.

Floor Grid

Dynamic grid helper sized to the warehouse area. Grid scale responds to the area input in the Setup Wizard.

Product Boxes

Small box meshes placed on rack shelves to represent stocked items. Togglable via setBoxesVisibility().

Warehouse Door — canvas texture generation function createDoorTexture() {
  const canvas = document.createElement('canvas');
  canvas.width = 512; canvas.height = 512;
  // 1. Base metal colour (grey)
  // 2. Horizontal slat shadows every 25 px
  // 3. Yellow + black caution stripes at bottom 80 px
  return new THREE.CanvasTexture(canvas);
}
Screenshot: 3D Models in Scene Show the warehouse door, racks, and boxes rendered by Three.js.

AI Design Commands

Managers can type natural-language commands into the AI Design prompt within the Racks view. The AI interprets the command and rearranges, adds, or removes racks in the 3D scene automatically.

Example commands: "Add 10 racks along the left wall", "Group small racks near the entrance", "Clear zone B and replace with 5 large racks".
FunctionDescription
applyAIDesignCommand(cmd)Parses the command string and applies corresponding Three.js scene mutations. Uses the undo stack so actions can be reversed.
showAILoading()Displays a loading overlay while the AI command is being processed.
hideAILoading()Hides the loading overlay on completion or error.

Marketplace Integrations

The Integrations tab lists all connected Uzum seller accounts. Each account is identified by a Bearer API key. Multiple accounts (shops) can be connected simultaneously.

1

Get your API key

Log in to the Uzum Seller Portal → Account Settings → API Key. Copy the Bearer token.

2

Click "Connect New Store"

In the Integrations tab, click the button and paste your Bearer token. The system calls fetchUzumShops() to verify the key and retrieve the shop list.

3

Save and sync

The account is stored in users/{uid}/uzumAccounts. Navigate to Products → Sync Uzum to import all SKUs.

Screenshot: Integrations Page — Connected Stores Show a connected Uzum account card with shop name and status indicator.

Uzum Marketplace API

All Uzum API calls are routed through a Netlify redirect to avoid browser CORS restrictions. The proxy base URL is determined at runtime.

Proxy base — window.__uzumProxyBase() window.__uzumProxyBase = function () {
  const h = window.location.hostname;
  if (h === 'localhost' || h === '127.0.0.1')
    return 'http://localhost:8888/uzum-proxy';
  return '/uzum-proxy';
};
Key Endpoints Used
GET
/seller/shop
Returns the list of shops associated with the authenticated API key. Called by fetchUzumShops().
GET
/v1/product/shop/{shopId}?...
Paginated product list for a shop. Called per-page by importUzumProducts(key).
GET
/v1/posting/fbo/list
FBO orders list. Called by syncOrders().
GET
/v1/review/list
Product reviews. Called by fetchUzumReviews(apiKey).
Sync Anti-Duplication: Before each sync, Saleman clears window.__uzumSyncInProgress, window.__uzumShopScanLocks, window.uzumBlacklist, and the uzum_blacklist localStorage key. This ensures stale locks from a previous crashed sync never silently block subsequent syncs.

Invoices (Timeslots)

The Invoice tab shows Uzum fulfilment invoices (internally called "timeslots"). They are fetched automatically at startup after accounts load, and can be viewed, filtered, and exported.

Invoices tab is RBAC-controlled. Visible to: Manager and roles that include timeslots in their allowed pages list.
Screenshot: Invoices / Timeslots Tab Show the invoice list with columns for shop, date, status, and amount.

Finance

The Finance tab provides a financial overview of revenue, expenses, and profit margins based on order and invoice data synced from Uzum.

Visible to: Manager and Finance / Accountant roles only.
Screenshot: Finance Page Show revenue charts, expense breakdown, and profit summary.

Reviews

The Reviews tab fetches product reviews from Uzum and displays them with ratings, customer messages, and dates. Requires a connected Uzum API key.

Screenshot: Reviews Tab Show review cards with star ratings and customer text.

Print Center

The Print Center generates printable barcode labels for your products. Two tabs: Products (label by product) and Invoices (label by invoice/order).

QR Code Support

Each label includes a QR code generated by the qrcodejs library, linking to the product record.

PDF Export

Labels are rendered in a print-ready layout. Clicking Print opens the browser print dialog with a pre-formatted page.

Invoice Labels

Generate labels for Uzum fulfilment invoices with item details, quantity, and destination.

Screenshot: Print Center — Products Labels Show the label preview grid with QR codes and product names.
FunctionDescription
window.renderLabelProducts()Renders product label cards in the Print Center.
window.renderInvoiceList()Renders invoice-based labels.
window.switchLabelTab(tabName)Switches between 'products' and 'invoices' sub-tabs.
window.printLabel(productId)Opens the browser print dialog for a single product label.

Activity Logs

Every stock change, sync event, and significant user action is written to the Logs tab. Logs are retained according to the configured retention duration and can be cleared manually.

SettingOptions
Retention Duration1 Week · 1 Month (default) · 3 Months · 6 Months · 1 Year · Unlimited
Clear Expired LogsRemoves log entries older than the retention window from Firestore.
Clear All LogsWipes all log entries for the current user.
Screenshot: Activity Logs List Show a list of log entries with timestamps, event types, and product names.

Worker Management

Managers can create sub-accounts for warehouse workers. Each worker gets a Firebase Auth account, a username for login, and a role that controls their UI permissions.

Visible to Manager only. Workers cannot see this tab.
Creating a Worker
1

Click "+ Add User"

Opens the Worker Wizard modal.

2

Enter name and select role

Choose from: Warehouse Worker, Picker, Packer, Logistics, Driver, Finance, Accountant, Print Manager, or Manager.

3

Generate credentials

Click "Generate". A clean username (firstname.001) and 6-character password are generated and displayed.

4

Save

Creates a real Firebase Auth account using a secondary app instance (the manager session is never interrupted). Three Firestore documents are written. Email + password are stored in the worker sub-doc for Login-as and Reset Password operations.

Three-Dots Menu Actions
ActionDescription
Login Writes a one-time token to localStorage and opens the app in a new tab. The worker is signed in there without touching the manager's current session.
Reset Password Generates a new random 8-character password, signs in as the worker via a secondary Firebase App, calls updatePassword(), and saves the new password back to Firestore.
Delete Account Deletes the Firebase Auth account, the manager's workers sub-doc, the top-level users/{uid} profile, and the workerUsernames/{username} lookup doc.
Screenshot: Worker Management Table Show the workers table with Login button, ⋮ menu open, showing Reset Password and Delete Account.

Settings

The Settings page covers general preferences, stock log retention, dashboard layout editor, AI feature toggles, and system information.

SectionControls
GeneralLanguage selector (English / O'zbek / Русский)
Stock Log RetentionDropdown (1 week → unlimited). "Clear Expired Logs" button.
Dashboard"Edit Dashboard" button — opens the widget reorder/visibility modal
AI FeaturesToggle to enable/disable AI dashboard insights panel
System InfoLive counts: Total Products, Total Vehicles, Total Logs, App Version
ActionsExport Data (JSON download), Clear All Logs

Multi-Language Support

Saleman's UI is fully translatable. Language is selected in Settings and persisted to Firestore. The app uses data-lang-key HTML attributes to identify translatable elements.

LanguageCodeCoverage
🇬🇧 EnglishenFull — primary language
🇺🇿 O'zbekuzFull — all core UI elements
🇷🇺 РусскийruFull — all core UI elements

Firebase Architecture

Saleman uses Firebase Firestore (with offline persistence) for all data storage, and Firebase Auth for user management.

Firestore Collection Structure
PathContentsOwner
users/{uid}Manager profile: name, warehouseName, profilePic, uzumAccounts, dashboardLayout, languageManager
users/{uid}/products_localManually added warehouse productsManager
users/{uid}/products_uzumSynced Uzum SKUs with full stock dataManager
users/{uid}/products_yandexYandex Market products (future)Manager
users/{uid}/workers/{workerUid}Worker profile reference required by security rulesManager
users/{uid}/warehouseLayout3D warehouse config: area, zones, rack positionsManager
users/{uid}/dashboardLayoutWidget order and visibility preferencesManager
users/{workerUid}Worker profile: isWorker, role, managerId, usernameWorker
workerUsernames/{username}Public lookup index: username → email + managerIdGlobal
Offline Persistence: Firestore is initialized with enablePersistence({ synchronizeTabs: true }). On newer Firebase SDK versions, the persistentLocalCache with persistentMultipleTabManager is used instead.

Global JS API Reference

All functions exposed on window by app.js. These can be called from the browser console or from other modules.

Dashboard
asyncwindow.renderDashboard()
asyncwindow.loadDashboardState()
windowwindow.updateDashboardWidgets()
windowwindow.showDashboardTabLoading()
Products
asyncwindow.loadProducts()
windowwindow.renderTable()
windowwindow.syncUzumProducts()
asyncwindow.importUzumProducts(key)
asyncwindow.fetchUzumShops(key)
windowwindow.showUzumSkuDetail(skuId,productId)
windowwindow.getValidImageUrl(url)
windowwindow.showImageModal(src)
Orders & Sync
asyncwindow.syncOrders()
asyncwindow.syncReturns()
windowwindow.switchOrderTab(tab)
windowwindow.changeOrdersPage(n)
windowwindow.updateOrdersPerPage(n)
windowwindow.renderOrdersPagination(total)
Workers
asyncwindow.loadWorkers()
windowwindow.renderWorkersTable()
windowwindow.openAddWorkerWizard()
windowwindow.generateWorkerCreds()
asyncwindow.saveNewWorker()
windowwindow.loginAsWorker(workerId)
asyncwindow.resetWorkerPassword(workerId)
asyncwindow.deleteWorkerAccount(workerId)
asyncwindow.__createWorkerFirebaseAccount(email,pw,data)
RBAC & Session
windowwindow.updateSidebar()
windowwindow.resetWorkerPermissions()
windowwindow.enforceWorkerPermissions()
asyncwindow.initializeApplication()
windowwindow.handleLogin()
windowwindow.signInWithGoogle()
Utilities
windowwindow.showToast(message, type)
windowwindow.renderStockSearch(query)
windowwindow.renderWorkersTable()
windowwindow.renderConnectedStores()
windowwindow.showIntroductionModal()
windowwindow.locateProductInCatalog(query)
windowwindow.__uzumProxyBase()
windowwindow.getUserProductsRef(platform)

Netlify Proxy

The netlify.toml file defines a redirect rule that proxies all /uzum-proxy/* requests to the Uzum Seller API, injecting the correct Host header and bypassing browser CORS restrictions.

netlify.toml [[redirects]]
  from = "/uzum-proxy/*"
  to = "https://api-seller.uzum.uz/api/seller-openapi/:splat"
  status = 200
  force = true
The :splat wildcard passes the remainder of the path to Uzum. For example, /uzum-proxy/seller/shop becomes https://api-seller.uzum.uz/api/seller-openapi/seller/shop.

Deployment

Saleman is deployed on Netlify at saleman.uz. The proxy redirects, custom domain, and HTTPS are all handled by Netlify's platform.

Netlify Hosting

Static file hosting with CDN edge delivery. Deploy by pushing to the connected Git branch.

Firebase Project

Project ID: salemanwms. Firestore rules restrict data access to authenticated users and their workers via the isManager() check.

HTTPS & Custom Domain

Netlify auto-provisions an SSL certificate for saleman.uz and all docs.saleman.uz subdomains.

For local development always use netlify dev (not live-server or direct file open) so the Uzum proxy functions correctly. The app detects localhost hostname automatically.
© 2026 Saleman WMS — Smart Warehouse Management System saleman.uz  ·  Open App