Add USB, Notifications, Network plugins and reusable EmployeeSearch component

New Plugins:
- USB plugin: Device checkout/checkin with employee lookup, checkout history
- Notifications plugin: Announcements with types, scheduling, shopfloor display
- Network plugin: Network device management with subnets and VLANs
- Equipment and Computers plugins: Asset type separation

Frontend:
- EmployeeSearch component: Reusable employee lookup with autocomplete
- USB views: List, detail, checkout/checkin modals
- Notifications views: List, form with recognition mode
- Network views: Device list, detail, form
- Calendar view with FullCalendar integration
- Shopfloor and TV dashboard views
- Reports index page
- Map editor for asset positioning
- Light/dark mode fixes for map tooltips

Backend:
- Employee search API with external lookup service
- Collector API for PowerShell data collection
- Reports API endpoints
- Slides API for TV dashboard
- Fixed AppVersion model (removed BaseModel inheritance)
- Added checkout_name column to usbcheckouts table

Styling:
- Unified detail page styles
- Improved pagination (page numbers instead of prev/next)
- Dark/light mode theme improvements

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
cproudlock
2026-01-21 16:37:49 -05:00
parent 02d83335ee
commit 9c220a4194
110 changed files with 17693 additions and 600 deletions

View File

@@ -41,13 +41,28 @@
</template>
<script setup>
import { ref, computed, nextTick, watch } from 'vue'
import { ref, computed, nextTick, watch, onMounted, onUnmounted } from 'vue'
const props = defineProps({
left: { type: Number, default: null },
top: { type: Number, default: null },
machineName: { type: String, default: '' },
theme: { type: String, default: 'dark' }
machineName: { type: String, default: '' }
})
// Auto-detect system theme with reactive updates
const systemTheme = ref(window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light')
const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)')
function handleThemeChange(e) {
systemTheme.value = e.matches ? 'dark' : 'light'
}
onMounted(() => {
mediaQuery.addEventListener('change', handleThemeChange)
})
onUnmounted(() => {
mediaQuery.removeEventListener('change', handleThemeChange)
})
const visible = ref(false)
@@ -67,7 +82,7 @@ const hasPosition = computed(() => {
})
const blueprintUrl = computed(() => {
return props.theme === 'light'
return systemTheme.value === 'light'
? '/static/images/sitemap2025-light.png'
: '/static/images/sitemap2025-dark.png'
})
@@ -202,9 +217,9 @@ watch(visible, (newVal) => {
}
.map-tooltip-content {
background: var(--bg-card);
background: var(--bg-card, #ffffff);
border-radius: 8px;
border: 1px solid var(--border);
border: 1px solid var(--border, #e0e0e0);
box-shadow: 0 4px 20px rgba(0,0,0,0.25);
overflow: hidden;
}
@@ -214,7 +229,7 @@ watch(visible, (newVal) => {
width: 500px;
height: 385px;
overflow: hidden;
background: var(--bg);
background: var(--bg, #f5f5f5);
}
.map-transform {
@@ -235,7 +250,7 @@ watch(visible, (newVal) => {
width: 16px;
height: 16px;
background: #ff0000;
border: 2px solid var(--border);
border: 2px solid #ffffff;
border-radius: 50%;
box-shadow: 0 0 0 3px rgba(255,0,0,0.3), 0 0 10px #ff0000;
pointer-events: none;
@@ -243,8 +258,8 @@ watch(visible, (newVal) => {
.map-tooltip-footer {
padding: 0.5rem 0.75rem;
background: var(--bg);
border-top: 1px solid var(--border);
background: var(--bg, #f5f5f5);
border-top: 1px solid var(--border, #e0e0e0);
display: flex;
justify-content: space-between;
align-items: center;
@@ -253,11 +268,11 @@ watch(visible, (newVal) => {
.coordinates {
font-family: 'SF Mono', 'Monaco', 'Consolas', monospace;
font-size: 0.875rem;
color: var(--text-light);
color: var(--text-light, #666666);
}
.zoom-hint {
font-size: 0.75rem;
color: var(--text-light);
color: var(--text-light, #666666);
}
</style>