real init
This commit is contained in:
parent
97402e1efc
commit
50ed3f5606
@ -1,5 +1,5 @@
|
|||||||
<!doctype html>
|
<!doctype html>
|
||||||
<html lang="en">
|
<html lang="en" class="dark">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
|
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
|
||||||
|
@ -15,8 +15,11 @@
|
|||||||
let inputElement: HTMLInputElement | null = null;
|
let inputElement: HTMLInputElement | null = null;
|
||||||
let unfocusTimeout: NodeJS.Timeout;
|
let unfocusTimeout: NodeJS.Timeout;
|
||||||
|
|
||||||
|
let charArray: string[] = [];
|
||||||
|
let lastPressed: number;
|
||||||
|
|
||||||
async function onKeydown(event: KeyboardEvent) {
|
async function onKeydown(event: KeyboardEvent) {
|
||||||
// If we this is a manual input
|
// If this is a manual input
|
||||||
if (focused) {
|
if (focused) {
|
||||||
// Auto unfocus after 5 seconds of inactivity
|
// Auto unfocus after 5 seconds of inactivity
|
||||||
clearTimeout(unfocusTimeout);
|
clearTimeout(unfocusTimeout);
|
||||||
@ -27,24 +30,29 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if this is an indicator that scanning has been initiated
|
let scanning: boolean = false;
|
||||||
if (event.key == 'Unidentified') {
|
if (Date.now() - lastPressed < 150) {
|
||||||
scanning = true;
|
scanning = true;
|
||||||
return;
|
} else {
|
||||||
|
charArray = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (event.key.length == 1) {
|
||||||
|
charArray.push(event.key);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send the key to onscan
|
// Send the key to onscan
|
||||||
if ((event.key == 'Tab' || event.key == 'Enter' || event.key == 'Backspace' || event.key == "Home" || event.key == "Delete")) {
|
if ((event.key == 'Tab' || event.key == 'Enter')) {
|
||||||
// Prevent tab or enter from doing anything
|
// Prevent tab or enter from doing anything
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
|
|
||||||
// Send key as text
|
// Send key as text
|
||||||
onscan(event.key);
|
onscan(charArray.join(''));
|
||||||
} else if (event.key.length == 1) {
|
charArray = [];
|
||||||
onscan(event.key);
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Haptics.impact({ style: ImpactStyle.Light });
|
lastPressed = Date.now();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function onKeyup(event: KeyboardEvent) {
|
async function onKeyup(event: KeyboardEvent) {
|
||||||
|
@ -20,11 +20,11 @@
|
|||||||
<style>
|
<style>
|
||||||
#header {
|
#header {
|
||||||
height: var(--safe-area-inset-top);
|
height: var(--safe-area-inset-top);
|
||||||
background-color: white;
|
background-color: black;
|
||||||
}
|
}
|
||||||
|
|
||||||
#footer {
|
#footer {
|
||||||
height: var(--safe-area-inset-bottom);
|
height: var(--safe-area-inset-bottom);
|
||||||
background-color: white;
|
background-color: black;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -1,316 +1,31 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import * as Dialog from '$lib/components/ui/dialog';
|
import Button from '$lib/components/ui/button/button.svelte';
|
||||||
import { Input } from '$lib/components/ui/input';
|
|
||||||
import { Label } from '$lib/components/ui/label';
|
|
||||||
import { Button, buttonVariants } from '$lib/components/ui/button';
|
|
||||||
import ScanCapture from '$lib/ScanCapture.svelte';
|
import ScanCapture from '$lib/ScanCapture.svelte';
|
||||||
import { cn } from '$lib/utils';
|
import { SvelteSet } from 'svelte/reactivity';
|
||||||
import { toast } from 'svelte-sonner';
|
import { fade } from 'svelte/transition';
|
||||||
import { Filesystem, Directory, Encoding } from '@capacitor/filesystem';
|
|
||||||
import { checkFileExists, createDirectory } from '$lib/cutils';
|
|
||||||
import { Haptics, ImpactStyle } from '@capacitor/haptics';
|
|
||||||
import { SvelteMap } from 'svelte/reactivity';
|
|
||||||
import { onMount } from 'svelte';
|
|
||||||
|
|
||||||
let showdialog = $state(false);
|
const list: SvelteSet<string> = new SvelteSet([]);
|
||||||
|
let count = $derived(list.size);
|
||||||
let focused = $state(false);
|
|
||||||
let confirmation = $state(false);
|
|
||||||
|
|
||||||
const formValues: SvelteMap<string, string | undefined> = new SvelteMap();
|
|
||||||
let sessioncount = $state(0);
|
|
||||||
let daycount = $state(0);
|
|
||||||
|
|
||||||
let selected = $state('service');
|
|
||||||
|
|
||||||
function onscan(value: string) {
|
function onscan(value: string) {
|
||||||
// Set as empty if undefined
|
if (!list.has(value)) {
|
||||||
if (formValues.get(selected) == undefined) {
|
list.add(value);
|
||||||
formValues.set(selected, '');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (value == 'Home') {
|
|
||||||
showdialog = !showdialog;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value == 'Delete') {
|
|
||||||
reset();
|
|
||||||
toast.success('Cleared');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Advance to next input field
|
|
||||||
if (value == 'Tab' || value == 'Enter') {
|
|
||||||
if (selected == 'service') {
|
|
||||||
selected = 'serial';
|
|
||||||
} else if (selected == 'serial') {
|
|
||||||
selected = 'description';
|
|
||||||
} else if (selected == 'description') {
|
|
||||||
selected = 'manufacturer';
|
|
||||||
} else if (selected == 'manufacturer') {
|
|
||||||
selected = 'model';
|
|
||||||
} else if (selected == 'model') {
|
|
||||||
selected = 'condition';
|
|
||||||
} else if (confirmation == false) {
|
|
||||||
confirmation = true;
|
|
||||||
} else if (confirmation == true) {
|
|
||||||
save();
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cancel confirmation
|
|
||||||
if (value == 'Backspace' && confirmation == true) {
|
|
||||||
confirmation = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Go back to the previous input field
|
|
||||||
if (value == 'Backspace' && formValues.get(selected)?.length == 0) {
|
|
||||||
if (selected == 'condition') {
|
|
||||||
selected = 'model';
|
|
||||||
} else if (selected == 'model') {
|
|
||||||
selected = 'manufacturer';
|
|
||||||
} else if (selected == 'manufacturer') {
|
|
||||||
selected = 'description';
|
|
||||||
} else if (selected == 'description') {
|
|
||||||
selected = 'serial';
|
|
||||||
} else if (selected == 'serial') {
|
|
||||||
selected = 'service';
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Subtract one from the current input
|
|
||||||
if (value == 'Backspace') {
|
|
||||||
formValues.set(selected, formValues.get(selected)?.slice(0, -1));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Append current input with new text
|
|
||||||
formValues.set(selected, formValues.get(selected) + value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async function onsubmit(event: SubmitEvent & { currentTarget: EventTarget & HTMLFormElement }) {
|
|
||||||
event.preventDefault();
|
|
||||||
Haptics.impact({ style: ImpactStyle.Medium });
|
|
||||||
confirmation = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function save() {
|
|
||||||
Haptics.impact({ style: ImpactStyle.Medium });
|
|
||||||
|
|
||||||
// Create directory
|
|
||||||
await createDirectory({
|
|
||||||
path: 'inventory',
|
|
||||||
directory: Directory.Documents
|
|
||||||
});
|
|
||||||
|
|
||||||
const filename = `inventory/${new Date().toLocaleDateString().replaceAll('/', '-')}.csv`;
|
|
||||||
|
|
||||||
// Check if CSV file exists
|
|
||||||
const exists = await checkFileExists({
|
|
||||||
path: filename,
|
|
||||||
directory: Directory.Documents
|
|
||||||
});
|
|
||||||
|
|
||||||
// Create file if it does not exist
|
|
||||||
if (!exists) {
|
|
||||||
await Filesystem.writeFile({
|
|
||||||
path: filename,
|
|
||||||
data: '',
|
|
||||||
directory: Directory.Documents,
|
|
||||||
encoding: Encoding.UTF8
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Append data to file
|
|
||||||
await Filesystem.appendFile({
|
|
||||||
path: filename,
|
|
||||||
data: `${formValues.get('service')},${formValues.get('serial')},${formValues.get('manufacturer')},${formValues.get('model')},${formValues.get('description')},${formValues.get('condition')},${new Date().toLocaleDateString()}\n`,
|
|
||||||
directory: Directory.Documents,
|
|
||||||
encoding: Encoding.UTF8
|
|
||||||
});
|
|
||||||
|
|
||||||
sessioncount = sessioncount + 1;
|
|
||||||
daycount = daycount + 1;
|
|
||||||
toast.success('Added item to inventory');
|
|
||||||
|
|
||||||
// Reset values
|
|
||||||
reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
function reset() {
|
|
||||||
confirmation = false;
|
|
||||||
selected = 'service';
|
|
||||||
formValues.set('serial', '');
|
|
||||||
formValues.set('description', '');
|
|
||||||
formValues.set('service', '');
|
|
||||||
formValues.set('manufacturer', '');
|
|
||||||
formValues.set('model', '');
|
|
||||||
formValues.set('condition', '');
|
|
||||||
}
|
|
||||||
|
|
||||||
onMount(async () => {
|
|
||||||
const filename = `inventory/${new Date().toLocaleDateString().replaceAll('/', '-')}.csv`;
|
|
||||||
|
|
||||||
// Check if CSV file exists
|
|
||||||
const exists = await checkFileExists({
|
|
||||||
path: filename,
|
|
||||||
directory: Directory.Documents
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!exists) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read CSV file
|
|
||||||
const file = await Filesystem.readFile({
|
|
||||||
path: filename,
|
|
||||||
directory: Directory.Documents,
|
|
||||||
encoding: Encoding.UTF8
|
|
||||||
});
|
|
||||||
|
|
||||||
// Parse CSV file
|
|
||||||
if (typeof file.data !== 'string') {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const lines = file.data.split("\n");
|
|
||||||
daycount = lines.length - 1;
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<ScanCapture {onscan} bind:focused />
|
<ScanCapture {onscan} />
|
||||||
|
|
||||||
<Dialog.Root bind:open={showdialog}>
|
<div class="flex h-screen">
|
||||||
<Dialog.Content
|
<div class="m-auto flex flex-col gap-8 p-4">
|
||||||
onEscapeKeydown={(e) => {
|
{#key count}
|
||||||
e.preventDefault();
|
<h1
|
||||||
}}
|
in:fade
|
||||||
>
|
class="decoration-sky text-9xl font-bold text-center underline-offset-4"
|
||||||
<Dialog.Header>
|
|
||||||
<Dialog.Title>Stats</Dialog.Title>
|
|
||||||
<div class="grid grid-cols-2 gap-2 py-4">
|
|
||||||
<p>day count:</p>
|
|
||||||
<p>{daycount}</p>
|
|
||||||
<p>session count:</p>
|
|
||||||
<p>{sessioncount}</p>
|
|
||||||
</div>
|
|
||||||
<Button
|
|
||||||
type="button"
|
|
||||||
class={cn(buttonVariants({ variant: 'outline' }), 'text-black cursor-pointer')}
|
|
||||||
onclick={() => {
|
|
||||||
sessioncount = 0;
|
|
||||||
toast.success('Session count reset');
|
|
||||||
}}>Reset session count</Button
|
|
||||||
>
|
>
|
||||||
</Dialog.Header>
|
{count}
|
||||||
</Dialog.Content>
|
</h1>
|
||||||
</Dialog.Root>
|
{/key}
|
||||||
|
<Button class="w-28 cursor-pointer">Submit</Button>
|
||||||
<form class="flex flex-col gap-4 py-2 px-3 py-4" {onsubmit}>
|
|
||||||
<div class="flex gap-2 items-center">
|
|
||||||
<Label class="basis-1/4" for="service">Asset Tag</Label>
|
|
||||||
<Input
|
|
||||||
id="service"
|
|
||||||
name="service"
|
|
||||||
type="text"
|
|
||||||
disabled={confirmation}
|
|
||||||
class={cn(!focused && selected == 'service' && 'ring-offset-2 ring-2 ring-green-600')}
|
|
||||||
bind:value={() => formValues.get('service'), (v) => formValues.set('service', v)}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="flex gap-2 items-center">
|
</div>
|
||||||
<Label class="basis-1/4" for="serial">Serial #</Label>
|
|
||||||
<Input
|
|
||||||
id="serial"
|
|
||||||
name="serial"
|
|
||||||
type="text"
|
|
||||||
disabled={confirmation}
|
|
||||||
class={cn(!focused && selected == 'serial' && 'ring-offset-2 ring-2 ring-green-600')}
|
|
||||||
bind:value={() => formValues.get('serial'), (v) => formValues.set('serial', v)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="flex gap-2 items-center">
|
|
||||||
<Label class="basis-1/4" for="description">Machine Type</Label>
|
|
||||||
<Input
|
|
||||||
id="description"
|
|
||||||
name="description"
|
|
||||||
type="text"
|
|
||||||
disabled={confirmation}
|
|
||||||
class={cn(!focused && selected == 'description' && 'ring-offset-2 ring-2 ring-green-600')}
|
|
||||||
bind:value={() => formValues.get('description'), (v) => formValues.set('description', v)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="flex gap-2 items-center">
|
|
||||||
<Label class="basis-1/4" for="manufacturer">Maker</Label>
|
|
||||||
<Input
|
|
||||||
id="manufacturer"
|
|
||||||
name="manufacturer"
|
|
||||||
type="text"
|
|
||||||
disabled={confirmation}
|
|
||||||
class={cn(!focused && selected == 'manufacturer' && 'ring-offset-2 ring-2 ring-green-600')}
|
|
||||||
bind:value={() => formValues.get('manufacturer'), (v) => formValues.set('manufacturer', v)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="flex gap-2 items-center">
|
|
||||||
<Label class="basis-1/4" for="model">Model</Label>
|
|
||||||
<Input
|
|
||||||
id="model"
|
|
||||||
name="model"
|
|
||||||
type="text"
|
|
||||||
disabled={confirmation}
|
|
||||||
class={cn(!focused && selected == 'model' && 'ring-offset-2 ring-2 ring-green-600')}
|
|
||||||
bind:value={() => formValues.get('model'), (v) => formValues.set('model', v)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="flex gap-2 items-center">
|
|
||||||
<Label class="basis-1/4" for="condition">Condition</Label>
|
|
||||||
<Input
|
|
||||||
id="condition"
|
|
||||||
name="condition"
|
|
||||||
type="text"
|
|
||||||
disabled={confirmation}
|
|
||||||
class={cn(!focused && selected == 'condition' && 'ring-offset-2 ring-2 ring-green-600')}
|
|
||||||
bind:value={() => formValues.get('condition'), (v) => formValues.set('condition', v)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
{#if confirmation}
|
|
||||||
<div class="flex flex-col gap-2">
|
|
||||||
<Button
|
|
||||||
type="button"
|
|
||||||
class={cn(buttonVariants({ variant: 'outline' }), 'text-black cursor-pointer')}
|
|
||||||
onclick={() => {
|
|
||||||
reset();
|
|
||||||
toast.success('Canceled');
|
|
||||||
}}>Cancel</Button
|
|
||||||
>
|
|
||||||
<Button type="button" class="cursor-pointer" onclick={() => save()}>Confirm</Button>
|
|
||||||
</div>
|
|
||||||
{:else}
|
|
||||||
<div class="flex flex-col gap-2">
|
|
||||||
<div class="flex justify-center gap-1">
|
|
||||||
<Button
|
|
||||||
type="button"
|
|
||||||
class={cn(buttonVariants({ variant: 'outline' }), 'text-black cursor-pointer grow')}
|
|
||||||
onclick={() => {
|
|
||||||
showdialog = !showdialog;
|
|
||||||
}}>Stats</Button
|
|
||||||
>
|
|
||||||
<Button
|
|
||||||
type="button"
|
|
||||||
class={cn(buttonVariants({ variant: 'outline' }), 'text-black cursor-pointer grow')}
|
|
||||||
onclick={() => {
|
|
||||||
reset();
|
|
||||||
toast.success('Cleared');
|
|
||||||
}}>Reset</Button
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
<Button type="submit" class="cursor-pointer">Submit</Button>
|
|
||||||
</div>
|
|
||||||
{/if}
|
|
||||||
</form>
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user