Application and warranty uploads, toner forecast, fiscal week
This commit is contained in:
28
CHANGELOG.md
28
CHANGELOG.md
@@ -12,6 +12,34 @@ ADR-007 and ADR-002.
|
||||
|
||||
### Added
|
||||
|
||||
- Applications accept an uploaded image and an uploaded installer, rather than
|
||||
only a path typed into a text box. The path field kept working only while
|
||||
someone maintained a share by hand, and a link to a file nobody could still
|
||||
find was indistinguishable from a link to one that worked. The installer
|
||||
download is gated on `applications.view` because it is licensed vendor
|
||||
software; the image is public, since tiles render before login.
|
||||
- Warranty records accept an uploaded proof document - the invoice or
|
||||
certificate that actually settles a claim. The documentation path stays as it
|
||||
was: it points at a vendor portal or a share, which an upload cannot replace.
|
||||
Downloads require `warranty.view`, since a proof carries pricing and a service
|
||||
tag, and it comes back under the vendor's own filename so it is recognisable
|
||||
in a support ticket.
|
||||
- Toner forecast report: days until each cartridge runs out, soonest first,
|
||||
from Zabbix level history. Ordering by days left rather than by level is the
|
||||
point - a cartridge at 60% draining fast needs ordering before one sitting at
|
||||
8% that has not moved in a month. It also counts how many cartridges each
|
||||
printer has been through in the window, which is the number a purchasing
|
||||
conversation asks for. Printers with no honest estimate are listed
|
||||
separately with the reason (too little history, gauge has not moved, replaced
|
||||
recently) instead of being sorted in as zero or as safe, and an unreachable
|
||||
Zabbix says so rather than rendering an empty table that reads as "nothing is
|
||||
due".
|
||||
- Fiscal week under the site name in the sidebar and on the shop-floor board,
|
||||
as the classic ASP site showed it. People quote a week number across both
|
||||
sites, so the arithmetic is a port of the old one (ISO 8601: week 1 contains
|
||||
4 January, the week's Thursday decides the year) rather than a fresh
|
||||
interpretation.
|
||||
|
||||
- Backup history distinguishes CAPTURED from LAST VERIFIED. Captured is when a
|
||||
configuration was written; verified is when a PC last posted it and the hash
|
||||
still matched. They differ by design, because an unchanged config writes no
|
||||
|
||||
@@ -4,6 +4,11 @@
|
||||
<div class="sidebar-header">
|
||||
<img :src="siteLogo" alt="Site logo" class="sidebar-logo" />
|
||||
<h1>{{ facilityName }}</h1>
|
||||
<!-- Under the site name, where the classic site put it. -->
|
||||
<div class="fiscal-week">
|
||||
<span class="fiscal-week-label">Fiscal Week</span>
|
||||
<span class="fiscal-week-number">{{ currentFiscalWeek }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sidebar-search">
|
||||
@@ -98,7 +103,7 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, onMounted } from 'vue'
|
||||
import { ref, computed, onMounted, onUnmounted } from 'vue'
|
||||
import { useRouter, useRoute } from 'vue-router'
|
||||
import ToastHost from '../components/ToastHost.vue'
|
||||
import {
|
||||
@@ -109,6 +114,7 @@ import {
|
||||
import { useAuthStore } from '../stores/auth'
|
||||
import { currentTheme, toggleTheme } from '../stores/theme'
|
||||
import { dashboardApi, notificationsApi } from '../api'
|
||||
import { fiscalWeek } from '@/utils/fiscalWeek'
|
||||
import { getFacilityName, getSiteLogo, getServicenowUrls } from '../utils/siteSettings'
|
||||
import { withBase } from '../utils/basePath'
|
||||
|
||||
@@ -137,6 +143,11 @@ const searchQuery = ref('')
|
||||
const navItems = ref([])
|
||||
const activeNotifications = ref([])
|
||||
const facilityName = ref('ShopDB')
|
||||
|
||||
// A tab left open overnight would otherwise show last week's number until
|
||||
// somebody reloaded. Recomputed on a timer rather than only at mount.
|
||||
const currentFiscalWeek = ref(fiscalWeek())
|
||||
let fiscalWeekTimer = null
|
||||
const siteLogo = ref(withBase('/ge-aerospace-logo.svg'))
|
||||
const servicenowConfig = ref({ enabled: true, searchUrl: '' })
|
||||
|
||||
@@ -222,6 +233,10 @@ function buildNavItems(items) {
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
// Half-hourly: cheap, and it crosses midnight into the new week without a
|
||||
// reload on a screen nobody touches.
|
||||
fiscalWeekTimer = setInterval(() => { currentFiscalWeek.value = fiscalWeek() }, 30 * 60 * 1000)
|
||||
|
||||
getFacilityName().then(name => { facilityName.value = name })
|
||||
getSiteLogo().then(logo => { siteLogo.value = logo })
|
||||
getServicenowUrls().then(config => { servicenowConfig.value = config })
|
||||
@@ -274,9 +289,32 @@ function onAvatarError(event) {
|
||||
event.target.src = fallbackAvatar
|
||||
event.target.classList.add('ge-avatar-fallback')
|
||||
}
|
||||
onUnmounted(() => {
|
||||
if (fiscalWeekTimer) clearInterval(fiscalWeekTimer)
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* Fiscal week, under the site name, where the classic site put it. The number
|
||||
carries the weight; the label is small because it never changes. */
|
||||
.fiscal-week {
|
||||
margin-top: 0.35rem;
|
||||
text-align: center;
|
||||
line-height: 1.15;
|
||||
}
|
||||
.fiscal-week-label {
|
||||
display: block;
|
||||
font-size: 0.62rem;
|
||||
letter-spacing: 0.08em;
|
||||
text-transform: uppercase;
|
||||
opacity: 0.65;
|
||||
}
|
||||
.fiscal-week-number {
|
||||
display: block;
|
||||
font-size: 1.2rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
/* Footer user block: identity row on top, compact icon actions below, all
|
||||
kept inside the fixed-width sidebar (no full-width buttons overflowing). */
|
||||
.user-menu { display: flex; flex-direction: column; gap: 0.6rem; }
|
||||
@@ -305,4 +343,4 @@ function onAvatarError(event) {
|
||||
text-decoration: none;
|
||||
}
|
||||
.icon-btn:hover { background: rgba(255,255,255,0.16); color: #fff; }
|
||||
</style>
|
||||
</style>
|
||||
@@ -9,6 +9,9 @@
|
||||
<div class="header-center">
|
||||
<div class="location-title">{{ facilityName }}</div>
|
||||
<h1>Shopfloor Dashboard</h1>
|
||||
<!-- Same number the sidebar and the classic site show, since the board
|
||||
is what people read it off from across the floor. -->
|
||||
<div class="fiscal-week">Fiscal Week {{ currentFiscalWeek }}</div>
|
||||
</div>
|
||||
|
||||
<div class="header-right">
|
||||
@@ -241,9 +244,12 @@ import { notificationsApi, businessUnitsApi, dashboardDefaultsApi, settingsApi }
|
||||
import { formatInZone, zonedInputFromUtc, DEFAULT_TZ } from '@/utils/datetime'
|
||||
import { getFacilityName, getSiteLogo, getServicenowUrls } from '@/utils/siteSettings'
|
||||
import { withBase } from '@/utils/basePath'
|
||||
import { fiscalWeek } from '@/utils/fiscalWeek'
|
||||
|
||||
const loading = ref(true)
|
||||
const facilityName = ref('ShopDB')
|
||||
// A wall display runs for weeks; the clock tick refreshes this too.
|
||||
const currentFiscalWeek = ref(fiscalWeek())
|
||||
const siteLogo = ref(withBase('/ge-aerospace-logo.svg'))
|
||||
// Employee photo placeholder: the shipped GE monogram (square avatar). Kept
|
||||
// separate from siteLogo so a blank/broken site_logo setting never leaves an
|
||||
@@ -716,6 +722,15 @@ function handlePhotoError(e) {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.fiscal-week {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
letter-spacing: 2px;
|
||||
color: #b8c4d8;
|
||||
margin-top: 4px;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.location-title {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
|
||||
Reference in New Issue
Block a user