Skip to content

Commit 9aed612

Browse files
chore(terminal): add Prettier config + format scripts to match admin (#56)
1 parent 387c774 commit 9aed612

30 files changed

Lines changed: 475 additions & 102 deletions

src/web/terminal/.prettierignore

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
dist
2+
web-build
3+
.expo
4+
node_modules
5+
6+
# Generated by release-please — not authored, not Prettier-shaped.
7+
CHANGELOG.md

src/web/terminal/.prettierrc.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"printWidth": 100,
3+
"tabWidth": 2,
4+
"singleQuote": true,
5+
"semi": true,
6+
"trailingComma": "all",
7+
"endOfLine": "auto"
8+
}

src/web/terminal/README.md

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# Warehouse Operator Terminal
22

33
The **Operator terminal** front end — a React Native (Expo) app for floor staff on a
4-
rugged handheld: *one hand, gloves, cold room, glare*. Scanner-first, huge tap targets
4+
rugged handheld: _one hand, gloves, cold room, glare_. Scanner-first, huge tap targets
55
(≥48 px), one task at a time, zero prose.
66

77
It implements the terminal screens from the design pass
@@ -46,22 +46,22 @@ data getter) + `index.ts` (public API). Cross-feature imports go through the fea
4646

4747
### A note on `app/` vs `navigation/`
4848

49-
expo-router is file-based: the tree under `src/app/` *is* the router. So the route files
49+
expo-router is file-based: the tree under `src/app/` _is_ the router. So the route files
5050
stay thin (a one-line re-export) and `navigation/routes.ts` holds the typed path map
5151
(`ROUTES.pack`) so features navigate by name instead of bare string literals.
5252

5353
## Screens & navigation
5454

55-
| Route | Feature screen | Use case | Prototype |
56-
|---|---|---|---|
57-
| `/` | `Tasks/TaskHubScreen` | landing | `terminal-1-hub` |
58-
| `/receive` | `Receiving/ReceiveScreen` | UC-02 | `terminal-2-receive` |
59-
| `/putaway` | `PutAway/PutAwayScreen` | UC-04 | `terminal-3-putaway` |
60-
| `/pick` | `Picking/PickingScreen` | UC-10 | `terminal-4-pick` |
61-
| `/move` | `Movement/MovementScreen` | UC-06 | `terminal-5-move` |
62-
| `/pack` | `Packing/PackingScreen` | UC-11 | `terminal-6-pack` |
63-
| `/scan` | `Scan/ScanScreen` || Scan tab |
64-
| `/lookup` | `Lookup/LookupScreen` || Look up tab |
55+
| Route | Feature screen | Use case | Prototype |
56+
| ---------- | ------------------------- | -------- | -------------------- |
57+
| `/` | `Tasks/TaskHubScreen` | landing | `terminal-1-hub` |
58+
| `/receive` | `Receiving/ReceiveScreen` | UC-02 | `terminal-2-receive` |
59+
| `/putaway` | `PutAway/PutAwayScreen` | UC-04 | `terminal-3-putaway` |
60+
| `/pick` | `Picking/PickingScreen` | UC-10 | `terminal-4-pick` |
61+
| `/move` | `Movement/MovementScreen` | UC-06 | `terminal-5-move` |
62+
| `/pack` | `Packing/PackingScreen` | UC-11 | `terminal-6-pack` |
63+
| `/scan` | `Scan/ScanScreen` | | Scan tab |
64+
| `/lookup` | `Lookup/LookupScreen` | | Look up tab |
6565

6666
Navigation mirrors the operator clickpath: the hub launches each task, and tasks loop
6767
back to the hub when done (`/pick` flows on to `/pack`).
@@ -72,10 +72,10 @@ The bottom nav switches between three top-level roots; `router.navigate` gives t
7272
behaviour (returning to a root already in history rather than stacking duplicates):
7373

