@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.
Files changed (53) hide show
  1. package/package.json +47 -0
  2. package/src/api/client.ts +31 -0
  3. package/src/components/counting/CountEntryForm.vue +833 -0
  4. package/src/components/counting/CountReport.vue +385 -0
  5. package/src/components/counting/CountSessionDetail.vue +650 -0
  6. package/src/components/counting/CountSessionList.vue +683 -0
  7. package/src/components/dashboard/InventoryDashboard.vue +670 -0
  8. package/src/components/dashboard/UnlocatedProducts.vue +468 -0
  9. package/src/components/labels/LabelBatchPrint.vue +528 -0
  10. package/src/components/labels/LabelPreview.vue +293 -0
  11. package/src/components/locations/InventoryLocatorShell.vue +408 -0
  12. package/src/components/locations/LocationBreadcrumb.vue +144 -0
  13. package/src/components/locations/LocationCodeBadge.vue +46 -0
  14. package/src/components/locations/LocationDetail.vue +884 -0
  15. package/src/components/locations/LocationForm.vue +360 -0
  16. package/src/components/locations/LocationSearchInput.vue +428 -0
  17. package/src/components/locations/LocationTree.vue +156 -0
  18. package/src/components/locations/LocationTreeNode.vue +280 -0
  19. package/src/components/locations/LocationTypeIcon.vue +58 -0
  20. package/src/components/products/LocationProductAdd.vue +637 -0
  21. package/src/components/products/LocationProductList.vue +547 -0
  22. package/src/components/products/ProductLocationList.vue +215 -0
  23. package/src/components/products/QuickMoveModal.vue +592 -0
  24. package/src/components/scanning/ScanHistory.vue +146 -0
  25. package/src/components/scanning/ScanResult.vue +350 -0
  26. package/src/components/scanning/ScannerOverlay.vue +696 -0
  27. package/src/components/shared/InvBadge.vue +71 -0
  28. package/src/components/shared/InvButton.vue +206 -0
  29. package/src/components/shared/InvCard.vue +254 -0
  30. package/src/components/shared/InvEmptyState.vue +132 -0
  31. package/src/components/shared/InvInput.vue +125 -0
  32. package/src/components/shared/InvModal.vue +296 -0
  33. package/src/components/shared/InvSelect.vue +155 -0
  34. package/src/components/shared/InvTable.vue +288 -0
  35. package/src/composables/useCountSessions.ts +184 -0
  36. package/src/composables/useLabelPrinting.ts +71 -0
  37. package/src/composables/useLocationBreadcrumbs.ts +19 -0
  38. package/src/composables/useLocationProducts.ts +125 -0
  39. package/src/composables/useLocationSearch.ts +46 -0
  40. package/src/composables/useLocations.ts +159 -0
  41. package/src/composables/useMovements.ts +71 -0
  42. package/src/composables/useScanner.ts +83 -0
  43. package/src/env.d.ts +7 -0
  44. package/src/index.ts +46 -0
  45. package/src/plugin.ts +14 -0
  46. package/src/stores/countStore.ts +95 -0
  47. package/src/stores/locationStore.ts +113 -0
  48. package/src/stores/scannerStore.ts +51 -0
  49. package/src/types/index.ts +216 -0
  50. package/src/utils/codeFormatter.ts +29 -0
  51. package/src/utils/locationIcons.ts +64 -0
  52. package/tsconfig.json +21 -0
  53. 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
+ })