feat: add lucide-vue-next and plantuml-encoder dependencies, update documentation structure for TrackGo

- Added `lucide-vue-next` and `plantuml-encoder` as dependencies in the project for enhanced icon usage and diagram support.
- Updated the documentation to reflect the new structure, including user guides for TrackGo, emphasizing the separation of web and mobile functionalities.
- Revised the main index and user documentation to provide clearer navigation and improved user experience.
- Introduced new mobile documentation covering check-in and daily work processes, enhancing guidance for field users.

These changes improve the overall functionality and usability of the TrackGo platform, ensuring a better experience for both web and mobile users.
This commit is contained in:
shancheas
2026-09-04 16:19:56 +07:00
parent 2445df8e20
commit 82bbb91992
21 changed files with 924 additions and 308 deletions
+2
View File
@@ -12,6 +12,7 @@
"dependencies": {
"@repo/ui": "workspace:*",
"dayjs": "^1.11.19",
"lucide-vue-next": "^1.0.0",
"react": "^19.2.3",
"react-dom": "^19.2.3"
},
@@ -20,6 +21,7 @@
"@repo/typescript-config": "workspace:*",
"eslint": "^8.57.0",
"mermaid": "^11.15.0",
"plantuml-encoder": "^1.4.0",
"typescript": "5.5.4",
"vitepress": "^1.6.4",
"vitepress-plugin-mermaid": "^2.0.17",
+50 -52
View File
@@ -1,14 +1,17 @@
import { defineConfig } from 'vitepress';
import { withMermaid } from 'vitepress-plugin-mermaid';
import { plantumlPlugin } from './plantuml';
const config = withMermaid(
defineConfig({
// title: "Frontend Monorepo",
title: 'Frontend Arch',
description: 'Centralized documentation for the Enterprise Frontend Monorepo',
head: [
['link', { rel: 'icon', href: '/favicon.svg' }], // Jika Anda menggunakan favicon.svg
],
title: 'TrackGo',
description: 'User guides for TrackGo web and mobile',
head: [['link', { rel: 'icon', href: '/favicon.svg' }]],
markdown: {
config(md) {
plantumlPlugin(md);
},
},
themeConfig: {
search: {
provider: 'local',
@@ -17,58 +20,57 @@ const config = withMermaid(
},
},
logo: '/logo.svg',
nav: [{ text: 'Docs', link: '/overview' }],
nav: [
{ text: 'User guides', link: '/user/' },
{ text: 'Web', link: '/user/web/' },
{ text: 'Mobile', link: '/user/mobile/' },
],
sidebar: [
{
text: 'Getting Started',
text: 'User guides',
items: [{ text: 'What is TrackGo', link: '/user/' }],
},
{
text: 'Web',
collapsed: false,
items: [
{ text: 'Overview', link: '/user/web/' },
{ text: 'Set up your company', link: '/user/web/setup' },
{ text: 'Plans and visits', link: '/user/web/plans' },
{ text: 'Sales workflow', link: '/user/web/sales-workflow' },
{ text: 'Logistics', link: '/user/web/logistics' },
{ text: 'Timeline', link: '/user/web/timeline' },
{ text: 'Import CSV', link: '/user/web/import' },
],
},
{
text: 'Mobile',
collapsed: false,
items: [
{ text: 'Overview', link: '/user/mobile/' },
{ text: 'Check in', link: '/user/mobile/check-in' },
{ text: 'Daily work', link: '/user/mobile/daily-work' },
],
},
{
text: 'Developer reference',
collapsed: true,
items: [
{ text: 'Project Overview', link: '/overview' },
{ text: 'Development Setup', link: '/setup' },
],
},
{
text: 'Core Architecture',
collapsed: false,
items: [
// { text: 'API & Domain Logic', link: '/packages/core-api/' },
{
text: 'API & Domain Logic',
collapsed: false,
items: [
{ text: 'API Engine', link: '/packages/core-api' },
{ text: 'Data Transformers', link: '/packages/core-api/transformers' },
],
},
{ text: 'Event Bus System', link: '/packages/core-events/' },
{ text: 'Storage & Persistence', link: '/packages/core-storage/' },
{ text: 'I18n & Localization', link: '/packages/core-i18n/' },
],
},
{
text: 'UI System',
collapsed: false,
items: [
{ text: 'Overview', link: '/packages/ui/' },
{ text: 'UI Overview', link: '/packages/ui/' },
{ text: 'App Layout', link: '/packages/ui/CORE-APP-SHELL' },
{ text: 'Action Tools', link: '/packages/ui/ACTION-TOOLS' },
{ text: 'Form Primitives', link: '/packages/ui/FORM-COMPONENTS' },
],
},
{
text: 'TrackGo Web',
collapsed: false,
items: [
{ text: 'Overview', link: '/apps/web/' },
{ text: 'Sales Workflow', link: '/apps/web/SALES_WORKFLOW' },
],
},
{
text: 'Desktop Ecosystem',
collapsed: false,
items: [
{ text: 'Overview', link: '/apps/desktop/' },
{ text: 'Web Overview', link: '/apps/web/' },
{ text: 'Sales Workflow (legacy)', link: '/apps/web/SALES_WORKFLOW' },
{ text: 'Desktop Overview', link: '/apps/desktop/' },
{ text: 'Lifecycle & Configuration', link: '/apps/desktop/CONFIGURATION' },
{ text: 'IPC & Bridge Architecture', link: '/apps/desktop/IPC_ARCHITECTURE' },
{ text: 'Distribution & Auto-Update', link: '/apps/desktop/AUTO_UPDATER' },
@@ -87,25 +89,21 @@ const config = withMermaid(
],
},
// Mermaid configuration
mermaid: {
theme: 'default',
},
// Fix cascading CJS/ESM SyntaxErrors caused by Vite dynamically discovering mermaid
vite: {
ssr: {
noExternal: ['lucide-vue-next'],
},
optimizeDeps: {
include: ['mermaid'],
include: ['mermaid', 'lucide-vue-next'],
},
},
}),
);
// Pnpm strict workspace workaround:
// vitepress-plugin-mermaid aggressively injects sub-dependencies into optimizeDeps.include.
// Because pnpm uses strict symlinks, Vite fails to resolve these sub-dependencies from the project root,
// causing pre-bundling to fail and cascading CJS/ESM SyntaxErrors in the browser.
// We strip them out so esbuild can naturally inline them into the 'mermaid' chunk instead.
if (config.vite?.optimizeDeps?.include) {
config.vite.optimizeDeps.include = config.vite.optimizeDeps.include.filter(
(dep) => !['@braintree/sanitize-url', 'debug', 'cytoscape-cose-bilkent', 'cytoscape'].includes(dep),
+4
View File
@@ -0,0 +1,4 @@
declare module 'plantuml-encoder' {
export function encode(source: string): string;
export function decode(encoded: string): string;
}
+25
View File
@@ -0,0 +1,25 @@
import { createRequire } from 'node:module';
import type MarkdownIt from 'markdown-it';
const require = createRequire(import.meta.url);
const plantumlEncoder = require('plantuml-encoder') as {
encode: (source: string) => string;
};
export function plantumlPlugin(md: MarkdownIt): void {
const defaultFence = md.renderer.rules.fence;
if (!defaultFence) {
return;
}
md.renderer.rules.fence = (tokens, idx, options, env, slf) => {
const token = tokens[idx];
const info = token.info.trim().split(/\s+/)[0];
if (info === 'plantuml') {
const encoded = plantumlEncoder.encode(token.content);
const src = `https://www.plantuml.com/plantuml/svg/${encoded}`;
return `<div class="plantuml-diagram"><img src="${src}" alt="diagram" /></div>\n`;
}
return defaultFence(tokens, idx, options, env, slf);
};
}
@@ -114,3 +114,34 @@
flex-grow: 0;
}
}
.doc-lead {
display: flex;
align-items: center;
gap: 0.5rem;
color: var(--vp-c-text-2);
margin-top: -0.25rem;
}
.doc-icon {
display: inline-block;
vertical-align: -0.2em;
color: var(--vp-c-brand-1);
flex-shrink: 0;
}
.plantuml-diagram {
background: #ffffff;
border: 1px solid var(--vp-c-divider);
border-radius: 8px;
padding: 12px;
margin: 16px 0;
overflow-x: auto;
}
.plantuml-diagram img {
max-width: 100%;
height: auto;
display: block;
margin: 0 auto;
}
+1 -186
View File
@@ -1,188 +1,3 @@
# Sales Workflow
How to run a sale in TrackGo: from a request or order, through packing and invoicing, to payment.
You need permission to view each menu. Status buttons (Process, Complete, Submit, Approve, and so on) appear only if you are allowed to edit.
---
## Before you start
Set up the records that every sales document needs:
| You need | Where to create it |
| ----------------------------------- | ---------------------------- |
| Division, branch, customer, product | **Settings → Data** |
| Salesperson | **Sales → Data → Employees** |
If a menu is missing, ask an administrator to give your privilege **View** (and **Update** for status buttons) on that area.
---
## The usual flow
```mermaid
flowchart LR
subgraph sales [Sales]
R[Sales Request]
O[Sales Order]
P[Process]
I[Sales Invoice]
PAY[Sales Payment]
end
subgraph logistics [Logistics]
PS[Packing Slip]
C[Complete]
end
R -.->|optional| O
O --> P
P --> I
P -->|unless you skip it| PS
PS --> C
I --> PAY
```
1. Optionally create and approve a **sales request**.
2. Create a **sales order** (from the request, or from scratch).
3. Open the draft order and click **Process**. Leave **Generate packing slip** checked unless you do not need delivery paperwork. Processing creates a **sales invoice**, and a **packing slip** unless you skip it.
4. If there is a packing slip, open **Logistics → Packing slips**, then **Complete** and enter how much was delivered.
5. Open the invoice and click **Create Sales Payment**. **Submit** the payment, then **Approve** it (or **Reject** / **Rollback**).
---
## Sales request
**Menu:** Sales → Activities → Sales Requests
A request is optional. You can go straight to a sales order.
1. Click **Create**. Fill in date, salesperson, branch, division, customer, address, and at least one product with a quantity. Product price and document code are optional. Save.
2. While the status is **Draft**, click **Submit** to send it for approval. You can also use **Change status** to mark it **Rejected**.
3. While it is **Pending**, click **Approve** or **Reject**. **Change status** can send it back to **Draft**.
4. After it is **Approved**, there is nothing more to do on the request itself.
5. On the request detail page, click **Create Sales Order**. The order form opens with the requests customer, location, and products already filled in.
---
## Sales order
**Menu:** Sales → Activities → Sales Orders
1. Click **Create**. Fill in the same kind of details as a request. On a new order you can pick a sales request under **Source**; that copies the request into the form. Save. A new order starts as **Draft**.
2. On a **Draft** order, click **Process**.
- You will see: _Processing creates a sales invoice and, unless skipped, a packing slip._
- **Generate packing slip** is checked by default. Uncheck it if you do not want a packing slip.
3. On **Draft** or **Processed**, you can **Cancel**. There is no **Complete** button on orders. If the order later shows **Completed**, that was not something you clicked on this screen.
4. On the order detail page you can see linked packing slips and invoices, and click **Create Sales Invoice**.
You can select several orders and Process or Cancel them together, as long as they are all in a status that allows that action.
---
## Packing slip
**Menu:** Logistics → Activities → Packing slips
Packing slips live under **Logistics**, not Sales. You can also create one by hand and link it to a sales order.
1. If the status is **Draft**, use **Change status** to move it to **Processed**, or **Cancel** it. There is no **Process** button here.
2. When it is **Processed**, click **Complete**. Enter the **delivered quantity** for each product. Remaining quantity opens a **new packing slip**.
3. You can **Cancel** a slip while it is **Draft** or **Processed**.
---
## Sales invoice
**Menu:** Sales → Activities → Sales Invoices
Invoices appear when you **Process** an order. You can also create one yourself, optionally from an order or a packing slip.
On the invoice, the only status button is **Cancel** (while the invoice is **Draft**, **Processed**, or **Partial**). You cannot choose Processed, Partial, or Completed yourself. Those labels can still show on the invoice.
On the invoice detail page, click **Create Sales Payment**. The payment form opens with that invoice and its remaining balance.
---
## Sales plan
**Menu:** Sales → Activities → Sales Plans
When you create or edit a **sales** plan, you can attach one or more invoices. Saving the plan does not change the invoice status.
(Logistics plans attach packing slips instead.)
---
## Sales payment
**Menu:** Sales → Activities → Sales Payments
1. Click **Create**. Enter the date and at least one line: an invoice and an amount. Images and notes are optional. Save. A new payment starts as **Draft**.
2. Click **Submit** to move it to **Pending**. You can also use **Change status** to mark it **Rejected**.
3. While it is **Pending**, click **Approve**, **Reject**, or **Rollback** (back to **Draft**).
4. After **Approved**, you cannot change the status further. A **Rejected** payment can be sent back to **Draft** with **Change status**.
Approving a payment does not change the invoice status from this screen. Refresh the invoice if you need to see an updated balance.
---
## What you can do at each status
**Buttons** are the labelled actions on the list or detail page. **Change status** is a separate dialog that lists the statuses you may pick. Invoices do not have Change status.
### Sales request and sales payment
Both use Draft → Pending → Approved or Rejected.
| Current status | Buttons | Change status |
| -------------- | ------------------------------------------------------------ | ------------------------- |
| Draft | Submit | Rejected |
| Pending | Approve, Reject. Payments also have Rollback (back to Draft) | Draft, Approved, Rejected |
| Approved | — | — |
| Rejected | — | Draft |
### Sales order
| Current status | Buttons | Change status |
| -------------- | --------------- | -------------------- |
| Draft | Process, Cancel | Processed, Cancelled |
| Processed | Cancel | Cancelled |
| Completed | — | — |
| Cancelled | — | — |
**Process** always opens the packing-slip checkbox dialog (not Change status).
### Packing slip
| Current status | Buttons | Change status |
| -------------- | ---------------- | -------------------- |
| Draft | Cancel | Processed, Cancelled |
| Processed | Complete, Cancel | Completed, Cancelled |
| Completed | — | — |
| Cancelled | — | — |
**Complete** always asks for delivered quantities.
### Sales invoice
You may see Draft, Processed, Partial, Completed, or Cancelled.
| Current status | What you can do |
| ---------------------------- | ----------------- |
| Draft, Processed, or Partial | Cancel |
| Completed | No status buttons |
| Cancelled | No status buttons |
---
## What you should not expect to click
These are not buttons on the screens:
- **Complete** on a sales order
- Setting an invoice to Processed, Partial, or Completed yourself
- Changing an invoices status by putting it on a sales plan
- Changing an invoices status by approving a payment
- Cancelling an order, packing slip, or invoice in one click for all related documents — **Cancel** applies only to the document you are looking at
If a related documents status changes after you act, open that document again to see the latest value.
This guide moved to [Sales workflow](/user/web/sales-workflow).
+9 -2
View File
@@ -9,7 +9,7 @@ Use the sidebar to open **Sales** for day-to-day selling, **Logistics** for pack
## Where to find things
| Area | Menu | What you do there |
| ------------ | ---------------------- | ------------------------------------------------- |
| --- | --- | --- |
| Sales | Sales → Activities | Requests, orders, invoices, payments, visit plans |
| Logistics | Logistics → Activities | Packing slips, delivery plans |
| Company data | Settings → Data | Divisions, branches, customers, products |
@@ -21,4 +21,11 @@ If a menu item is missing, your privilege does not include **View** for that are
## Guides
- [Sales workflow](/apps/web/SALES_WORKFLOW) — from a sales request or order through packing, invoicing, and payment
User guides now live under **User guides** in the sidebar:
- [Web overview](/user/web/)
- [Set up your company](/user/web/setup)
- [Sales workflow](/user/web/sales-workflow)
- [Plans and visits](/user/web/plans)
- [Logistics](/user/web/logistics)
- [Timeline](/user/web/timeline)
+45 -34
View File
@@ -2,63 +2,74 @@
layout: home
hero:
name: 'Frontend Architecture'
text: 'Enterprise Monorepo'
tagline: 'A scalable, standardized foundation for Web & Desktop applications. Built for performance, consistency, and velocity.'
name: TrackGo
text: Sales and logistics guides
tagline: How operators, sales, and drivers use TrackGo on the web and on mobile.
actions:
- theme: brand
text: Get Started
link: /setup
text: User guides
link: /user/
- theme: alt
text: View Architecture
link: /overview
text: Web
link: /user/web/
features:
- title: 🧩 App Layout Engine
details: Core App Shell wrapping Mantine for rapid, consistent layout creation across all products.
link: /packages/ui/CORE-APP-SHELL
linkText: Explore App Shell
- title: 📝 Form Primitives
details: RHF + Zod + Mantine components designed for O(1) render cost and deep type safety.
link: /packages/ui/FORM-COMPONENTS
linkText: View Forms
- title: 🖥️ Desktop Ecosystem
details: Complete Electron integration with secure Inter-Process Communication (IPC) and Bridge Architecture.
link: /apps/desktop/
linkText: Desktop Docs
- title: 🔌 Core Architecture
details: Robust state management, Event Bus, first-class i18n, and unified Storage layers.
link: /packages/core-api/
linkText: Explore Core
- title: Web
details: Set up company data, users, plans, invoices, payments, packing slips, and activity history.
link: /user/web/
linkText: Web guides
- title: Mobile
details: Check in at a branch, follow today's plan, visit customers, and collect payments in the field.
link: /user/mobile/
linkText: Mobile guides
- title: Sales
details: Move from a sales request or order through invoicing and payment.
link: /user/web/sales-workflow
linkText: Sales workflow
- title: Plans
details: Create a one-off daily plan, or generate plans from weekly visit cycles.
link: /user/web/plans
linkText: Plans and visits
---
<script setup>
import { ShoppingCart, Truck, Smartphone, MapPin } from 'lucide-vue-next'
</script>
<div class="custom-divider"></div>
<div class="bento-container">
<div class="bento-header">
<h2>Why this Monorepo Exists</h2>
<p>Building high-end enterprise applications requires strict consistency, shared tooling, and well-defined boundaries. This monorepo serves as the absolute source of truth for our frontend ecosystem.</p>
<h2>How TrackGo is used</h2>
<p>Operators prepare master data and daily plans on the web. Sales and drivers check in on mobile, then work the plan: visit customers, collect invoices, or complete deliveries.</p>
</div>
<div class="bento-grid">
<div class="bento-card">
<div class="bento-icon">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z"></path></svg>
<ShoppingCart :size="24" />
</div>
<h3>Unified Tooling</h3>
<p>By enforcing strict linting, formatting, and building patterns at the root (powered by Turborepo), we ensure that every application in the ecosystem adheres to our high standards without duplicate configuration.</p>
<h3>Sales on the web</h3>
<p>Create requests, orders, invoices, and payments. Attach invoices to a sales plan so field staff know what to collect.</p>
</div>
<div class="bento-card">
<div class="bento-icon">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"></path><polyline points="3.27 6.96 12 12.01 20.73 6.96"></polyline><line x1="12" y1="22.08" x2="12" y2="12"></line></svg>
<Truck :size="24" />
</div>
<h3>Shared Domain Logic</h3>
<p>The separation of UI components and Core logic guarantees that our web and desktop applications all share the exact same state management and event-driven architecture, drastically reducing cognitive load.</p>
<h3>Logistics on the web</h3>
<p>Manage packing slips and delivery plans. Complete delivered quantities so orders can finish.</p>
</div>
<div class="bento-card">
<div class="bento-icon">
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2"></polygon></svg>
<Smartphone :size="24" />
</div>
<h3>Accelerated Velocity</h3>
<p>With a rich library of 22+ advanced Form components, pre-configured Vite/TypeScript environments, and an enterprise App Shell, teams can stop reinventing the wheel and start building product features on day one.</p>
<h3>Field work on mobile</h3>
<p>Clock in at the plan start branch, open today's destinations, check in to a customer, and record a payment.</p>
</div>
<div class="bento-card">
<div class="bento-icon">
<MapPin :size="24" />
</div>
<h3>Activity history</h3>
<p>Review a day's footprints and field events on Timeline. Live map streaming on the web is Coming soon.</p>
</div>
</div>
</div>
@@ -37,7 +37,7 @@ outline: [2, 3]
## Overview
The **Core App Shell** is a configuration-driven layout engine that wraps Mantine's `AppShell` component. It provides a single `<CoreAppShell>` component that renders enterprise-grade application frames — complete with headers, sidebars, aside panels, utility bars, and footers — controlled entirely through a declarative `config` object and slot-based content injection.
The **Core App Shell** is a configuration-driven layout engine that wraps Mantine's `AppShell` component. It provides a single `CoreAppShell` component that renders enterprise-grade application frames — complete with headers, sidebars, aside panels, utility bars, and footers — controlled entirely through a declarative `config` object and slot-based content injection.
Key capabilities:
@@ -60,7 +60,7 @@ The layout engine uses a **Provider → Inner** composition pattern:
CoreAppShell (Public API)
└── CoreAppShellProvider (Context — state management)
└── CoreAppShellInner (Layout rendering — consumes context)
└── Mantine <AppShell> (CSS Grid engine)
└── Mantine AppShell (CSS Grid engine)
├── AppShell.Header ← slots.utilityBar + slots.header
├── AppShell.Navbar ← slots.sidebar | slots.sidebarRail + slots.sidebarPanel
├── AppShell.Main ← children
@@ -120,7 +120,7 @@ type LayoutVariant = 'header-first' | 'sidebar-first' | 'top-nav';
| `top-nav` | `default` | Header-only layout with **no visible desktop sidebar**. The sidebar is hidden on desktop but remains accessible as a mobile drawer on small screens. Ideal for documentation sites or marketing pages. |
> [!IMPORTANT]
> When `variant` is set to `top-nav`, the desktop navbar is visually hidden via `collapsed.desktop: true` and width `0`. However, the `<AppShell.Navbar>` DOM element remains mounted with responsive width props so the mobile drawer continues to function. This is an intentional design choice to avoid conditional DOM removal.
> When `variant` is set to `top-nav`, the desktop navbar is visually hidden via `collapsed.desktop: true` and width `0`. However, the `AppShell.Navbar` DOM element remains mounted with responsive width props so the mobile drawer continues to function. This is an intentional design choice to avoid conditional DOM removal.
---
@@ -201,7 +201,7 @@ interface CoreAppShellSlots {
| Slot | Location | Notes |
| --------------- | --------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- |
| `utilityBar` | Above the header, hidden on mobile (`display: none` below `sm`) | Typically used for environment banners, announcements, or top-level links. |
| `header` | Main application header | Must contain its own `<Burger>` for mobile toggle (use `useCoreAppShell()` context). |
| `header` | Main application header | Must contain its own `Burger` for mobile toggle (use `useCoreAppShell()` context). |
| `sidebar` | Desktop navbar body (single-sidebar mode) | Ignored when `withDoubleSidebar` is `true` — use `sidebarRail` + `sidebarPanel` instead. |
| `sidebarMobile` | Mobile drawer content | Falls back to `sidebar` if not provided. Use this to render a simplified mobile-specific navigation. |
| `sidebarRail` | Narrow icon rail (double-sidebar mode) | Only rendered when `withDoubleSidebar` is `true`. Separated from `sidebarPanel` by a 1px border. |
@@ -233,7 +233,7 @@ import { useCoreAppShell } from '@repo/ui/components';
| `toggleNavbarPanel()` | `() => void` | Toggle the double-sidebar panel open/closed |
| `setSidebarVariant()` | `(variant: SidebarVariant) => void` | Programmatically set the sidebar to `'expanded'`, `'mini'`, or `'hidden'` |
> [!WARNING] > `useCoreAppShell()` **must** be called from within a `<CoreAppShell>` subtree. Calling it outside the provider will throw: `"useCoreAppShell must be used within CoreAppShellProvider"`. If you need context access in the header slot, pass a component (not inline JSX) so it mounts inside the provider tree.
> [!WARNING] > `useCoreAppShell()` **must** be called from within a `CoreAppShell` subtree. Calling it outside the provider will throw: `"useCoreAppShell must be used within CoreAppShellProvider"`. If you need context access in the header slot, pass a component (not inline JSX) so it mounts inside the provider tree.
---
@@ -416,8 +416,9 @@ function App() {
}
```
> [!NOTE]
> When `withDoubleSidebar` is `true`, the `sidebar` slot is ignored on desktop. The navbar renders `sidebarRail` (fixed-width icon column) and `sidebarPanel` (collapsible contextual panel) side-by-side. On mobile, `sidebarMobile` takes priority, falling back to `sidebar` if not provided.
::: info
When `withDoubleSidebar` is `true`, the `sidebar` slot is ignored on desktop. The navbar renders `sidebarRail` (fixed-width icon column) and `sidebarPanel` (collapsible contextual panel) side-by-side. On mobile, `sidebarMobile` takes priority, falling back to `sidebar` if not provided.
:::
---
@@ -458,7 +459,7 @@ function ShellDemo() {
## CorePageContainer
A companion component for structuring page-level content within the `<AppShell.Main>` area. It provides a sticky page header and a contained, padded content region.
A companion component for structuring page-level content within the `AppShell.Main` area. It provides a sticky page header and a contained, padded content region.
```tsx
import { CorePageContainer } from '@repo/ui/components';
@@ -508,7 +509,7 @@ interface CorePageContainerProps extends ContainerProps {
### Mobile Navbar Lifecycle
The `<AppShell.Navbar>` DOM element is **always mounted**, even when the layout variant is `top-nav`. The desktop content is hidden via `visibleFrom="sm"` and mobile content via `hiddenFrom="sm"`. This ensures Mantine's native drawer engine works correctly on mobile without conditional DOM removal breaking the transition animations.
The `AppShell.Navbar` DOM element is **always mounted**, even when the layout variant is `top-nav`. The desktop content is hidden via `visibleFrom="sm"` and mobile content via `hiddenFrom="sm"`. This ensures Mantine's native drawer engine works correctly on mobile without conditional DOM removal breaking the transition animations.
### Footer Positioning in `header-first` Mode
+60
View File
@@ -0,0 +1,60 @@
# What is TrackGo
TrackGo is a sales and logistics platform. Office staff run the **web** app. Sales, drivers, and crew work from the **mobile** app.
You set up the company, people, products, and customers first. Then you plan the day's visits, attach invoices or packing slips, and follow field activity.
## Who uses it
| Role | Where they work | Typical tasks |
| --- | --- | --- |
| Operator / admin | Web | Company data, users, privileges, plans, documents |
| Sales | Web and mobile | Requests, orders, invoices, payments, customer visits |
| Driver / crew | Web (plans) and mobile | Delivery plans, packing slips, customer stops |
What you can open depends on the **privilege** assigned to your user. If a menu is missing, ask an administrator to grant **View** for that area.
## The usual flow
```plantuml
@startuml
actor Operator
actor Field as "Sales or driver"
Operator -> Web: Set up company, users, and master data
Operator -> Web: Create cycles or a one-off plan
Operator -> Web: Attach invoices or packing slips
Field -> Mobile: Clock in at the plan start branch
Field -> Mobile: Visit customers on the plan
Field -> Mobile: Collect payment or follow packing tasks
Operator -> Web: Review documents and Timeline
@enduml
```
1. Add company settings, branches, divisions, customers, products, and employees.
2. Create users and privileges so each person only sees what they need.
3. Build **weekly cycles** (recurring) or a **one-off plan** for a date. Attach invoices (sales) or packing slips (logistics).
4. Field staff check in at the branch on mobile, then work the plan.
5. Sales collect payment on mobile or web. Drivers complete packing slips on the web.
## Sales documents
The sales path is:
**Sales request (optional) → Sales order → Invoice → Payment**
Processing an order can also create a packing slip for delivery. Details: [Sales workflow](/user/web/sales-workflow).
## Web and mobile
- [Web](/user/web/) — setup, plans, sales, logistics, timeline, CSV import
- [Mobile](/user/mobile/) — login, branch check-in, today's plan, customers, payments
## Coming soon
These items are not in the product yet:
- Web **Dashboard** (the menu exists; the page is a placeholder)
- **Live** position on the web map (Timeline plays back a recorded day instead)
- Mobile **packing slip status** updates (logistics tasks are listed, not updated on the phone)
- Forgot password, remember me, and sign-up on login screens
+41
View File
@@ -0,0 +1,41 @@
<script setup>
import { MapPin } from 'lucide-vue-next'
</script>
# Check in
<p class="doc-lead"><MapPin :size="20" class="doc-icon" /> Clock in at the plan start branch before field work</p>
Sales and drivers **clock in at the plan start branch** before they start field work. The app does not block browsing Home, Plan, or Customers without check-in, but create-customer, customer visit check-in, sales documents, and payments stay disabled until you are on duty.
## Before you clock in
You need **today's plan** with a start branch. If there is no plan, Clock In is unavailable and the screen explains there is no plan for today. An operator must [create or generate the plan](/user/web/plans) on the web.
## Clock in and out
1. From Home, tap **Check in now** if you see **You have not checked in**, or open the check-in screen.
2. Confirm your location on the map (OpenStreetMap). Use recenter if needed.
3. Tap **Clock In Now**. The app sends GPS coordinates for the plan **start branch**.
4. While on duty, the app records location footprints in the background (the interval comes from company GPS settings).
5. Tap **Clock Out** when the shift ends. Tracking stops.
The screen shows your name, date, coordinates, and clock-in / clock-out times.
A circle on the map is a visual guide. The Clock In button is enabled when a plan exists and you are not already checked in — it is not blocked by standing outside that circle.
## After check-in
While branch attendance is open:
- You can create a customer
- You can check in to a customer on a visit
- Sales actions (request, order, payment) also need a **customer visit** check-in for that customer
If GPS moves too far from a customer visit, Home can show a checkout warning so you can check out of the visit.
## Coming soon
- Check-in by **NFC** or **QR** (the screen always uses GPS)
- Enforcing the check-in radius before Clock In is enabled
@@ -0,0 +1,84 @@
<script setup>
import { Calendar } from 'lucide-vue-next'
</script>
# Daily work
<p class="doc-lead"><Calendar :size="20" class="doc-icon" /> Home, Plan, and Customers after you are on duty</p>
After [branch check-in](/user/mobile/check-in), use Home, Plan, and Customers to work the day.
## Home
Home has two tabs: **Plan** and **Timeline**.
**Plan tab**
- Greeting and your position (Sales, Driver, or Crew)
- Banner if you have not checked in at the branch
- Today's destinations — **Open** starts a customer visit
- Active visit card with checkout
- **Today Task** — invoices (sales) or packing-slip codes (driver/crew)
**Timeline tab**
Today's field events grouped by customer (check-in/out, sales documents, new customers). Pull down to refresh.
Logout is on the avatar menu.
### Coming soon on Home
- Work **performance** totals (customer / invoice / payment / packing counts are not shown on the screen)
- Messages and Notifications icons do nothing yet
## Plan
The **Plan** tab lists today's destinations. Open a stop to go to the customer visit screen.
If a plan exists, use **Add customer to plan**:
1. Search customers who are not already on the plan.
2. For **sales** plans, you can pick outstanding invoices to attach.
3. Confirm **Add to plan**.
You cannot create a new daily plan on the phone — only extend an existing one.
### Coming soon on Plan
- Picking **packing slips** when adding a customer to a logistics plan
## Customers
Search and sort the list by **Name**, **Code**, or **Distance**. Tap a row for the profile.
**Create** (FAB) is available only after branch check-in. The form includes code, name, phone, NFC ID (typed), address, map location, and optional contacts.
### Visit a customer
Open a destination from the plan, or open the visit route from the profile.
1. Complete **Customer check-in (GPS)** when sales actions are locked.
2. Then you can use **Sales actions**.
Without branch check-in you see **Branch check-in is required**. Without a visit check-in for that customer you see that you must check in to the customer.
### Sales request and sales order
**Create sales request** and **Create sales order (no packing)** send a document with the **first loaded product** and quantity **1**. There is no product picker or cart on the phone.
For full line items, create the documents on the [web](/user/web/sales-workflow).
### Payments
Outstanding invoices on the profile/visit list have **Create payment**.
1. Confirm invoice code, status, and balance.
2. Enter **amount** (starts at the remaining balance) and optional **notes**.
3. Record the payment. GPS is attached when available.
Amount must be greater than zero and not more than the balance. The same branch + customer visit rules apply.
## What drivers can do today
Drivers and crew follow the logistics plan and see packing-slip codes on Home. **Changing packing slip status on mobile is Coming soon** — complete slips on the [web](/user/web/logistics).
+55
View File
@@ -0,0 +1,55 @@
<script setup>
import { Smartphone } from 'lucide-vue-next'
</script>
# Mobile overview
<p class="doc-lead"><Smartphone :size="20" class="doc-icon" /> Field app for sales, drivers, and crew</p>
The TrackGo mobile app is for **sales**, **drivers**, and **crew** in the field. Bottom tabs are **Home**, **Plan**, and **Customers**. Branch check-in is a separate screen.
Your employee position decides which plan you get:
| Position | Plan type | Today's tasks on Home |
| --- | --- | --- |
| Sales | Sales | Invoices on today's plan |
| Driver or crew | Logistics | Packing slip codes on today's plan |
If the account is not linked to an employee, Home and Plan explain that the profile is not linked.
## Sign in
1. Open the app.
2. Enter **Employee ID** and **Password**.
3. Tap **Sign In**.
Username rules: 332 characters. Password: 872 characters.
## A typical day
```plantuml
@startuml
start
:Sign in;
:Open Home or Check in;
:Clock in at the plan start branch;
:Open today's plan;
:Check in to a customer;
:Collect payment or create a sales document;
:Clock out of the visit and the branch;
stop
@enduml
```
1. [Check in](/user/mobile/check-in) at the branch (required before most field actions).
2. [Daily work](/user/mobile/daily-work) — plan, customers, visits, payments, Home timeline.
## Coming soon on login
These controls are visible but do not work yet:
- Remember Me
- Forgot Password
- Sign in with email or phone
- Sign up
+29
View File
@@ -0,0 +1,29 @@
<script setup>
import { FileText } from 'lucide-vue-next'
</script>
# Import CSV
<p class="doc-lead"><FileText :size="20" class="doc-icon" /> Import on document lists when your privilege includes Import</p>
You can bulk-load documents from a **.csv** file on these index pages:
| Document | Menu |
| --- | --- |
| Sales requests | Sales → Activities → Sales Requests |
| Sales orders | Sales → Activities → Sales Orders |
| Sales invoices | Sales → Activities → Sales Invoices |
| Sales payments | Sales → Activities → Sales Payments |
| Packing slips | Logistics → Activities → Packing slips |
## How to import
1. Open the list.
2. Click **Import CSV** (hidden if you do not have **Import** on that area).
3. Choose a `.csv` file and confirm.
There is no separate import wizard beyond the file upload.
## What you cannot import here
Master data (branches, customers, products), users, privileges, cycles, and plans do **not** have an Import CSV button. Create those records in their own screens, or generate plans from cycles.
+40
View File
@@ -0,0 +1,40 @@
<script setup>
import { LayoutDashboard, MapPin, ShoppingCart, Truck, Settings } from 'lucide-vue-next'
</script>
# Web overview
<p class="doc-lead"><LayoutDashboard :size="20" class="doc-icon" /> Browser and desktop app for operators, sales, and logistics.</p>
Sign in with **Username** and **Password** on the login page. You can switch the language between English and Indonesian.
After login, the sidebar shows only the menus your privilege allows.
## Menus
| Menu | Icon | What you do there |
| --- | --- | --- |
| Dashboard | <LayoutDashboard :size="16" class="doc-icon" /> | Coming soon |
| Timeline | <MapPin :size="16" class="doc-icon" /> | Play back a day's GPS footprints and field activities for sales employees |
| Sales | <ShoppingCart :size="16" class="doc-icon" /> | Employees, cycles, requests, orders, invoices, payments, plans, reports |
| Logistics | <Truck :size="16" class="doc-icon" /> | Drivers and crew, cycles, packing slips, plans, reports |
| Settings | <Settings :size="16" class="doc-icon" /> | Branches, divisions, customers, products, company settings, users, privileges |
## Suggested order
1. [Set up your company](/user/web/setup) — master data, users, and privileges
2. [Plans and visits](/user/web/plans) — one-off plans and weekly cycles
3. [Sales workflow](/user/web/sales-workflow) — request, order, invoice, payment
4. [Logistics](/user/web/logistics) — packing slips and delivery plans
5. [Timeline](/user/web/timeline) — activity history on the map
6. [Import CSV](/user/web/import) — bulk load documents
## Reports
**Sales → Sales Reports** and **Logistics → Logistics Reports** show report tables from the server. If none are configured, the page says there are no reports available.
## Status and permissions
Most master-data records use **Draft**, **Active**, and **Archived**. Sales and logistics documents use their own statuses (see each guide).
Buttons such as Create, Process, Submit, and Approve appear only when your privilege includes that action (**Create**, **Update**, **Delete**, or **Import**).
+74
View File
@@ -0,0 +1,74 @@
<script setup>
import { Truck, Package, Calendar } from 'lucide-vue-next'
</script>
# Logistics
<p class="doc-lead"><Truck :size="20" class="doc-icon" /> Logistics menus for drivers, crew, packing slips, and delivery plans</p>
Use Logistics when you need delivery paperwork and driver routes. Sales still owns requests, orders, invoices, and payments.
## Employees and cycles
- **Logistics → Data → Employees** — positions **driver** and **crew**
- **Logistics → Data → Logistics Cycles** — weekly visit templates for those employees
Generate daily plans from cycles the same way as sales. See [Plans and visits](/user/web/plans).
## Packing slips
<p class="doc-lead"><Package :size="18" class="doc-icon" /> Logistics → Activities → Packing slips</p>
Packing slips are created when you **Process** a sales order with **Generate packing slip** checked. You can also create one by hand and link it to a sales order.
Typical fields: code, date, customer, sales order, location, products, notes.
```plantuml
@startuml
start
if (Status is Draft?) then (yes)
:Change status to Processed\nor Cancel;
endif
if (Status is Processed?) then (yes)
:Complete;
:Enter delivered quantity per product;
if (Remaining quantity?) then (yes)
:A new packing slip is created\nfor the remainder;
else (no)
:Slip is Completed;
endif
endif
stop
@enduml
```
1. If the status is **Draft**, use **Change status** to move it to **Processed**, or **Cancel** it. There is no **Process** button here.
2. When it is **Processed**, click **Complete**. Enter the **delivered quantity** for each product. Remaining quantity opens a **new packing slip**.
3. You can **Cancel** a slip while it is **Draft** or **Processed**.
When every related packing slip is completed, the linked sales order can move to **Completed**. If you skip packing on the order, the order does not wait for a slip.
### Status actions
| Current status | Buttons | Change status |
| --- | --- | --- |
| Draft | Cancel | Processed, Cancelled |
| Processed | Complete, Cancel | Completed, Cancelled |
| Completed | — | — |
| Cancelled | — | — |
**Complete** always asks for delivered quantities.
You can [import packing slips from CSV](/user/web/import) if your privilege includes **Import**.
## Logistics plans
<p class="doc-lead"><Calendar :size="18" class="doc-icon" /> Logistics → Activities → Logistics Plans</p>
Same plan model as sales: employee, date, start/end branch, customers. Attach **packing slips** instead of invoices.
Drivers and crew open today's plan on [mobile](/user/mobile/daily-work). Home shows packing-slip codes as today's tasks.
## Coming soon
**Updating packing slip status on mobile** is not available. Drivers see assigned packing codes on Home; they complete slips in the web app (or an operator does).
+68
View File
@@ -0,0 +1,68 @@
<script setup>
import { Calendar, Repeat } from 'lucide-vue-next'
</script>
# Plans and visits
<p class="doc-lead"><Calendar :size="20" class="doc-icon" /> Sales → Activities → Sales Plans, or Logistics → Activities → Logistics Plans</p>
A **plan** is one employee's work for **one date**: start branch, end branch, customers to visit, and documents to collect.
There is no recurrence switch on the plan form. Recurring work uses **cycles**, then **Generate**.
```plantuml
@startuml
start
if (Need a repeating weekly route?) then (yes)
:Create a cycle with weekday routes;
:Generate plans for a date range;
else (no)
:Create a one-off plan for a date;
endif
:Attach invoices (sales) or packing slips (logistics);
:Field staff open the plan on mobile;
stop
@enduml
```
## One-off plan
**Create page description in the app:** "Create a one-off daily plan for an employee."
1. Open **Sales Plans** or **Logistics Plans**.
2. Click **Create**.
3. Choose the **employee**, **date** (today or later), **start branch**, and **end branch**.
4. Add **customers** as destinations (order matters for the route).
5. After customers are selected:
- Sales plans: attach **invoices** for those customers
- Logistics plans: attach **packing slips** for those customers
6. Save. Status is **Draft**, **Active**, or **Archived** like other master records.
You can preview customers and attached documents on the form.
Saving a sales plan does **not** change invoice status.
## Weekly cycles (recurring)
<p class="doc-lead"><Repeat :size="18" class="doc-icon" /> Sales → Data → Sales Cycles, or Logistics → Data → Logistics Cycles</p>
A cycle is a **weekly visit template** for one employee: cycle number and, for each weekday, whether the day is active, start/end branch, and customers.
1. Create a cycle for the employee.
2. Enable the weekdays they work and fill the route.
3. Set the cycle to **Active**.
## Generate plans from cycles
On the plans list, click **Generate**.
1. Choose the **employee**.
2. Choose **from** and **to** dates.
3. Confirm. The app creates daily plans from the cycle and reports how many were **created** and **skipped** (for example if a plan already exists).
## Who uses the plan
- **Web operators** create and generate plans, then attach documents.
- **Sales and drivers on mobile** see today's plan after they exist. They can add a customer to an existing plan; they cannot create a new plan on the phone.
See [Mobile daily work](/user/mobile/daily-work).
@@ -0,0 +1,154 @@
<script setup>
import { ShoppingCart, ClipboardList, Box, Receipt, CreditCard } from 'lucide-vue-next'
</script>
# Sales workflow
<p class="doc-lead"><ShoppingCart :size="20" class="doc-icon" /> Sales → Activities</p>
How to run a sale: from a request or order, through packing and invoicing, to payment.
You need permission to view each menu. Status buttons (Process, Complete, Submit, Approve, and so on) appear only if you are allowed to edit.
## Before you start
Set up the records that every sales document needs:
| You need | Where to create it |
| --- | --- |
| Division, branch, customer, product | [Settings → Data](/user/web/setup) |
| Salesperson | Sales → Data → Employees |
If a menu is missing, ask an administrator to give your privilege **View** (and **Update** for status buttons) on that area.
## The usual flow
```plantuml
@startuml
start
:Sales request (optional);
:Sales order (Draft);
:Process order;
fork
:Sales invoice created;
fork again
:Packing slip created\n(unless you skip it);
end fork
if (Packing slip exists?) then (yes)
:Complete delivered quantities;
endif
:Create sales payment;
:Submit then Approve;
stop
@enduml
```
1. Optionally create and approve a **sales request**.
2. Create a **sales order** (from the request, or from scratch).
3. Open the draft order and click **Process**. Leave **Generate packing slip** checked unless you do not need delivery paperwork. Processing creates a **sales invoice**, and a **packing slip** unless you skip it.
4. If there is a packing slip, open **Logistics → Packing slips**, then **Complete** and enter how much was delivered.
5. Open the invoice and click **Create Sales Payment**. **Submit** the payment, then **Approve** it (or **Reject** / **Rollback**).
You can also [import CSV](/user/web/import) for requests, orders, invoices, and payments.
## Sales request
<p class="doc-lead"><ClipboardList :size="18" class="doc-icon" /> Sales → Activities → Sales Requests</p>
A request is optional. You can go straight to a sales order.
1. Click **Create**. Fill in date, salesperson, branch, division, customer, address, and at least one product with a quantity. Product price and document code are optional. Save.
2. While the status is **Draft**, click **Submit** to send it for approval. You can also use **Change status** to mark it **Rejected**.
3. While it is **Pending**, click **Approve** or **Reject**. **Change status** can send it back to **Draft**.
4. After it is **Approved**, there is nothing more to do on the request itself.
5. On the request detail page, click **Create Sales Order**. The order form opens with the request's customer, location, and products already filled in.
## Sales order
<p class="doc-lead"><Box :size="18" class="doc-icon" /> Sales → Activities → Sales Orders</p>
1. Click **Create**. Fill in the same kind of details as a request. On a new order you can pick a sales request under **Source**; that copies the request into the form. Save. A new order starts as **Draft**.
2. On a **Draft** order, click **Process**.
- You will see: *Processing creates a sales invoice and, unless skipped, a packing slip.*
- **Generate packing slip** is checked by default. Uncheck it if you do not want a packing slip.
3. On **Draft** or **Processed**, you can **Cancel**. There is no **Complete** button on orders. If the order later shows **Completed**, that was not something you clicked on this screen.
4. On the order detail page you can see linked packing slips and invoices, and click **Create Sales Invoice**.
You can select several orders and Process or Cancel them together, as long as they are all in a status that allows that action.
## Packing slip
Packing slips live under **Logistics**, not Sales. See [Logistics](/user/web/logistics).
## Sales invoice
<p class="doc-lead"><Receipt :size="18" class="doc-icon" /> Sales → Activities → Sales Invoices</p>
Invoices appear when you **Process** an order. You can also create one yourself, optionally from an order or a packing slip.
On the invoice, the only status button is **Cancel** (while the invoice is **Draft**, **Processed**, or **Partial**). You cannot choose Processed, Partial, or Completed yourself. Those labels can still show on the invoice.
On the invoice detail page, click **Create Sales Payment**. The payment form opens with that invoice and its remaining balance.
You can attach invoices to a [sales plan](/user/web/plans). Saving the plan does not change invoice status.
## Sales payment
<p class="doc-lead"><CreditCard :size="18" class="doc-icon" /> Sales → Activities → Sales Payments</p>
1. Click **Create**. Enter the date and at least one line: an invoice and an amount. Images and notes are optional. Save. A new payment starts as **Draft**.
2. Click **Submit** to move it to **Pending**. You can also use **Change status** to mark it **Rejected**.
3. While it is **Pending**, click **Approve**, **Reject**, or **Rollback** (back to **Draft**).
4. After **Approved**, you cannot change the status further. A **Rejected** payment can be sent back to **Draft** with **Change status**.
Approving a payment does not change the invoice status from this screen. Refresh the invoice if you need to see an updated balance.
Field staff can also [record a payment on mobile](/user/mobile/daily-work) from an outstanding invoice.
## What you can do at each status
**Buttons** are the labelled actions on the list or detail page. **Change status** is a separate dialog that lists the statuses you may pick. Invoices do not have Change status.
### Sales request and sales payment
Both use Draft → Pending → Approved or Rejected.
| Current status | Buttons | Change status |
| --- | --- | --- |
| Draft | Submit | Rejected |
| Pending | Approve, Reject. Payments also have Rollback (back to Draft) | Draft, Approved, Rejected |
| Approved | — | — |
| Rejected | — | Draft |
### Sales order
| Current status | Buttons | Change status |
| --- | --- | --- |
| Draft | Process, Cancel | Processed, Cancelled |
| Processed | Cancel | Cancelled |
| Completed | — | — |
| Cancelled | — | — |
**Process** always opens the packing-slip checkbox dialog (not Change status).
### Sales invoice
You may see Draft, Processed, Partial, Completed, or Cancelled.
| Current status | What you can do |
| --- | --- |
| Draft, Processed, or Partial | Cancel |
| Completed | No status buttons |
| Cancelled | No status buttons |
## What you should not expect to click
These are not buttons on the screens:
- **Complete** on a sales order
- Setting an invoice to Processed, Partial, or Completed yourself
- Changing an invoice's status by putting it on a sales plan
- Changing an invoice's status by approving a payment
- Cancelling an order, packing slip, or invoice in one click for all related documents — **Cancel** applies only to the document you are looking at
If a related document's status changes after you act, open that document again to see the latest value.
+78
View File
@@ -0,0 +1,78 @@
<script setup>
import { Settings, Users, Shield } from 'lucide-vue-next'
</script>
# Set up your company
<p class="doc-lead"><Settings :size="20" class="doc-icon" /> Settings → Data and Settings → User</p>
Do this before you create plans or sales documents. Records that other screens need (branch, division, customer, product, employee) must exist first.
## Company settings
**Menu:** Settings → Data → Company settings
This is a single form, not a list. Save:
| Field | Used for |
| --- | --- |
| Cycle start date | When weekly visit cycles start counting |
| Check-in radius (meters) | How close field staff should be when checking in |
| GPS interval (seconds) | How often the mobile app records a footprint while on duty |
| Checkout warning radius (meters) | When mobile warns that the user left a customer visit |
## Divisions, branches, customers, products
**Menu:** Settings → Data
Each of these is a list with Create, Detail, Edit, and Duplicate. Status is **Draft**, **Active**, or **Archived**.
| Record | Typical fields |
| --- | --- |
| Divisions | Name, code |
| Branches | Code, name, phone, NFC ID, address, map location, division, working days and hours |
| Customers | Code, name, phone, address, map location, NFC ID; contacts on create |
| Products | Code, name, unit, price, brand |
Pick a location on the map for branches and customers (or type latitude and longitude).
## Employees
Employees are split by purpose:
| Menu | Positions |
| --- | --- |
| Sales → Data → Employees | Sales |
| Logistics → Data → Employees | Driver, crew |
Fields: code, name, phone, position. Link a **user** to an employee later so mobile knows whether to load a sales or logistics plan.
## Privileges
<p class="doc-lead"><Shield :size="18" class="doc-icon" /> Settings → User → Privileges</p>
A privilege is a named access set. On the form, set **name**, **code**, and a permission matrix. For each area you can allow:
- **View** — show the menu and open the pages
- **Create** — create and duplicate
- **Update** — edit and change status
- **Delete** — delete
- **Import** — CSV import on supported lists
Without **View**, the sidebar hides that item.
## Users
<p class="doc-lead"><Users :size="18" class="doc-icon" /> Settings → User → Users</p>
1. Click **Create**.
2. Enter **username** and **password** (password is required on create; on edit you can leave it blank to keep the current one).
3. Choose a **privilege**.
4. Optionally link an **employee** so field login maps to sales or logistics.
Activate the user when they should be able to sign in.
## Coming soon
- **Remember me** on the web login form is not saved
- **Forgot password** is not available
+38
View File
@@ -0,0 +1,38 @@
<script setup>
import { MapPin } from 'lucide-vue-next'
</script>
# Timeline
<p class="doc-lead"><MapPin :size="20" class="doc-icon" /> Timeline in the sidebar</p>
Timeline shows **one day's** recorded GPS footprints and field activities for **sales** employees. It is a playback of what the mobile app stored while the person was on duty, not a live feed.
## How to use it
1. Open **Timeline**.
2. Pick a **date**.
3. Optionally filter by **employee** (sales employees), or leave all employees.
4. Search activities if the list is long.
5. Switch tabs **On the way** and **Completed**.
6. Use **Playback** (play, pause, time) to move along the recorded path. The overlay **Current location** is the playback cursor, not a live GPS point.
If there is no data for that day, the map shows that there is no timeline data.
## What activities look like
The list can include:
- Branch check-in / check-out
- Customer check-in / check-out
- Sales order created
- Sales request created
- Sales payment created
- Customer created
Footprints are recorded on mobile after branch clock-in, using the **GPS interval** in [company settings](/user/web/setup).
## Coming soon
- **Live** tracking of sales or drivers on the web map
- A dedicated **logistics / driver** timeline (this screen loads sales employees)
+19 -18
View File
@@ -54,6 +54,9 @@ importers:
dayjs:
specifier: ^1.11.19
version: 1.11.19
lucide-vue-next:
specifier: ^1.0.0
version: 1.0.0(vue@3.5.38)
react:
specifier: ^19.2.3
version: 19.2.3
@@ -73,6 +76,9 @@ importers:
mermaid:
specifier: ^11.15.0
version: 11.15.0
plantuml-encoder:
specifier: ^1.4.0
version: 1.4.0
typescript:
specifier: 5.5.4
version: 5.5.4
@@ -1041,7 +1047,6 @@ packages:
/@babel/helper-string-parser@7.29.7:
resolution: {integrity: sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==}
engines: {node: '>=6.9.0'}
dev: true
/@babel/helper-validator-identifier@7.28.5:
resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==}
@@ -1050,7 +1055,6 @@ packages:
/@babel/helper-validator-identifier@7.29.7:
resolution: {integrity: sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==}
engines: {node: '>=6.9.0'}
dev: true
/@babel/helper-validator-option@7.27.1:
resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==}
@@ -1076,7 +1080,6 @@ packages:
hasBin: true
dependencies:
'@babel/types': 7.29.7
dev: true
/@babel/plugin-transform-arrow-functions@7.27.1(@babel/core@7.28.5):
resolution: {integrity: sha512-8Z4TGic6xW70FKThA5HYEKKyBpOOsucTOD1DjU3fZxDg+K3zBJcXMFnt/4yQiZnf5+MiOMSXQ9PaEK/Ilh1DeA==}
@@ -1147,7 +1150,6 @@ packages:
dependencies:
'@babel/helper-string-parser': 7.29.7
'@babel/helper-validator-identifier': 7.29.7
dev: true
/@braintree/sanitize-url@6.0.4:
resolution: {integrity: sha512-s3jaWicZd0pkP0jf5ysyHUI/RE7MHos6qlToFcGWXVp+ykHOy77OUMrfbgJ9it2C5bow7OIQwYYaHjk9XlBQ2A==}
@@ -4791,14 +4793,12 @@ packages:
entities: 7.0.1
estree-walker: 2.0.2
source-map-js: 1.2.1
dev: true
/@vue/compiler-dom@3.5.38:
resolution: {integrity: sha512-JTqp25l8aFfJYF7/KmsXZjAxJz7T+SjmTJLoXVjHtc2BrSgSiW2n9Aem/cWq1OPe68A8JL06B3eVdhlP0H4TVw==}
dependencies:
'@vue/compiler-core': 3.5.38
'@vue/shared': 3.5.38
dev: true
/@vue/compiler-sfc@3.5.38:
resolution: {integrity: sha512-DuA2GiZawSEW442iw/9+Fkol8hTgb4Ke5KkhmSry65QA7YuyMbIdy8p0XZRMvNwJdgRz307W8g1CSzdvS4nuNg==}
@@ -4812,14 +4812,12 @@ packages:
magic-string: 0.30.21
postcss: 8.5.15
source-map-js: 1.2.1
dev: true
/@vue/compiler-ssr@3.5.38:
resolution: {integrity: sha512-7s+W5Gc42FGxZMcuwl8H5B29T8BJPMdBT7KHFE+BbAuZ/iTEdTtv7z2XiMjiaUUw4w3ZcCEdHs36RuYJ2VA7bA==}
dependencies:
'@vue/compiler-dom': 3.5.38
'@vue/shared': 3.5.38
dev: true
/@vue/devtools-api@7.7.9:
resolution: {integrity: sha512-kIE8wvwlcZ6TJTbNeU2HQNtaxLx3a84aotTITUuL/4bzfPxzajGBOoqjMhwZJ8L9qFYDU/lAYMEEm11dnZOD6g==}
@@ -4849,14 +4847,12 @@ packages:
resolution: {integrity: sha512-pG6LV/NDNRbKizcUjFFLAfjaL8mcv4DmR9avNcUw2gDHBzZneuS2TWCmp633ynzxz9YYKNeEPK2I8Wraqy2HUQ==}
dependencies:
'@vue/shared': 3.5.38
dev: true
/@vue/runtime-core@3.5.38:
resolution: {integrity: sha512-iyW8WVfF1CpCXxncZY5Ei6rSd6oZr5DgEom//fUjRBRl56AXPD+s9ATvukRt77ZFTuYlnVA1bxY+dJB94tWVYw==}
dependencies:
'@vue/reactivity': 3.5.38
'@vue/shared': 3.5.38
dev: true
/@vue/runtime-dom@3.5.38:
resolution: {integrity: sha512-apX2wt9sdfDshS+a2xueFZLVpt0GkRJZSoPmrW/SA4yzXTznhfcMVW59gr7h4YQeY0vJhdJkk2rsIDwgfFgC5A==}
@@ -4865,7 +4861,6 @@ packages:
'@vue/runtime-core': 3.5.38
'@vue/shared': 3.5.38
csstype: 3.2.3
dev: true
/@vue/server-renderer@3.5.38(vue@3.5.38):
resolution: {integrity: sha512-vue8vbf2QlV4quHqzwmJy6dWfmRhP1J8l4wtZg60CL6VoKqcPY2oe7may3+1d9qfpedjK5PRLFqd5k3Isj9mUw==}
@@ -4875,11 +4870,9 @@ packages:
'@vue/compiler-ssr': 3.5.38
'@vue/shared': 3.5.38
vue: 3.5.38(typescript@5.5.4)
dev: true
/@vue/shared@3.5.38:
resolution: {integrity: sha512-FTW0AFZNaK5/mOqvGBwVfUlNLU38TiQn4+DQgIFUnrBBJQ1crMJ82yeGQLV5jyKFsO8yRukpbuP7x+nRbH6aug==}
dev: true
/@vueuse/core@12.8.2(typescript@5.5.4):
resolution: {integrity: sha512-HbvCmZdzAu3VGi/pWYm5Ut+Kd9mn1ZHnn4L5G8kOQTPs/IwIAmJoBrmYk2ckLArgMXZj0AW3n5CAejLUO+PhdQ==}
@@ -6733,7 +6726,6 @@ packages:
/entities@7.0.1:
resolution: {integrity: sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==}
engines: {node: '>=0.12'}
dev: true
/env-paths@2.2.1:
resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==}
@@ -7442,7 +7434,6 @@ packages:
/estree-walker@2.0.2:
resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==}
dev: true
/estree-walker@3.0.3:
resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==}
@@ -9007,6 +8998,15 @@ packages:
react: 19.2.3
dev: false
/lucide-vue-next@1.0.0(vue@3.5.38):
resolution: {integrity: sha512-V6SPvx1IHTj/UY+FrIYWV5faISsPSb8BnWSFDxAtezWKvWc9ZZ40PDrdu1/Qb5vg4lHWr1hs1BAMGVGm6V1Xdg==}
deprecated: Package deprecated. Please use @lucide/vue instead.
peerDependencies:
vue: '>=3.0.1'
dependencies:
vue: 3.5.38(typescript@5.5.4)
dev: false
/lz-string@1.5.0:
resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==}
hasBin: true
@@ -9661,7 +9661,6 @@ packages:
resolution: {integrity: sha512-y7Wygv/7mEOvxTuEQDB8StXdMRBWf1kR/tlhAzBRUFkB2jfcLOAxO/SHmOO2zgz1pVgK29/kyupn059/bCHdjA==}
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
hasBin: true
dev: true
/napi-postinstall@0.3.4:
resolution: {integrity: sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ==}
@@ -10117,6 +10116,10 @@ packages:
engines: {node: '>=12'}
dev: false
/plantuml-encoder@1.4.0:
resolution: {integrity: sha512-sxMwpDw/ySY1WB2CE3+IdMuEcWibJ72DDOsXLkSmEaSzwEUaYBT6DWgOfBiHGCux4q433X6+OEFWjlVqp7gL6g==}
dev: true
/playwright-core@1.62.1:
resolution: {integrity: sha512-wPYSwEBJY9GHraISXqyqtx0na0LpO3XEX7jNDhntbex7tzUS7kLnZsOlFruFJB4Hi/rhDMjXGqHewDZ68nYZVw==}
engines: {node: '>=20'}
@@ -10170,7 +10173,6 @@ packages:
nanoid: 3.3.15
picocolors: 1.1.1
source-map-js: 1.2.1
dev: true
/postcss@8.5.3:
resolution: {integrity: sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==}
@@ -12718,7 +12720,6 @@ packages:
'@vue/server-renderer': 3.5.38(vue@3.5.38)
'@vue/shared': 3.5.38
typescript: 5.5.4
dev: true
/vuvuzela@1.0.3:
resolution: {integrity: sha512-Tm7jR1xTzBbPW+6y1tknKiEhz04Wf/1iZkcTJjSFcpNko43+dFW6+OOeQe9taJIug3NdfUAjFKgUSyQrIKaDvQ==}