-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtest.html
More file actions
476 lines (430 loc) · 22 KB
/
test.html
File metadata and controls
476 lines (430 loc) · 22 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
<!DOCTYPE html>
<html lang="en" data-theme="dark">
<head>
<title>RFDSort – Rockford UPS Air Hub Sorting Guide</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no, interactive-widget=resizes-content">
<meta name="description" content="RFDSort is a quick visual lookup tool for the Rockford UPS Air Hub (6119N). Find the correct chute, belt, and destination by ZIP code or state to reduce missorts and speed up training.">
<meta name="author" content="Michael Schwartz">
<meta name="mobile-web-app-capable" content="yes">
<meta name="application-name" content="RFDSort – Rockford UPS Air Hub Sorting Guide">
<meta name="theme-color" content="#13171f">
<meta name="apple-mobile-web-app-title" content="RFDSort – Rockford UPS Air Hub Sorting Guide">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<meta name="msapplication-starturl" content="./index.html">
<meta name="msapplication-navbutton-color" content="#13171f">
<meta property="og:url" content="https://michaelsboost.com/RFDSort" />
<meta property="og:type" content="website" />
<meta property="og:title" content="RFDSort – Rockford UPS Air Hub Sorting Guide" />
<meta property="og:description" content="RFDSort is a quick visual lookup tool for the Rockford UPS Air Hub (6119N). Find the correct chute, belt, and destination by ZIP code or state to reduce missorts and speed up training." />
<link rel="shortcut icon" type="image/x-icon" href="imgs/logo.svg">
<link rel="icon" type="image/svg+xml" href="imgs/logo.svg" />
<link rel="apple-touch-icon" href="imgs/logo.svg">
<link href="https://cdnjs.cloudflare.com/ajax/libs/picocss/2.0.6/pico.min.css" rel="stylesheet">
<script src="https://cdn.jsdelivr.net/npm/alpinejs@3.14.1/dist/cdn.min.js" defer></script>
<link rel="stylesheet" href="src/bundle.css">
</head>
<body>
<div class="absolute inset-0" x-data="App()">
<div class="flex absolute inset-0">
<!-- Sidebar (Belts) -->
<aside class="p-2 w-36 text-white grid grid-cols-2 gap-2 overflow-y-auto">
<template x-for="belt in belts" :key="belt.chute">
<button @click="showBelt(belt.chute)" :title="belt.chute" :class="[
'flex items-center border-0 justify-center rounded outline-none cursor-pointer',
isHighlighted(belt.chute) ? 'ring-4 ring-white' : '',
belt.color
]">
<span class="text-[10px] font-bold" x-text="belt.short"></span>
</button>
</template>
</aside>
<!-- Main Panel -->
<main class="flex-1 p-4 overflow-y-auto">
<div class="flex flex-col justify-between h-full">
<!-- Top bar -->
<header class="flex items-center mb-3">
<div class="w-auto">
<a x-show="selectedState" class="my-3 text-lg font-thin no-underline cursor-pointer text-current" @click="clear" target="_blank" x-text="selectedState"></a>
<a x-show="selectedBelt" class="my-3 text-lg font-thin no-underline cursor-pointer text-current" @click="clear" target="_blank" x-text="selectedBelt"></a>
<a x-show="!selectedState && !selectedBelt" class="my-3 text-lg font-thin no-underline cursor-pointer text-current" @click="openMenu=true" target="_blank">
<img alt="logo" class="h-8 mx-3 rounded-full" :src="logo" loading="lazy">
<span class="text-lg font-thin" x-text="name"></span>
</a>
</div>
<div class="ml-auto flex items-center gap-2">
<button @click="openMenu=true" class="px-3 py-1 rounded bg-transparent border-0" style="color:unset;">
<svg class="p-1 w-8" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<g fill="none" stroke="currentColor" stroke-dasharray="16" stroke-dashoffset="16" stroke-linecap="round" stroke-linejoin="round" stroke-width="2">
<path d="M5 5h14">
<animate fill="freeze" attributeName="stroke-dashoffset" dur="0.2s" values="16;0"></animate>
</path>
<path d="M5 12h14">
<animate fill="freeze" attributeName="stroke-dashoffset" begin="0.2s" dur="0.2s" values="16;0"></animate>
</path>
<path d="M5 19h14">
<animate fill="freeze" attributeName="stroke-dashoffset" begin="0.4s" dur="0.2s" values="16;0"></animate>
</path>
</g>
</svg>
</button>
</div>
</header>
<!-- State picker -->
<article x-show="!selectedState && !selectedBelt">
<h2 class="text-base font-thin mb-2">Select a State</h2>
<div class="grid grid-cols-1 sm:grid-cols-4 gap-2 mb-4">
<template x-for="s in states" :key="s">
<button @click="showState(s)">
<span x-text="s"></span>
</button>
</template>
</div>
</article>
<!-- STATE → BELTS result -->
<div x-show="selectedState">
<div class="grid gap-4 grid-cols-2">
<template x-for="(match, i) in matches" :key="match._key">
<article class="mb-2 p-3 w-full" :class="{ 'col-span-2': matches.length % 2 === 1 && i === matches.length - 1 }">
<div class="mb-1">
<strong>Chute(s):</strong>
<template x-for="c in asArray(match.chute)" :key="match._key + '::' + c">
<kbd class="inline-block px-2 py-0.5 mr-1 mb-1 rounded border" x-text="c"></kbd>
</template>
</div>
<div><strong>Dest (Slick):</strong> <span x-text="match.dest || '—'"></span></div>
<div><strong>ZIPs:</strong> <span x-text="match.zips.join(', ')"></span></div>
<div><strong>Pull:</strong> <span x-text="match.pull || '—'"></span></div>
<div><strong>Wave:</strong> <span x-text="match.wave || '—'"></span></div>
</article>
</template>
</div>
<div class="mt-2">
<button @click="clear" class="block w-full">Back</button>
</div>
</div>
<!-- BELT → STATES result -->
<div x-show="selectedBelt">
<div class="grid gap-4 grid-cols-2">
<template x-for="(m, i) in beltMatches" :key="m._key ?? i">
<article class="mb-2 p-3 w-full" :class="{ 'col-span-2': beltMatches.length % 2 === 1 && i === beltMatches.length - 1 }">
<div><strong>State:</strong> <span x-text="m.state"></span></div>
<div><strong>Dest (Slick):</strong> <span x-text="m.dest || '—'"></span></div>
<div><strong>ZIPs:</strong> <span x-text="m.zips.join(', ')"></span></div>
<div><strong>Pull:</strong> <span x-text="m.pull || '—'"></span></div>
<div><strong>Wave:</strong> <span x-text="m.wave || '—'"></span></div>
</article>
</template>
</div>
<div class="mt-2">
<button @click="clear" class="block w-full">Back</button>
</div>
</div>
<!-- Footer disclaimer -->
<footer class="p-4 text-center text-sm text-gray-600">
⚠️ Not UPS official. Use only when stopped — never while driving or on equipment.
</footer>
</div>
</main>
</div>
<dialog x-bind:open="openMenu" x-transition.opacity="">
<article class="rounded-md">
<header class="flex justify-between items-center">
<h1 class="text-lg font-thin m-0 capitalize">
file menu
</h1>
<button class="text-xs w-auto px-3 py-2 m-0 capitalize rounded-md bg-transparent border-0" style="color: unset;" aria-label="Close" @click="openMenu=false">
<svg class="h-4 w-4" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18 18 6M6 6l12 12"></path>
</svg>
</button>
</header>
<main>
<ul class="py-4 px-0 font-thin">
<li class="list-none">
<div class="items-center text-center">
<div>
<a aria-label="project homepage" name="project homepage" target="_blank" :href="url">
<img alt="logo" class="my-4 w-24 m-auto rounded-full" :src="logo" loading="lazy">
</a>
<div class="text-2xl">
About <span x-text="name"></span>
</div>
<div class="my-2 text-xs" x-text="version"></div>
<a target="_blank" class="text-sm underline mb-2 text-blue-500" :href="license">
Open Source License
</a>
</div>
</div>
</li>
<li class="p-0 list-none">
<hr>
</li>
<li class="p-0 list-none">
<nav class="flex justify-between mt-5 items-center">
<label for="theme" class="mb-2 flex justify-between items-center cursor-pointer">
Dark:
</label>
<input id="theme" class="m-0" type="checkbox" role="switch" name="toggle dark mode" @change="toggleTheme()">
</nav>
</li>
<li class="p-0 list-none">
<hr>
</li>
<li class="p-0 list-none">
<nav class="flex justify-between mt-5 items-center">
<strong>Shift:</strong>
<!-- Shift selector -->
<div class="ml-auto flex items-center gap-2">
<select id="shift" class="px-3 py-1 m-0 text-sm" x-model="shift" @change="applyShift()">
<template x-for="s in shiftOrder" :key="s">
<option :value="s" :selected="shift === s" x-text="labelForShift(s)"></option>
</template>
</select>
</div>
</nav>
</li>
<li class="p-0 list-none">
<hr>
</li>
<li class="p-0 list-none">
<nav class="flex justify-between mt-5 items-center">
<strong class="mb-2">Share:</strong>
<div class="text-sm">
<div class="w-full bg-white p-2 rounded">
<img src="imgs/qrcode.png" alt="Share RFDSort">
</div>
</div>
</nav>
</li>
<li class="p-0 list-none">
<hr>
</li>
<li class="list-none">
<strong class="mb-2">Developer:</strong>
<blockquote class="pt-4 text-sm text-center">
📦 Made with ❤️, ☕ & 👨💻 by <a href="https://michaelsboost.com/" target="_blank">Michael Schwartz</a> 🚀
</blockquote>
</li>
</ul>
</main>
<footer>
<button class="text-xs w-auto px-3 py-2 m-0 capitalize rounded-md bg-transparent border border-gray-600" style="color: unset;" aria-label="Close" @click="openMenu=false">
close
</button>
</footer>
</article>
</dialog>
</div>
<script>
function App() {
return {
// --- UI state ---
openMenu: false,
name: 'RFDSort',
version: 'v0.0.1',
url: 'https://github.com/michaelsboost/RFDSort/',
license: 'https://github.com/michaelsboost/RFDSort/blob/main/LICENSE',
logo: 'https://michaelsboost.com/RFDSort/imgs/logo.svg',
// --- Shifts/Sorts ---
shiftOrder: ['sunrise','day','twilight','night'],
shift: 'night',
labelForShift(s){ return ({sunrise:'Sunrise', day:'Day', twilight:'Twilight', night:'Night'})[s] || s; },
// Sidebar belts (short labels + colors)
belts: [
{chute:'Blue Yellow', short:'B-Y', color:'bg-blue-600'},
{chute:'Blue Black', short:'B-Bk', color:'bg-blue-800'},
{chute:'Blue Blue', short:'B-B', color:'bg-blue-500'},
{chute:'White Brown', short:'W-Br', color:'bg-gray-500'},
{chute:'Blue Orange', short:'B-O', color:'bg-orange-500'},
{chute:'Blue Green', short:'B-G', color:'bg-green-600'},
{chute:'Blue Red', short:'B-R', color:'bg-red-600'},
{chute:'Blue White', short:'B-W', color:'bg-blue-400'},
{chute:'White Orange',short:'W-O', color:'bg-orange-300 text-black'},
{chute:'White Green', short:'W-G', color:'bg-green-400 text-black'},
{chute:'Brown Brown', short:'Br-Br',color:'bg-yellow-800'},
{chute:'Brown Blue', short:'Br-B', color:'bg-blue-900'},
{chute:'Brown Yellow',short:'Br-Y', color:'bg-yellow-500 text-black'},
{chute:'Brown Black', short:'Br-Bk',color:'bg-black'},
{chute:'Brown Orange',short:'Br-O', color:'bg-orange-700'},
{chute:'Brown Green', short:'Br-G', color:'bg-green-800'},
{chute:'Brown Red', short:'Br-R', color:'bg-red-700'},
{chute:'Brown White', short:'Br-W', color:'bg-gray-400 text-black'},
{chute:'Yellow', short:'Y', color:'bg-yellow-400 text-black'},
{chute:'Black', short:'Bk', color:'bg-black'},
{chute:'White Red', short:'W-R', color:'bg-red-500'},
{chute:'White White', short:'W-W', color:'bg-gray-600'},
{chute:'White Blue', short:'W-B', color:'bg-blue-400'},
{chute:'Orange', short:'O', color:'bg-orange-400 text-black'},
{chute:'Green Runout',short:'Run', color:'bg-green-500'},
{chute:'PD-1', short:'PD1', color:'bg-gray-300 text-black'},
{chute:'PD-2', short:'PD2', color:'bg-gray-400 text-black'},
{chute:'PD-3', short:'PD3', color:'bg-gray-500'},
],
// --- RULES BY SHIFT ---
rulesByShift: {
sunrise: [
// Add Sunrise rules here later (same format)
],
day: [
// Add Day rules here later
],
twilight: [
// Add Twilight rules here later
],
night: [
// Your current rules (unchanged)
{state:'TX', zips:['735','750-758','760-767'], chute:['Blue Yellow','Blue Black'], dest:'DFW', pull:'225/245', wave:'2nd'},
{state:'OK', zips:['735','750-758','760-767'], chute:['Blue Yellow','Blue Black'], dest:'DFW', pull:'225/245', wave:'2nd'},
{state:'CA', zips:['900-928'], chute:'Blue Blue', dest:'ONT', pull:'201/221', wave:'1st'},
{state:'CA', zips:['900-928'], chute:'White Brown', dest:'ONT', pull:'201/221', wave:'1st'},
{state:'MI', zips:['484-499'], chute:'Blue Orange', dest:'LAN', pull:'227/247', wave:'2nd'},
{state:'MI', zips:['480-483'], chute:'Blue Green', dest:'DTW', pull:'235/255', wave:'2nd'},
{state:'OR', zips:['970-979'], chute:'Blue Red', dest:'PDX', pull:'153/213', wave:'1st'},
{state:'WA', zips:['986-987'], chute:'Blue Red', dest:'PDX', pull:'153/213', wave:'1st'},
{state:'MN', zips:['550-566'], chute:'Blue White', dest:'MSP', pull:'237/257', wave:'2nd'},
{state:'WI', zips:['540', '546-548'], chute:'Blue White', dest:'MSP', pull:'237/257', wave:'2nd'},
{state:'MD', zips:['200-219'], chute:'White Orange', dest:'BWI', pull:'221/241', wave:'2nd'},
{state:'DC', zips:['200-219'], chute:'White Orange', dest:'BWI', pull:'221/241', wave:'2nd'},
{state:'VA', zips:['201'], chute:'White Green', dest:'IAD', pull:'218/238', wave:'2nd'},
{state:'MD', zips:['217', '220-228'], chute:'White Green', dest:'IAD', pull:'218/238', wave:'2nd'},
{state:'WV', zips:['254', '267-268'], chute:'White Green', dest:'IAD', pull:'218/238', wave:'2nd'},
{state:'IL', zips:['620-623'], chute:'Brown Brown', dest:'STL', pull:'250/310', wave:'2nd'},
{state:'MO', zips:['630-637', '650-655'], chute:'Brown Brown', dest:'STL', pull:'250/310', wave:'2nd'},
{state:'MO', zips:['640-646', '649', '653'], chute:'Brown Blue', dest:'MCI', pull:'258/318', wave:'2nd'},
{state:'KS', zips:['660-666', '668'], chute:'Brown Blue', dest:'MCI', pull:'258/318', wave:'2nd'},
{state:'CA', zips:['942','952-953','955-961'], chute:'Brown Yellow', dest:'MHR/MEM', pull:'147/207/248', wave:'1st'},
{state:'TN', zips:['375-384'], chute:'Brown Yellow', dest:'MHR/MEM', pull:'147/207/248', wave:'1st'},
{state:'CA', zips:['939-951','954'], chute:'Brown Black', dest:'OAK', pull:'142/202', wave:'1st'},
{state:'MA', zips:['010-013', '668'], chute:'Brown Orange', dest:'BDL', pull:'222/242', wave:'2nd'},
{state:'CT', zips:['060-069'], chute:'Brown Orange', dest:'BDL', pull:'222/242', wave:'2nd'},
{state:'NY', zips:['104', '1006-119'], chute:'Brown Green', dest:'JFK', pull:'223/243', wave:'2nd'},
{state:'TX', zips:['759','770-779'], chute:'Brown Red', dest:'IAH', pull:'231/251', wave:'2nd'},
{state:'NE', zips:['689-691','693'], chute:'Brown White', dest:'DEN', pull:'149/209', wave:'1st'},
{state:'CO', zips:['820-826'], chute:'Brown White', dest:'DEN', pull:'149/209', wave:'1st'},
{state:'WY', zips:['820-826'], chute:'Brown White', dest:'DEN', pull:'149/209', wave:'1st'},
{state:'NY', zips:['100-103'], chute:'Yellow', dest:'EWR', pull:'221/241', wave:'2nd'},
{state:'NJ', zips:['070-079','085-089'], chute:'Black', dest:'EWR', pull:'221/241', wave:'2nd'},
{state:'AZ', zips:['850-869'], chute:'White Red', dest:'PHX', pull:'200/220', wave:'1st'},
{state:'IA', zips:['500-516','520-526'], chute:'White White', dest:'CID/OMA', pull:'242/248/256', wave:'1st'},
{state:'NE', zips:['680-688'], chute:'White White', dest:'CID/OMA', pull:'242/248/256', wave:'1st'},
{state:'OH', zips:['434','438-449'], chute:'White Blue', dest:'CLE', pull:'224/244', wave:'2nd'},
{state:'WA', zips:['980-985'], chute:'Orange', dest:'BFI', pull:'151/211', wave:'1st'},
{state:'IA', zips:['527-528'], chute:'Green Runout', dest:'PD Wall'},
{state:'IL', zips:['601-603','605','610','612-617'], chute:'Green Runout', dest:'PD Wall'},
{state:'IL', zips:['601','604-606','608-609'], chute:'PD-1', dest:'PD Wall'},
{state:'WI', zips:['530-539','541-545','549'], chute:'PD-2', dest:'PD Wall'},
{state:'WI', zips:['546-547'], chute:'PD-3', dest:'PD Wall'},
{state:'IL', zips:['60546','606-607','610-611'], chute:'PD-3', dest:'PD Wall'},
]
},
// Derived (current view)
rules: [],
states: [],
selectedState: null,
selectedBelt: null,
matches: [],
beltMatches: [],
highlightedSet: new Set(),
byChute: new Map(),
// THEME
theme: localStorage.getItem('theme') || 'dark',
// --- Lifecycle ---
init() {
this.applyShift();
this.applyTheme();
// keep theme persisted whenever it changes (covers selects etc.)
this.$watch('theme', (t) => {
localStorage.setItem('theme', t);
this.applyTheme();
});
// OPTIONAL: live-update if user changes OS theme while on "auto"
const mq = window.matchMedia('(prefers-color-scheme: dark)');
mq.addEventListener?.('change', () => {
if (this.theme === 'auto') this.applyTheme();
});
},
applyTheme() {
const html = document.documentElement; // <html>
let color;
let value = this.theme;
if (value === 'auto') {
value = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
}
if (value === 'dark') {
color = '#13171f'; // navbar dark color
} else {
color = 'hsl(205deg 18.75% 87.45%)'; // navbar light color
}
html.setAttribute('data-theme', value);
document.querySelector('meta[name=theme-color]').setAttribute('content', color);
document.querySelector('meta[name=msapplication-navbutton-color]').setAttribute('content', color);
},
toggleTheme() {
// If on 'auto', flip to the resolved mode first, then toggle.
const resolved = (this.theme === 'auto')
? (window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light')
: this.theme;
this.theme = (resolved === 'dark') ? 'light' : 'dark';
// applyTheme is called via $watch, but calling here is fine too:
this.applyTheme();
},
// --- Helpers ---
asArray(v){ return Array.isArray(v) ? v : [v]; },
isHighlighted(chute){ return this.highlightedSet.has(chute); },
// --- Shift handling ---
setShift(s){
if (this.shift === s) return;
this.shift = s;
this.applyShift();
},
applyShift(){
// reset selections/highlights
this.selectedState = null;
this.selectedBelt = null;
this.matches = [];
this.beltMatches = [];
this.highlightedSet = new Set();
// load rules for current shift
this.rules = this.rulesByShift[this.shift] || [];
// unique states
this.states = [...new Set(this.rules.map(r => r.state))].sort();
// reverse index: chute -> list of rules
this.byChute = new Map();
this.rules.forEach((r, i) => {
this.asArray(r.chute).forEach(c => {
if(!this.byChute.has(c)) this.byChute.set(c, []);
this.byChute.get(c).push({...r, _key:`belt:${this.shift}:${c}:${i}:${r.state}:${(r.dest||'')}`});
});
});
},
// --- Actions ---
showState(code){
this.selectedBelt = null;
this.selectedState = code;
this.matches = this.rules
.filter(r => r.state === code)
.map((r,i) => ({...r, _key: `state:${this.shift}:${code}:${i}:${(r.dest||'')}:${this.asArray(r.chute).join('|')}`}));
this.highlightedSet = new Set();
this.matches.forEach(m => this.asArray(m.chute).forEach(c => this.highlightedSet.add(c)));
},
showBelt(chuteName){
this.selectedState = null;
this.selectedBelt = chuteName;
this.beltMatches = this.byChute.get(chuteName) || [];
this.highlightedSet = new Set([chuteName]);
},
clear(){
this.selectedState = null;
this.selectedBelt = null;
this.matches = [];
this.beltMatches = [];
this.highlightedSet = new Set();
}
}
}
</script>
<script src="https://michaelsboost.com/TailwindCSSMod/tailwind-mod-noreset.min.js"></script>
</body>
</html>