7474
- **Tasks** — the hub: today's task piles.
75-
- **Scan***“I have a physical thing, what do I do with it?”* A context-free scanner
75+
- **Scan**_“I have a physical thing, what do I do with it?”_ A context-free scanner
7676
that resolves any code (ASN / order / EAN / LPN / location) and dispatches to the
7777
matching task.
78-
- **Look up***“I want to know something.”* Read-only, keyboard-driven search over
78+
- **Look up**_“I want to know something.”_ Read-only, keyboard-driven search over
7979
products (stock + ATP), locations (capacity, room type) and batches (status, BBE/FEFO).
8080
- **More** — no screen yet, so it stays dimmed and inert.
8181

@@ -91,8 +91,8 @@ that encode domain stock status** (never decoration). The shared primitives in
9191
- `BigActionButton` — confirm / exception / alternative; 56 px tap floor.
9292
- `Stepper`, `Chip`, `CheckRow`, `Card`, `TopBar`, `BottomNav`, `ScreenScaffold`.
9393

94-
The two **green check rows** on put-away / move make the *environment-compatibility
95-
invariant* visible; FEFO shows as a batch/BBE badge on picking.
94+
The two **green check rows** on put-away / move make the _environment-compatibility
95+
invariant_ visible; FEFO shows as a batch/BBE badge on picking.
9696

9797
## Running
9898

src/web/terminal/package-lock.json

Lines changed: 17 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/web/terminal/package.json

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@
1010
"web": "expo start --web",
1111
"build:web": "expo export --platform web",
1212
"lint": "expo lint",
13-
"typecheck": "tsc --noEmit"
13+
"typecheck": "tsc --noEmit",
14+
"format": "prettier --write .",
15+
"format:check": "prettier --check ."
1416
},
1517
"dependencies": {
1618
"@expo/vector-icons": "^15.0.2",
@@ -32,6 +34,7 @@
3234
},
3335
"devDependencies": {
3436
"@types/react": "~19.2.0",
37+
"prettier": "^3.8.4",
3538
"typescript": "^6.0.3"
3639
}
37-
}
40+
}

