Two open endpoints: an item lookup by scanned code and the take POST - the product's first unauthenticated write, held to the decision record's bar (decrement-only, badge-attributed server-side, bounded, physically rate-limited; justification in the plugin README). The /parts-kiosk route is a full-screen no-auth view beside /shopfloor: a hidden always-focused input consumes keyboard-wedge scans for whichever step is active, TouchKeypad (net-new 3x4 grid) takes the quantity, and a success screen resets after a few seconds. Manual type-in fallbacks cover damaged labels. Kiosk test proves open access, the over-take guard, the badge policy, and cache==ledger afterward.
39 lines
1.1 KiB
Vue
39 lines
1.1 KiB
Vue
<template>
|
|
<div class="touch-keypad">
|
|
<button v-for="digit in digits" :key="digit" type="button"
|
|
class="keypad-button" @click="$emit('digit', digit)">
|
|
{{ digit }}
|
|
</button>
|
|
<button type="button" class="keypad-button keypad-muted"
|
|
@click="$emit('clear')">C</button>
|
|
<button type="button" class="keypad-button" @click="$emit('digit', '0')">0</button>
|
|
<button type="button" class="keypad-button keypad-muted"
|
|
@click="$emit('backspace')"><</button>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
const digits = ['1', '2', '3', '4', '5', '6', '7', '8', '9']
|
|
defineEmits(['digit', 'clear', 'backspace'])
|
|
</script>
|
|
|
|
<style scoped>
|
|
.touch-keypad {
|
|
display: grid;
|
|
grid-template-columns: repeat(3, 1fr);
|
|
gap: 0.6rem;
|
|
max-width: 20rem;
|
|
}
|
|
.keypad-button {
|
|
font-size: 1.8rem;
|
|
padding: 1rem 0;
|
|
border-radius: 0.5rem;
|
|
border: 1px solid var(--border);
|
|
background: var(--bg-card);
|
|
color: var(--text);
|
|
cursor: pointer;
|
|
}
|
|
.keypad-button:active { background: var(--primary); color: #fff; }
|
|
.keypad-muted { color: var(--text-light); }
|
|
</style>
|