@codingfactory/inventory-locator-client 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +47 -0
- package/src/api/client.ts +31 -0
- package/src/components/counting/CountEntryForm.vue +833 -0
- package/src/components/counting/CountReport.vue +385 -0
- package/src/components/counting/CountSessionDetail.vue +650 -0
- package/src/components/counting/CountSessionList.vue +683 -0
- package/src/components/dashboard/InventoryDashboard.vue +670 -0
- package/src/components/dashboard/UnlocatedProducts.vue +468 -0
- package/src/components/labels/LabelBatchPrint.vue +528 -0
- package/src/components/labels/LabelPreview.vue +293 -0
- package/src/components/locations/InventoryLocatorShell.vue +408 -0
- package/src/components/locations/LocationBreadcrumb.vue +144 -0
- package/src/components/locations/LocationCodeBadge.vue +46 -0
- package/src/components/locations/LocationDetail.vue +884 -0
- package/src/components/locations/LocationForm.vue +360 -0
- package/src/components/locations/LocationSearchInput.vue +428 -0
- package/src/components/locations/LocationTree.vue +156 -0
- package/src/components/locations/LocationTreeNode.vue +280 -0
- package/src/components/locations/LocationTypeIcon.vue +58 -0
- package/src/components/products/LocationProductAdd.vue +637 -0
- package/src/components/products/LocationProductList.vue +547 -0
- package/src/components/products/ProductLocationList.vue +215 -0
- package/src/components/products/QuickMoveModal.vue +592 -0
- package/src/components/scanning/ScanHistory.vue +146 -0
- package/src/components/scanning/ScanResult.vue +350 -0
- package/src/components/scanning/ScannerOverlay.vue +696 -0
- package/src/components/shared/InvBadge.vue +71 -0
- package/src/components/shared/InvButton.vue +206 -0
- package/src/components/shared/InvCard.vue +254 -0
- package/src/components/shared/InvEmptyState.vue +132 -0
- package/src/components/shared/InvInput.vue +125 -0
- package/src/components/shared/InvModal.vue +296 -0
- package/src/components/shared/InvSelect.vue +155 -0
- package/src/components/shared/InvTable.vue +288 -0
- package/src/composables/useCountSessions.ts +184 -0
- package/src/composables/useLabelPrinting.ts +71 -0
- package/src/composables/useLocationBreadcrumbs.ts +19 -0
- package/src/composables/useLocationProducts.ts +125 -0
- package/src/composables/useLocationSearch.ts +46 -0
- package/src/composables/useLocations.ts +159 -0
- package/src/composables/useMovements.ts +71 -0
- package/src/composables/useScanner.ts +83 -0
- package/src/env.d.ts +7 -0
- package/src/index.ts +46 -0
- package/src/plugin.ts +14 -0
- package/src/stores/countStore.ts +95 -0
- package/src/stores/locationStore.ts +113 -0
- package/src/stores/scannerStore.ts +51 -0
- package/src/types/index.ts +216 -0
- package/src/utils/codeFormatter.ts +29 -0
- package/src/utils/locationIcons.ts +64 -0
- package/tsconfig.json +21 -0
- package/vite.config.ts +37 -0
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
export interface Location {
|
|
2
|
+
id: number
|
|
3
|
+
parent_id: number | null
|
|
4
|
+
type: LocationType
|
|
5
|
+
type_label: string
|
|
6
|
+
name: string
|
|
7
|
+
code: string
|
|
8
|
+
full_code: string
|
|
9
|
+
description: string | null
|
|
10
|
+
is_active: boolean
|
|
11
|
+
is_mobile: boolean
|
|
12
|
+
sort_order: number
|
|
13
|
+
barcode: string | null
|
|
14
|
+
metadata: Record<string, unknown> | null
|
|
15
|
+
children_count?: number
|
|
16
|
+
product_count?: number
|
|
17
|
+
children?: Location[]
|
|
18
|
+
products?: LocationProduct[]
|
|
19
|
+
path?: PathSegment[]
|
|
20
|
+
created_at: string
|
|
21
|
+
updated_at: string
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export type LocationType =
|
|
25
|
+
| 'site' | 'building' | 'container' | 'zone'
|
|
26
|
+
| 'aisle' | 'rack' | 'shelf' | 'bin' | 'pallet'
|
|
27
|
+
|
|
28
|
+
export interface PathSegment {
|
|
29
|
+
id: number
|
|
30
|
+
name: string
|
|
31
|
+
code: string
|
|
32
|
+
full_code: string
|
|
33
|
+
type: LocationType
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface LocationProduct {
|
|
37
|
+
id: number
|
|
38
|
+
location_id: number
|
|
39
|
+
product_id: number | string
|
|
40
|
+
product_type: string
|
|
41
|
+
product_name: string
|
|
42
|
+
product_sku: string | null
|
|
43
|
+
quantity: number
|
|
44
|
+
is_primary: boolean
|
|
45
|
+
notes: string | null
|
|
46
|
+
placed_at: string | null
|
|
47
|
+
placed_by: number | null
|
|
48
|
+
location?: Location
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export interface Movement {
|
|
52
|
+
id: number
|
|
53
|
+
from_location: { id: number; name: string; full_code: string } | null
|
|
54
|
+
to_location: { id: number; name: string; full_code: string } | null
|
|
55
|
+
product_id: number | string
|
|
56
|
+
product_type: string
|
|
57
|
+
product_name: string
|
|
58
|
+
quantity: number
|
|
59
|
+
reason: MovementReason
|
|
60
|
+
reason_label: string
|
|
61
|
+
notes: string | null
|
|
62
|
+
performed_by: number | null
|
|
63
|
+
performer_name: string | null
|
|
64
|
+
performed_at: string
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export type MovementReason =
|
|
68
|
+
| 'placed' | 'moved' | 'picked' | 'adjusted'
|
|
69
|
+
| 'counted' | 'received' | 'returned'
|
|
70
|
+
|
|
71
|
+
export interface CountSession {
|
|
72
|
+
id: number
|
|
73
|
+
name: string
|
|
74
|
+
scope_location_id: number | null
|
|
75
|
+
status: CountStatus
|
|
76
|
+
progress: CountProgress
|
|
77
|
+
started_at: string | null
|
|
78
|
+
completed_at: string | null
|
|
79
|
+
started_by: number | null
|
|
80
|
+
completed_by: number | null
|
|
81
|
+
notes: string | null
|
|
82
|
+
entries_count?: number
|
|
83
|
+
created_at: string
|
|
84
|
+
updated_at: string
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export type CountStatus = 'pending' | 'in_progress' | 'completed' | 'cancelled'
|
|
88
|
+
|
|
89
|
+
export interface CountProgress {
|
|
90
|
+
total: number
|
|
91
|
+
counted: number
|
|
92
|
+
verified: number
|
|
93
|
+
discrepancies: number
|
|
94
|
+
percent_complete: number
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export interface CountEntry {
|
|
98
|
+
id: number
|
|
99
|
+
count_session_id: number
|
|
100
|
+
location_id: number
|
|
101
|
+
product_id: number | string
|
|
102
|
+
product_type: string
|
|
103
|
+
product_name: string
|
|
104
|
+
expected_quantity: number | null
|
|
105
|
+
counted_quantity: number | null
|
|
106
|
+
discrepancy: number | null
|
|
107
|
+
status: CountEntryStatus
|
|
108
|
+
counted_by: number | null
|
|
109
|
+
counted_at: string | null
|
|
110
|
+
verified_by: number | null
|
|
111
|
+
verified_at: string | null
|
|
112
|
+
notes: string | null
|
|
113
|
+
location?: Location
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export type CountEntryStatus = 'pending' | 'counted' | 'verified' | 'adjusted'
|
|
117
|
+
|
|
118
|
+
export interface LabelData {
|
|
119
|
+
location: {
|
|
120
|
+
id: number
|
|
121
|
+
name: string
|
|
122
|
+
code: string
|
|
123
|
+
full_code: string
|
|
124
|
+
type: LocationType
|
|
125
|
+
type_label: string
|
|
126
|
+
}
|
|
127
|
+
path: string
|
|
128
|
+
path_segments: PathSegment[]
|
|
129
|
+
qr_code_svg: string
|
|
130
|
+
qr_code_url: string
|
|
131
|
+
barcode_svg: string
|
|
132
|
+
barcode_value: string
|
|
133
|
+
format: LabelFormat
|
|
134
|
+
size: { width: string; height: string }
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export type LabelFormat = 'standard' | 'small' | 'pallet'
|
|
138
|
+
|
|
139
|
+
export interface DashboardStats {
|
|
140
|
+
total_locations: number
|
|
141
|
+
locations_by_type: Record<string, number>
|
|
142
|
+
total_products_tracked: number
|
|
143
|
+
locations_with_products: number
|
|
144
|
+
empty_leaf_locations: number
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export interface CountReport {
|
|
148
|
+
session: { id: number; name: string; status: CountStatus }
|
|
149
|
+
summary: {
|
|
150
|
+
total_entries: number
|
|
151
|
+
counted: number
|
|
152
|
+
verified: number
|
|
153
|
+
pending: number
|
|
154
|
+
with_discrepancies: number
|
|
155
|
+
accuracy_percent: number
|
|
156
|
+
total_expected: number
|
|
157
|
+
total_counted: number
|
|
158
|
+
net_discrepancy: number
|
|
159
|
+
}
|
|
160
|
+
top_discrepancies: Array<{
|
|
161
|
+
location: string
|
|
162
|
+
product: string
|
|
163
|
+
expected: number
|
|
164
|
+
counted: number
|
|
165
|
+
discrepancy: number
|
|
166
|
+
}>
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
export interface CreateLocationData {
|
|
170
|
+
name: string
|
|
171
|
+
type: LocationType
|
|
172
|
+
parent_id?: number | null
|
|
173
|
+
code?: string | null
|
|
174
|
+
description?: string | null
|
|
175
|
+
metadata?: Record<string, unknown> | null
|
|
176
|
+
sort_order?: number
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export interface UpdateLocationData {
|
|
180
|
+
name?: string
|
|
181
|
+
code?: string
|
|
182
|
+
description?: string | null
|
|
183
|
+
is_active?: boolean
|
|
184
|
+
metadata?: Record<string, unknown> | null
|
|
185
|
+
sort_order?: number
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
export interface PlaceProductData {
|
|
189
|
+
product_id: number | string
|
|
190
|
+
product_type?: string
|
|
191
|
+
quantity?: number
|
|
192
|
+
is_primary?: boolean
|
|
193
|
+
notes?: string
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
export interface MoveProductData {
|
|
197
|
+
from_location_id: number
|
|
198
|
+
to_location_id: number
|
|
199
|
+
quantity?: number
|
|
200
|
+
notes?: string
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
export interface LocationTypeInfo {
|
|
204
|
+
value: LocationType
|
|
205
|
+
label: string
|
|
206
|
+
can_contain_products: boolean
|
|
207
|
+
is_mobile: boolean
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
export interface Column {
|
|
211
|
+
key: string
|
|
212
|
+
label: string
|
|
213
|
+
sortable?: boolean
|
|
214
|
+
width?: string
|
|
215
|
+
align?: 'left' | 'center' | 'right'
|
|
216
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export function splitCode(fullCode: string, separator = '-'): string[] {
|
|
2
|
+
return fullCode.split(separator)
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
export function truncateCode(fullCode: string, maxSegments = 2, separator = '-'): string {
|
|
6
|
+
const parts = fullCode.split(separator)
|
|
7
|
+
if (parts.length <= maxSegments) return fullCode
|
|
8
|
+
return '…' + parts.slice(-maxSegments).join(separator)
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function formatQuantity(qty: number): string {
|
|
12
|
+
if (Number.isInteger(qty)) return String(qty)
|
|
13
|
+
return qty.toFixed(2).replace(/\.?0+$/, '')
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function timeAgo(dateStr: string): string {
|
|
17
|
+
const date = new Date(dateStr)
|
|
18
|
+
const now = new Date()
|
|
19
|
+
const seconds = Math.floor((now.getTime() - date.getTime()) / 1000)
|
|
20
|
+
|
|
21
|
+
if (seconds < 60) return 'just now'
|
|
22
|
+
const minutes = Math.floor(seconds / 60)
|
|
23
|
+
if (minutes < 60) return `${minutes}m ago`
|
|
24
|
+
const hours = Math.floor(minutes / 60)
|
|
25
|
+
if (hours < 24) return `${hours}h ago`
|
|
26
|
+
const days = Math.floor(hours / 24)
|
|
27
|
+
if (days < 7) return `${days}d ago`
|
|
28
|
+
return date.toLocaleDateString()
|
|
29
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import type { LocationType } from '../types'
|
|
2
|
+
|
|
3
|
+
interface IconDef {
|
|
4
|
+
viewBox: string
|
|
5
|
+
path: string
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const icons: Record<LocationType, IconDef> = {
|
|
9
|
+
site: {
|
|
10
|
+
viewBox: '0 0 24 24',
|
|
11
|
+
path: 'M12 2C8.13 2 5 5.13 5 9c0 5.25 7 13 7 13s7-7.75 7-13c0-3.87-3.13-7-7-7zm0 9.5a2.5 2.5 0 010-5 2.5 2.5 0 010 5z',
|
|
12
|
+
},
|
|
13
|
+
building: {
|
|
14
|
+
viewBox: '0 0 24 24',
|
|
15
|
+
path: 'M4 2v20h16V2H4zm4 18H6v-2h2v2zm0-4H6v-2h2v2zm0-4H6V8h2v4zm0-6H6V4h2v2zm4 14h-2v-2h2v2zm0-4h-2v-2h2v2zm0-4h-2V8h2v4zm0-6h-2V4h2v2zm6 14h-4v-4h4v4zm0-6h-2v-2h2v2zm0-4h-2V8h2v4zm0-6h-2V4h2v2z',
|
|
16
|
+
},
|
|
17
|
+
container: {
|
|
18
|
+
viewBox: '0 0 24 24',
|
|
19
|
+
path: 'M2 4v16h20V4H2zm18 14H4V6h16v12zM6 8h2v8H6V8zm4 0h2v8h-2V8zm4 0h2v8h-2V8z',
|
|
20
|
+
},
|
|
21
|
+
zone: {
|
|
22
|
+
viewBox: '0 0 24 24',
|
|
23
|
+
path: 'M3 3v18h18V3H3zm16 16H5V5h14v14zM7 7h4v4H7V7zm6 0h4v4h-4V7zm-6 6h4v4H7v-4zm6 0h4v4h-4v-4z',
|
|
24
|
+
},
|
|
25
|
+
aisle: {
|
|
26
|
+
viewBox: '0 0 24 24',
|
|
27
|
+
path: 'M7 2v20h2V2H7zm8 0v20h2V2h-2zM11 6h2v2h-2V6zm0 4h2v2h-2v-2zm0 4h2v2h-2v-2zm0 4h2v2h-2v-2z',
|
|
28
|
+
},
|
|
29
|
+
rack: {
|
|
30
|
+
viewBox: '0 0 24 24',
|
|
31
|
+
path: 'M3 2v20h2v-7h14v7h2V2H3zm16 4H5V4h14v2zm0 5H5V9h14v2z',
|
|
32
|
+
},
|
|
33
|
+
shelf: {
|
|
34
|
+
viewBox: '0 0 24 24',
|
|
35
|
+
path: 'M3 10h18v2H3v-2zm0-4h18v2H3V6zm2 8h4v6H5v-6zm6 0h4v6h-4v-6zm6 0h4v6h-4v-6z',
|
|
36
|
+
},
|
|
37
|
+
bin: {
|
|
38
|
+
viewBox: '0 0 24 24',
|
|
39
|
+
path: 'M5 4v16h14V4H5zm12 14H7V8h10v10zm-8-8h2v2H9V10zm4 0h2v2h-2v-2z',
|
|
40
|
+
},
|
|
41
|
+
pallet: {
|
|
42
|
+
viewBox: '0 0 24 24',
|
|
43
|
+
path: 'M2 18h2v2H2v-2zm4 0h2v2H6v-2zm4 0h2v2h-2v-2zm4 0h2v2h-2v-2zm4 0h2v2h-2v-2zM2 14h20v2H2v-2zm1-4h4v3H3v-3zm7 0h4v3h-4v-3zm7 0h4v3h-4v-3z',
|
|
44
|
+
},
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function getLocationIcon(type: LocationType): IconDef {
|
|
48
|
+
return icons[type] || icons.building
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function getLocationTypeLabel(type: LocationType): string {
|
|
52
|
+
const labels: Record<LocationType, string> = {
|
|
53
|
+
site: 'Site',
|
|
54
|
+
building: 'Building',
|
|
55
|
+
container: 'Shipping Container',
|
|
56
|
+
zone: 'Zone',
|
|
57
|
+
aisle: 'Aisle',
|
|
58
|
+
rack: 'Rack',
|
|
59
|
+
shelf: 'Shelf',
|
|
60
|
+
bin: 'Bin',
|
|
61
|
+
pallet: 'Pallet',
|
|
62
|
+
}
|
|
63
|
+
return labels[type] || type
|
|
64
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"moduleResolution": "bundler",
|
|
6
|
+
"strict": true,
|
|
7
|
+
"jsx": "preserve",
|
|
8
|
+
"declaration": true,
|
|
9
|
+
"declarationMap": true,
|
|
10
|
+
"sourceMap": true,
|
|
11
|
+
"outDir": "dist",
|
|
12
|
+
"baseUrl": ".",
|
|
13
|
+
"paths": {
|
|
14
|
+
"@/*": ["src/*"]
|
|
15
|
+
},
|
|
16
|
+
"types": ["vite/client"],
|
|
17
|
+
"lib": ["ES2022", "DOM", "DOM.Iterable"]
|
|
18
|
+
},
|
|
19
|
+
"include": ["src/**/*.ts", "src/**/*.vue"],
|
|
20
|
+
"exclude": ["node_modules", "dist", "tests"]
|
|
21
|
+
}
|
package/vite.config.ts
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { defineConfig } from 'vite'
|
|
2
|
+
import vue from '@vitejs/plugin-vue'
|
|
3
|
+
import dts from 'vite-plugin-dts'
|
|
4
|
+
import { resolve } from 'path'
|
|
5
|
+
|
|
6
|
+
export default defineConfig({
|
|
7
|
+
plugins: [
|
|
8
|
+
vue(),
|
|
9
|
+
dts({ rollupTypes: true }),
|
|
10
|
+
],
|
|
11
|
+
build: {
|
|
12
|
+
lib: {
|
|
13
|
+
entry: resolve(__dirname, 'src/index.ts'),
|
|
14
|
+
name: 'InventoryLocatorClient',
|
|
15
|
+
formats: ['es', 'cjs'],
|
|
16
|
+
fileName: (format) => `index.${format === 'es' ? 'js' : 'cjs'}`,
|
|
17
|
+
},
|
|
18
|
+
rollupOptions: {
|
|
19
|
+
external: ['axios', 'pinia', 'vue', 'vue-router'],
|
|
20
|
+
output: {
|
|
21
|
+
globals: {
|
|
22
|
+
vue: 'Vue',
|
|
23
|
+
pinia: 'Pinia',
|
|
24
|
+
axios: 'axios',
|
|
25
|
+
'vue-router': 'VueRouter',
|
|
26
|
+
},
|
|
27
|
+
assetFileNames: 'inventory-locator-client.[ext]',
|
|
28
|
+
},
|
|
29
|
+
},
|
|
30
|
+
cssCodeSplit: false,
|
|
31
|
+
},
|
|
32
|
+
resolve: {
|
|
33
|
+
alias: {
|
|
34
|
+
'@': resolve(__dirname, 'src'),
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
})
|