diff --git a/api.md b/api.md
index ea84556..91a60da 100644
--- a/api.md
+++ b/api.md
@@ -242,40 +242,40 @@ HTTP mapping:
Catalog (`GET /privilege-keys`, needs `ADMIN.SETTINGS.USER.PRIVILEGES` `view`). Keys use `Group.Parent.Module` or `Group.Parent.Module.Submodule`:
-| code | label |
-| ---- | ----- |
-| `ADMIN.SETTINGS.USER.PRIVILEGES` | Privileges |
-| `ADMIN.SETTINGS.USER.USERS` | Users |
-| `ADMIN.SETTINGS.DATA.DIVISION` | Divisions |
-| `ADMIN.SETTINGS.DATA.BRANCH` | Branches |
-| `ADMIN.SETTINGS.DATA.CUSTOMER` | Customers |
-| `ADMIN.SETTINGS.DATA.PRODUCT` | Products |
-| `ADMIN.SETTINGS.DATA.SETTING` | Company settings |
-| `ADMIN.SALES.DATA.EMPLOYEE` | Employees |
-| `ADMIN.SALES.DATA.CYCLE` | Sales cycles |
-| `ADMIN.SALES.ACTIVITIES.REQUEST` | Sales requests |
-| `ADMIN.SALES.ACTIVITIES.ORDER` | Sales orders |
-| `ADMIN.SALES.ACTIVITIES.INVOICE` | Sales invoices |
-| `ADMIN.SALES.ACTIVITIES.PAYMENT` | Sales payments |
-| `ADMIN.SALES.ACTIVITIES.PLAN` | Sales plans |
-| `ADMIN.SALES.ACTIVITIES.TIMELINE` | Sales timeline |
-| `ADMIN.SALES.REPORT` | Sales reports |
-| `ADMIN.LOGISTICS.ACTIVITIES.PACKING_SLIP` | Packing slips |
-| `ADMIN.LOGISTICS.DATA.CYCLE` | Logistics cycles |
-| `ADMIN.LOGISTICS.ACTIVITIES.PLAN` | Logistics plans |
-| `ADMIN.LOGISTICS.REPORT` | Logistics reports |
-| `MOBILE.SALES.PLAN` | Sales plans (mobile) |
-| `MOBILE.SALES.PLAN.ATTENDANCE` | Branch attendance |
-| `MOBILE.SALES.VISIT` | Customer visits |
-| `MOBILE.SALES.TIMELINE` | Sales timeline (mobile) |
+| code | label |
+| ----------------------------------------- | ----------------------- |
+| `ADMIN.SETTINGS.USER.PRIVILEGES` | Privileges |
+| `ADMIN.SETTINGS.USER.USERS` | Users |
+| `ADMIN.SETTINGS.DATA.DIVISION` | Divisions |
+| `ADMIN.SETTINGS.DATA.BRANCH` | Branches |
+| `ADMIN.SETTINGS.DATA.CUSTOMER` | Customers |
+| `ADMIN.SETTINGS.DATA.PRODUCT` | Products |
+| `ADMIN.SETTINGS.DATA.SETTING` | Company settings |
+| `ADMIN.SALES.DATA.EMPLOYEE` | Employees |
+| `ADMIN.SALES.DATA.CYCLE` | Sales cycles |
+| `ADMIN.SALES.ACTIVITIES.REQUEST` | Sales requests |
+| `ADMIN.SALES.ACTIVITIES.ORDER` | Sales orders |
+| `ADMIN.SALES.ACTIVITIES.INVOICE` | Sales invoices |
+| `ADMIN.SALES.ACTIVITIES.PAYMENT` | Sales payments |
+| `ADMIN.SALES.ACTIVITIES.PLAN` | Sales plans |
+| `ADMIN.SALES.ACTIVITIES.TIMELINE` | Sales timeline |
+| `ADMIN.SALES.REPORT` | Sales reports |
+| `ADMIN.LOGISTICS.ACTIVITIES.PACKING_SLIP` | Packing slips |
+| `ADMIN.LOGISTICS.DATA.CYCLE` | Logistics cycles |
+| `ADMIN.LOGISTICS.ACTIVITIES.PLAN` | Logistics plans |
+| `ADMIN.LOGISTICS.REPORT` | Logistics reports |
+| `MOBILE.SALES.PLAN` | Sales plans (mobile) |
+| `MOBILE.SALES.PLAN.ATTENDANCE` | Branch attendance |
+| `MOBILE.SALES.VISIT` | Customer visits |
+| `MOBILE.SALES.TIMELINE` | Sales timeline (mobile) |
### Field purpose
Cycles and plans do **not** use a single key. Privilege is resolved from `purpose`:
-| purpose | cycle keys | plan keys |
-| ----------- | ---------- | --------- |
-| `sales` | `ADMIN.SALES.DATA.CYCLE` | `ADMIN.SALES.ACTIVITIES.PLAN`, `MOBILE.SALES.PLAN` |
+| purpose | cycle keys | plan keys |
+| ----------- | ---------------------------- | ---------------------------------------------------------- |
+| `sales` | `ADMIN.SALES.DATA.CYCLE` | `ADMIN.SALES.ACTIVITIES.PLAN`, `MOBILE.SALES.PLAN` |
| `logistics` | `ADMIN.LOGISTICS.DATA.CYCLE` | `ADMIN.LOGISTICS.ACTIVITIES.PLAN`, `MOBILE.LOGISTICS.PLAN` |
`purpose` is read from **body** (writes) or **query** (lists). If omitted, the user may proceed if they have the action on **either** purpose; list results are filtered to purposes they can view. Superadmin bypasses.
diff --git a/apps/web/src/apps/auth/login/components/right-section.tsx b/apps/web/src/apps/auth/login/components/right-section.tsx
index 9d8d49e..ec9e355 100644
--- a/apps/web/src/apps/auth/login/components/right-section.tsx
+++ b/apps/web/src/apps/auth/login/components/right-section.tsx
@@ -104,12 +104,6 @@ export function RightSection() {
-
- Compatible with{' '}
- Gmail, Outlook Web, LinkedIn and most web editors for a
- smooth writing experience anywhere online.
-
-
{/* Pagination Indicators */}
diff --git a/apps/web/src/apps/main/modules/configuration/company-settings/data/company-settings.remote.service.ts b/apps/web/src/apps/main/modules/configuration/company-settings/data/company-settings.remote.service.ts
index 3ece50d..fc31808 100644
--- a/apps/web/src/apps/main/modules/configuration/company-settings/data/company-settings.remote.service.ts
+++ b/apps/web/src/apps/main/modules/configuration/company-settings/data/company-settings.remote.service.ts
@@ -1,8 +1,5 @@
import type { AxiosInstance } from '@repo/core-api/http-client';
-import type {
- CompanySettingsEntity,
- UpdateCompanySettingsPayload,
-} from '../domain/entities/company-settings.entity';
+import type { CompanySettingsEntity, UpdateCompanySettingsPayload } from '../domain/entities/company-settings.entity';
export class CompanySettingsRemoteService {
constructor(private readonly client: AxiosInstance) {}
diff --git a/apps/web/src/apps/main/modules/configuration/company-settings/domain/factories/index.ts b/apps/web/src/apps/main/modules/configuration/company-settings/domain/factories/index.ts
index fc3d0b1..9489f74 100644
--- a/apps/web/src/apps/main/modules/configuration/company-settings/domain/factories/index.ts
+++ b/apps/web/src/apps/main/modules/configuration/company-settings/domain/factories/index.ts
@@ -1,10 +1,7 @@
import { BaseDataTransformer } from '@repo/core-api/data-services';
import { apiClient } from '../../../../../../../core/lib/api-client';
import { TrackGoRemoteDataServices } from '../../../../../../../core/lib/trackgo-remote-data-services';
-import {
- companySettingsModuleConfig,
- type CompanySettingsShellEntity,
-} from '../constants/company-settings.constants';
+import { companySettingsModuleConfig, type CompanySettingsShellEntity } from '../constants/company-settings.constants';
import { CompanySettingsRemoteService } from '../../data/company-settings.remote.service';
class CompanySettingsShellTransformer extends BaseDataTransformer
{
diff --git a/apps/web/src/apps/main/modules/configuration/company-settings/presentation/factory/index.tsx b/apps/web/src/apps/main/modules/configuration/company-settings/presentation/factory/index.tsx
index 0662b8c..3348d26 100644
--- a/apps/web/src/apps/main/modules/configuration/company-settings/presentation/factory/index.tsx
+++ b/apps/web/src/apps/main/modules/configuration/company-settings/presentation/factory/index.tsx
@@ -25,10 +25,7 @@ export default function CompanySettingsModule() {
>
} />
- }
- />
+ } />
} />
diff --git a/apps/web/src/apps/main/modules/configuration/company-settings/presentation/pages/company-settings.page.tsx b/apps/web/src/apps/main/modules/configuration/company-settings/presentation/pages/company-settings.page.tsx
index f9dbda4..5fb41c7 100644
--- a/apps/web/src/apps/main/modules/configuration/company-settings/presentation/pages/company-settings.page.tsx
+++ b/apps/web/src/apps/main/modules/configuration/company-settings/presentation/pages/company-settings.page.tsx
@@ -91,11 +91,7 @@ export default function CompanySettingsPage() {
-
+
diff --git a/apps/web/src/apps/main/modules/field/timeline/presentation/components/timeline-activity-panel.tsx b/apps/web/src/apps/main/modules/field/timeline/presentation/components/timeline-activity-panel.tsx
index 6c01baa..459b68a 100644
--- a/apps/web/src/apps/main/modules/field/timeline/presentation/components/timeline-activity-panel.tsx
+++ b/apps/web/src/apps/main/modules/field/timeline/presentation/components/timeline-activity-panel.tsx
@@ -1,12 +1,5 @@
import type { ReactNode } from 'react';
-import {
- Paper,
- ScrollArea,
- SegmentedControl,
- Stack,
- Text,
- TextInput,
-} from '@repo/ui/components';
+import { Paper, ScrollArea, SegmentedControl, Stack, Text, TextInput } from '@repo/ui/components';
import { Search } from 'lucide-react';
import { TimelineActivityList } from './timeline-activity-list';
import type { TimelineActivityGroup, TimelineActivityTab } from './timeline-helpers';
diff --git a/apps/web/src/apps/main/modules/field/timeline/presentation/components/timeline-helpers.test.ts b/apps/web/src/apps/main/modules/field/timeline/presentation/components/timeline-helpers.test.ts
index 6794bc9..40b3c62 100644
--- a/apps/web/src/apps/main/modules/field/timeline/presentation/components/timeline-helpers.test.ts
+++ b/apps/web/src/apps/main/modules/field/timeline/presentation/components/timeline-helpers.test.ts
@@ -115,7 +115,9 @@ describe('timeline helpers', () => {
'visit-1',
'ungrouped',
]);
- expect(filterActivityGroups(groups, { tab: 'completed', query: '' }).map((group) => group.key)).toEqual(['visit-2']);
+ expect(filterActivityGroups(groups, { tab: 'completed', query: '' }).map((group) => group.key)).toEqual([
+ 'visit-2',
+ ]);
expect(filterActivityGroups(groups, { tab: 'on_the_way', query: 'maju' }).map((group) => group.title)).toEqual([
'Toko Maju',
]);
diff --git a/apps/web/src/apps/main/modules/field/timeline/presentation/components/timeline-helpers.tsx b/apps/web/src/apps/main/modules/field/timeline/presentation/components/timeline-helpers.tsx
index e94dd3a..fa7bf43 100644
--- a/apps/web/src/apps/main/modules/field/timeline/presentation/components/timeline-helpers.tsx
+++ b/apps/web/src/apps/main/modules/field/timeline/presentation/components/timeline-helpers.tsx
@@ -1,7 +1,4 @@
-import type {
- TimelineActivityEntity,
- TimelineFootprintEntity,
-} from '../../domain/entities/timeline.entity';
+import type { TimelineActivityEntity, TimelineFootprintEntity } from '../../domain/entities/timeline.entity';
export type TimelineActivityGroup = {
key: string;
@@ -46,9 +43,7 @@ export function groupActivities(activities: TimelineActivityEntity[]): TimelineA
const sorted = sortByRecordedAt(items);
const last = sorted[sorted.length - 1];
const first = sorted[0];
- const title =
- last?.customer?.name ??
- (key === 'ungrouped' ? 'ungrouped' : (last?.customer?.code ?? key));
+ const title = last?.customer?.name ?? (key === 'ungrouped' ? 'ungrouped' : (last?.customer?.code ?? key));
return {
key,
@@ -95,10 +90,7 @@ export function resolvePlaybackPositions(
return [];
}
- const latestByEmployee = new Map<
- string,
- { latitude: number; longitude: number; label: string }
- >();
+ const latestByEmployee = new Map();
for (const footprint of footprints) {
if (footprint.recordedAt > playbackTime) {
@@ -155,10 +147,6 @@ export function shouldRestartPlayback(
return playbackTime === null || playbackTime >= bounds.max;
}
-export function advancePlaybackTime(
- current: number,
- bounds: { min: number; max: number },
- step: number,
-): number {
+export function advancePlaybackTime(current: number, bounds: { min: number; max: number }, step: number): number {
return Math.min(bounds.max, current + step);
}
diff --git a/apps/web/src/apps/main/modules/field/timeline/presentation/components/timeline-playback-overlay.tsx b/apps/web/src/apps/main/modules/field/timeline/presentation/components/timeline-playback-overlay.tsx
index b2d54ac..bcb6081 100644
--- a/apps/web/src/apps/main/modules/field/timeline/presentation/components/timeline-playback-overlay.tsx
+++ b/apps/web/src/apps/main/modules/field/timeline/presentation/components/timeline-playback-overlay.tsx
@@ -104,10 +104,7 @@ export function TimelinePlaybackOverlay({
value={last ? `${formatClock(last.recordedAt)} · ${activityLabel(last.type)}` : '—'}
/>
-
+
diff --git a/apps/web/src/apps/main/modules/field/timeline/presentation/factory/index.tsx b/apps/web/src/apps/main/modules/field/timeline/presentation/factory/index.tsx
index 89ee69b..a6c18d8 100644
--- a/apps/web/src/apps/main/modules/field/timeline/presentation/factory/index.tsx
+++ b/apps/web/src/apps/main/modules/field/timeline/presentation/factory/index.tsx
@@ -25,10 +25,7 @@ export default function SalesTimelineModule() {
>
} />
- }
- />
+ } />
} />
diff --git a/apps/web/src/apps/main/modules/field/timeline/presentation/pages/timeline.page.index.tsx b/apps/web/src/apps/main/modules/field/timeline/presentation/pages/timeline.page.index.tsx
index 1d2bd3e..e71db0e 100644
--- a/apps/web/src/apps/main/modules/field/timeline/presentation/pages/timeline.page.index.tsx
+++ b/apps/web/src/apps/main/modules/field/timeline/presentation/pages/timeline.page.index.tsx
@@ -153,10 +153,7 @@ export default function TimelinePage() {
[footprints, playbackTime],
);
const groups = useMemo(() => groupActivities(visibleActivities), [visibleActivities]);
- const filteredGroups = useMemo(
- () => filterActivityGroups(groups, { tab, query: search }),
- [groups, search, tab],
- );
+ const filteredGroups = useMemo(() => filterActivityGroups(groups, { tab, query: search }), [groups, search, tab]);
useEffect(() => {
if (filteredGroups.length === 0) {
@@ -179,9 +176,7 @@ export default function TimelinePage() {
);
const focusPositions = useMemo(() => {
if (selectedGroup && selectedGroup.activities.length > 0) {
- return selectedGroup.activities.map(
- (activity): [number, number] => [activity.latitude, activity.longitude],
- );
+ return selectedGroup.activities.map((activity): [number, number] => [activity.latitude, activity.longitude]);
}
return dayPositions;
}, [dayPositions, selectedGroup]);
@@ -195,10 +190,7 @@ export default function TimelinePage() {
})),
[visibleFootprints],
);
- const activityLabel = useCallback(
- (type: string) => t(`activities.types.${type}`, { defaultValue: type }),
- [t],
- );
+ const activityLabel = useCallback((type: string) => t(`activities.types.${type}`, { defaultValue: type }), [t]);
const mapActivities = useMemo(
() =>
visibleActivities.map((activity) => ({
@@ -282,13 +274,7 @@ export default function TimelinePage() {
-
+
{
expect(grouped.map((tab) => tab.group)).toEqual(['ADMIN', 'MOBILE']);
expect(grouped[0]?.parents[0]?.parent).toBe('SALES');
- expect(grouped[0]?.parents[0]?.modules.map((m) => m.module)).toEqual([
- 'ACTIVITIES',
- 'DATA',
- ]);
+ expect(grouped[0]?.parents[0]?.modules.map((m) => m.module)).toEqual(['ACTIVITIES', 'DATA']);
expect(grouped[1]?.parents[0]?.modules[0]?.directRow?.code).toBe('MOBILE.SALES.PLAN');
- expect(grouped[1]?.parents[0]?.modules[0]?.submodules[0]?.code).toBe(
- 'MOBILE.SALES.PLAN.ATTENDANCE',
- );
+ expect(grouped[1]?.parents[0]?.modules[0]?.submodules[0]?.code).toBe('MOBILE.SALES.PLAN.ATTENDANCE');
});
});
@@ -72,10 +67,7 @@ describe('flattenParentSectionRows', () => {
expect(salesParent).toBeDefined();
const tableRows = flattenParentSectionRows(salesParent!);
- expect(tableRows.map((r) => r.code)).toEqual([
- 'ADMIN.SALES.DATA.CYCLE',
- 'ADMIN.SALES.ACTIVITIES.PLAN',
- ]);
+ expect(tableRows.map((r) => r.code)).toEqual(['ADMIN.SALES.DATA.CYCLE', 'ADMIN.SALES.ACTIVITIES.PLAN']);
expect(tableRows[0]).toMatchObject({ module: 'DATA', submodule: 'CYCLE' });
expect(tableRows[1]).toMatchObject({ module: 'ACTIVITIES', submodule: 'PLAN' });
});
diff --git a/apps/web/src/apps/main/modules/system/privileges/presentation/components/privilege-matrix-layout.tsx b/apps/web/src/apps/main/modules/system/privileges/presentation/components/privilege-matrix-layout.tsx
index b1af7a0..a6c42d8 100644
--- a/apps/web/src/apps/main/modules/system/privileges/presentation/components/privilege-matrix-layout.tsx
+++ b/apps/web/src/apps/main/modules/system/privileges/presentation/components/privilege-matrix-layout.tsx
@@ -28,10 +28,7 @@ function segmentLabel(t: (key: string) => string, kind: string, value: string) {
return translated === key ? formatPrivilegeSegment(value) : translated;
}
-function submoduleCellLabel(
- t: (key: string) => string,
- row: PrivilegeTableRow,
-) {
+function submoduleCellLabel(t: (key: string) => string, row: PrivilegeTableRow) {
if (row.submodule) {
return row.label || segmentLabel(t, 'submodule', row.submodule);
}
@@ -53,11 +50,7 @@ function MatrixActionCells({
{mode === 'edit' && formControl ? (
-
+
) : row.cell[action] ? (
) : (
@@ -137,12 +130,7 @@ function ParentMatrixTable({
);
}
-export function PrivilegeMatrixLayout({
- groups,
- t,
- mode,
- formControl,
-}: PrivilegeMatrixLayoutProps) {
+export function PrivilegeMatrixLayout({ groups, t, mode, formControl }: PrivilegeMatrixLayoutProps) {
if (groups.length === 0) {
return null;
}
diff --git a/apps/web/src/core/lib/enterprise-storage-adapter.test.ts b/apps/web/src/core/lib/enterprise-storage-adapter.test.ts
index 04570ad..9c4a7fd 100644
--- a/apps/web/src/core/lib/enterprise-storage-adapter.test.ts
+++ b/apps/web/src/core/lib/enterprise-storage-adapter.test.ts
@@ -27,7 +27,9 @@ describe('enterpriseStorageAdapter', () => {
return null;
});
- await expect(enterpriseStorageAdapter.getPrivileges('ADMIN.SETTINGS.USER.PRIVILEGES')).resolves.toEqual(defaultPrivileges);
+ await expect(enterpriseStorageAdapter.getPrivileges('ADMIN.SETTINGS.USER.PRIVILEGES')).resolves.toEqual(
+ defaultPrivileges,
+ );
await expect(enterpriseStorageAdapter.getPrivileges('UNKNOWN')).resolves.toEqual(defaultPrivileges);
});
diff --git a/apps/web/src/core/lib/filter-menu-by-view-privilege.test.ts b/apps/web/src/core/lib/filter-menu-by-view-privilege.test.ts
index a75d825..c10d6af 100644
--- a/apps/web/src/core/lib/filter-menu-by-view-privilege.test.ts
+++ b/apps/web/src/core/lib/filter-menu-by-view-privilege.test.ts
@@ -94,7 +94,11 @@ describe('filterMenuByViewPrivilege', () => {
},
];
- const filtered = filterMenuByViewPrivilege(menu, { 'ADMIN.SALES.ACTIVITIES.ORDER': { ...noPrivileges, ALLOW_VIEW: true } }, false);
+ const filtered = filterMenuByViewPrivilege(
+ menu,
+ { 'ADMIN.SALES.ACTIVITIES.ORDER': { ...noPrivileges, ALLOW_VIEW: true } },
+ false,
+ );
expect(filtered.find((item) => item.key === 'sales')?.children?.map((child) => child.key)).toEqual([
'orders',
diff --git a/packages/ui/src/components/map/timeline-map.tsx b/packages/ui/src/components/map/timeline-map.tsx
index 124781a..44e5782 100644
--- a/packages/ui/src/components/map/timeline-map.tsx
+++ b/packages/ui/src/components/map/timeline-map.tsx
@@ -1,13 +1,5 @@
import { useEffect, useMemo } from 'react';
-import {
- CircleMarker,
- MapContainer,
- Polyline,
- TileLayer,
- Tooltip,
- ZoomControl,
- useMap,
-} from 'react-leaflet';
+import { CircleMarker, MapContainer, Polyline, TileLayer, Tooltip, ZoomControl, useMap } from 'react-leaflet';
import { Box, Text } from '@mantine/core';
import { OSM_ATTRIBUTION, OSM_TILE_URL } from './osm';
import { DEFAULT_MAP_CENTER, DEFAULT_MAP_ZOOM } from './location-point';
@@ -76,11 +68,7 @@ function InvalidateSize() {
return null;
}
-function FitTimelineBounds({
- positions,
-}: {
- positions: Array<[number, number]>;
-}) {
+function FitTimelineBounds({ positions }: { positions: Array<[number, number]> }) {
const map = useMap();
const boundsKey = positions.map(([lat, lng]) => `${lat.toFixed(6)},${lng.toFixed(6)}`).join('|');
useEffect(() => {
@@ -97,9 +85,7 @@ function FitTimelineBounds({
return null;
}
-function groupFootprintsByEmployee(
- footprints: readonly TimelineMapFootprint[],
-): Map {
+function groupFootprintsByEmployee(footprints: readonly TimelineMapFootprint[]): Map {
const grouped = new Map();
for (const point of footprints) {
const existing = grouped.get(point.employeeId) ?? [];
@@ -125,10 +111,7 @@ export function TimelineMap({
fullBleed = false,
emptyLabel = 'No timeline data',
}: TimelineMapProps) {
- const groupedTracks = useMemo(
- () => groupFootprintsByEmployee(footprints),
- [footprints],
- );
+ const groupedTracks = useMemo(() => groupFootprintsByEmployee(footprints), [footprints]);
const positions = useMemo(() => {
const points: Array<[number, number]> = [];
@@ -144,18 +127,12 @@ export function TimelineMap({
return points;
}, [activities, footprints, playbackPositions]);
- const boundsPositions =
- focusPositions && focusPositions.length > 0 ? focusPositions : positions;
+ const boundsPositions = focusPositions && focusPositions.length > 0 ? focusPositions : positions;
const employeeIds = [...groupedTracks.keys()];
const hasData = positions.length > 0;
return (
-
+
{!hasData ? (
-
+
{emptyLabel}