Add the printedparts plugin: 3D-printed parts storefront and kiosk

Catalog with photos and append-only print-file revisions, a stock
ledger attributing every take/restock/adjust to a badge-resolved
employee, a touch kiosk (scan bin, scan badge, keypad quantity) with a
deliberately open decrement-only take endpoint, 1x0.5in CODE128 bin
labels, low-stock email alerts to selected users, roles, or addresses,
and stock/consumption/by-person reports with a cache-vs-ledger
reconcile. Ships disabled; enable per site. Contract 0.13.0 adds the
mailer and the User/Role models to the plugin surface.
This commit is contained in:
2026-07-17 09:29:01 -04:00
parent 2fda94f67a
commit 11c8d79ba4
35 changed files with 3231 additions and 3 deletions

View File

@@ -27,6 +27,8 @@
<div class="nav-section">Displays</div>
<a :href="withBase('/shopfloor')" target="_blank" class="external-link">Shopfloor Dashboard</a>
<a :href="withBase('/tv')" target="_blank" class="external-link">TV Slideshow</a>
<a v-if="isPluginEnabled('printedparts')" :href="withBase('/parts-kiosk')"
target="_blank" class="external-link">Parts Kiosk</a>
<router-link v-if="authStore.isAdmin" to="/settings">Settings</router-link>
</nav>
@@ -101,13 +103,14 @@ import ToastHost from '../components/ToastHost.vue'
import {
Sun, Moon, LayoutDashboard, Calendar, Map, Cog, Monitor,
Printer, Globe, Usb, AppWindow, BookOpen, BarChart3, Bell, Image, ShieldCheck, Ruler,
KeyRound, LogOut
Box, KeyRound, LogOut
} from 'lucide-vue-next'
import { useAuthStore } from '../stores/auth'
import { currentTheme, toggleTheme } from '../stores/theme'
import { dashboardApi, notificationsApi } from '../api'
import { getFacilityName, getSiteLogo, getServicenowUrls } from '../utils/siteSettings'
import { withBase } from '../utils/basePath'
import { isPluginEnabled } from '../composables/enabledPlugins'
const router = useRouter()
const route = useRoute()
@@ -147,6 +150,7 @@ const iconMap = {
'image': Image,
'shield': ShieldCheck,
'ruler': Ruler,
'box': Box,
}
// Default navigation (used as fallback if API fails)