Show the fiscal week beside the site name

The classic ASP site prints a week number under the site title, and people
quote it in conversation and on paperwork. Anyone with both sites open needs
the two to agree, so this is a port of the old arithmetic rather than a fresh
interpretation of what a fiscal week is: ISO 8601, week 1 contains 4 January,
and the week's Thursday decides which year it belongs to. That rule is what
makes late December and early January land in the right year, which is exactly
where a naive day-of-year count goes wrong, so it is what the tests cover.

Worth recording: a true GE fiscal calendar need not follow ISO weeks. Nobody
has asked for a different rule, and inventing one here would silently disagree
with the site people compare against.

Computed in local time on purpose. The number people quote is the one on the
wall where they stand, and a UTC week rolls over hours early in the evening at
a US site. The sidebar re-checks every half hour; the shop-floor board picks it
up with the clock it already ticks.
This commit is contained in:
cproudlock
2026-08-12 12:06:25 -04:00
parent d109314123
commit ea35a134fe
4 changed files with 152 additions and 2 deletions

View File

@@ -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>

View File

@@ -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;