src/web/terminal/src/core/api/client.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,12 @@ class ApiError extends Error {
6969

7070
export { ApiError };
7171

72-
async function request<T>(method: string, resource: string, body?: unknown, retry = true): Promise<T> {
72+
async function request<T>(
73+
method: string,
74+
resource: string,
75+
body?: unknown,
76+
retry = true,
77+
): Promise<T> {
7378
const headers: Record<string, string> = {};
7479
if (body !== undefined) headers['Content-Type'] = 'application/json';
7580
if (activeWarehouseId) headers[WAREHOUSE_HEADER] = activeWarehouseId;
@@ -91,9 +96,11 @@ async function request<T>(method: string, resource: string, body?: unknown, retr
9196
if (!res.ok) {
9297
// Surface failures by their stable code (the same language the API returns).
9398
const payload = await res.json().catch(() => null);
94-
const code = payload && typeof payload === 'object' ? (payload as { code?: string }).code : undefined;
99+
const code =
100+
payload && typeof payload === 'object' ? (payload as { code?: string }).code : undefined;
95101
const message =
96-
(payload && typeof payload === 'object' && (payload as { message?: string }).message) || res.statusText;
102+
(payload && typeof payload === 'object' && (payload as { message?: string }).message) ||
103+
res.statusText;
97104
throw new ApiError(res.status, code, message);
98105
}
99106

src/web/terminal/src/core/api/useResource.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@ export interface Resource<T> {
1818
* (the module-level `getX` getters are stable), so the effect runs once.
1919
*/
2020
export function useResource<T>(fetcher: () => Promise<T>): Resource<T> {
21-
const [state, setState] = useState<{ data?: T; loading: boolean; error?: Error }>({ loading: true });
21+
const [state, setState] = useState<{ data?: T; loading: boolean; error?: Error }>({
22+
loading: true,
23+
});
2224
const [nonce, setNonce] = useState(0);
2325

2426
useEffect(() => {
@@ -29,7 +31,11 @@ export function useResource<T>(fetcher: () => Promise<T>): Resource<T> {
2931
fetcher().then(
3032
(data) => active && setState({ data, loading: false }),
3133
(error: unknown) =>
32-
active && setState({ loading: false, error: error instanceof Error ? error : new Error(String(error)) }),
34+
active &&
35+
setState({
36+
loading: false,
37+
error: error instanceof Error ? error : new Error(String(error)),
38+
}),
3339
);
3440
return () => {
3541
active = false;

src/web/terminal/src/features/Lookup/LookupScreen.tsx

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,26 @@
11
import { useMemo, useState } from 'react';
22
import { Pressable, ScrollView, StyleSheet, Text, View } from 'react-native';
33

4-
import { Card, Icon, QuantityWithUnit, ResourceView, SearchField, StatusBadge, TabScaffold } from '@/shared/ui';
4+
import {
5+
Card,
6+
Icon,
7+
QuantityWithUnit,
8+
ResourceView,
9+
SearchField,
10+
StatusBadge,
11+
TabScaffold,
12+
} from '@/shared/ui';
513
import { useResource } from '@/core/api/useResource';
614
import { fs, radius, s } from '@/shared/theme/tokens';
715
import { useTheme, useThemedStyles, type Theme } from '@/shared/theme/theme';
816
import { useT } from '@/shared/i18n/i18n';
9-
import { getLookupIndex, KIND_FILTERS, searchLookup, type LookupKind, type LookupRow } from './lookup.model';
17+
import {
18+
getLookupIndex,
19+
KIND_FILTERS,
20+
searchLookup,
21+
type LookupKind,
22+
type LookupRow,
23+
} from './lookup.model';
1024

1125
/** Terminal — Look up: read-only inquiry over products, locations and batches. */
1226
export function LookupScreen() {
@@ -40,7 +54,9 @@ function LookupView({ index }: { index: LookupRow[] }) {
4054
accessibilityRole="button"
4155
accessibilityState={{ selected: on }}
4256
>
43-
<Text style={[styles.filterLabel, on && styles.filterLabelOn]}>{t(`lookup.filter.${f.key}`)}</Text>
57+
<Text style={[styles.filterLabel, on && styles.filterLabelOn]}>
58+
{t(`lookup.filter.${f.key}`)}
59+
</Text>
4460
</Pressable>
4561
);
4662
})}
@@ -95,11 +111,22 @@ const makeStyles = (t: Theme) =>
95111
filterLabelOn: { color: '#fff' },
96112

97113
scroll: { paddingBottom: s[5] },
98-
count: { fontSize: fs.xs, color: t.color.inkFaint, marginHorizontal: s[5], marginVertical: s[2] },
114+
count: {
115+
fontSize: fs.xs,
116+
color: t.color.inkFaint,
117+
marginHorizontal: s[5],
118+
marginVertical: s[2],
119+
},
99120
empty: { fontSize: fs.sm, color: t.color.inkFaint, margin: s[5] },
100121

101122
list: { marginHorizontal: s[5] },
102-
row: { flexDirection: 'row', alignItems: 'center', gap: s[4], paddingVertical: s[4], paddingHorizontal: s[5] },
123+
row: {
124+
flexDirection: 'row',
125+
alignItems: 'center',
126+
gap: s[4],
127+
paddingVertical: s[4],
128+
paddingHorizontal: s[5],
129+
},
103130
rowBorder: { borderBottomWidth: 1, borderBottomColor: t.color.lineSoft },
104131
rowIcon: { width: 32, alignItems: 'center' },
105132
rowText: { flex: 1 },

src/web/terminal/src/features/Lookup/lookup.model.ts

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -69,9 +69,18 @@ export const getLookupIndex = async (): Promise<LookupRow[]> => {
6969
}));
7070

7171
// One product row per SKU, summing available-to-promise across its locations.
72-
const bySku = new Map<string, { name: string; atp: number; unit: string; locations: number; status: string }>();
72+
const bySku = new Map<
73+
string,
74+
{ name: string; atp: number; unit: string; locations: number; status: string }
75+
>();
7376
for (const r of rows) {
74-
const agg = bySku.get(r.sku) ?? { name: r.product, atp: 0, unit: r.unit, locations: 0, status: r.status };
77+
const agg = bySku.get(r.sku) ?? {
78+
name: r.product,
79+
atp: 0,
80+
unit: r.unit,
81+
locations: 0,
82+
status: r.status,
83+
};
7584
agg.atp += r.atp;
7685
agg.locations += 1;
7786
if (r.status === 'blocked' || r.status === 'expired') agg.status = r.status;
@@ -99,7 +108,11 @@ export const getLookupIndex = async (): Promise<LookupRow[]> => {
99108
};
100109

101110
/** Filter the index by free-text query and an optional kind. */
102-
export function searchLookup(rows: LookupRow[], query: string, kind: LookupKind | 'all'): LookupRow[] {
111+
export function searchLookup(
112+
rows: LookupRow[],
113+
query: string,
114+
kind: LookupKind | 'all',
115+
): LookupRow[] {
103116
const q = query.trim().toLowerCase();
104117
return rows.filter((r) => {
105118
if (kind !== 'all' && r.kind !== kind) return false;

src/web/terminal/src/features/Movement/MovementScreen.tsx

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,16 @@ import { router } from 'expo-router';
22
import { useState } from 'react';
33
import { StyleSheet, Text, View } from 'react-native';
44

5-
import { BigActionButton, Card, CheckRow, Chip, ResourceView, ScanField, ScreenScaffold, Stepper } from '@/shared/ui';
5+
import {
6+
BigActionButton,
7+
Card,
8+
CheckRow,
9+
Chip,
10+
ResourceView,
11+
ScanField,
12+
ScreenScaffold,
13+
Stepper,
14+
} from '@/shared/ui';
615
import { useResource } from '@/core/api/useResource';
716
import { fs, s } from '@/shared/theme/tokens';
817
import { useTheme, useThemedStyles, type Theme } from '@/shared/theme/theme';
@@ -39,7 +48,12 @@ function MovementView({ move }: { move: MoveTask }) {
3948
subtitle={t('move.task', { n: move.task, total: move.ofTasks })}
4049
accent={theme.color.move}
4150
actions={
42-
<BigActionButton label={t('move.confirm')} accent={theme.color.move} disabled={pending} onPress={commit} />
51+
<BigActionButton
52+
label={t('move.confirm')}
53+
accent={theme.color.move}
54+
disabled={pending}
55+
onPress={commit}
56+
/>
4357
}
4458
>
4559
{/* From → To legs */}
@@ -94,9 +108,20 @@ const makeStyles = (t: Theme) =>
94108

95109
item: { marginHorizontal: s[5], padding: s[5] },
96110
sku: { fontSize: fs.xs, color: t.color.inkFaint },
97-
product: { fontSize: fs.md, fontWeight: '700', color: t.color.ink, marginTop: 2, marginBottom: s[3] },
111+
product: {
112+
fontSize: fs.md,
113+
fontWeight: '700',
114+
color: t.color.ink,
115+
marginTop: 2,
116+
marginBottom: s[3],
117+
},
98118
chips: { flexDirection: 'row', gap: s[2] },
99-
qtyrow: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', marginTop: s[4] },
119+
qtyrow: {
120+
flexDirection: 'row',
121+
alignItems: 'center',
122+
justifyContent: 'space-between',
123+
marginTop: s[4],
124+
},
100125
lbl: { fontSize: fs.sm, color: t.color.inkSoft },
101126

102127
checks: { margin: s[5], gap: s[2] },

0 commit comments

Comments
 (0)