add config-driven channel label and dev-workstation bootstrap

A bleeding-edge container running next to a stable one on the same dev box is easy to confuse, and a branch-only badge would force perpetual rebase/cherry-pick drift between main and dev-be. Instead the badge is config-driven: the settings endpoint injects a synthetic env_label from SHOPDB_ENV_LABEL, the sidebar renders a red DEV tag only when it is non-empty, and stable simply leaves it unset. Same code on both channels, no divergence.

dev-bootstrap.ps1 makes a fresh Windows box turnkey: it checks git/Docker (winget-installs with a prompt), verifies the engine is actually running, then builds and starts an isolated per-channel stack so stable and edge coexist on distinct ports, projects, and volumes. docker-compose.channel.yml is a thin overlay so the base compose file stays channel-agnostic.
This commit is contained in:
benjamin.baker
2026-09-02 05:27:01 -04:00
parent ab4b9d7ec9
commit 0524ce7751
5 changed files with 377 additions and 2 deletions

View File

@@ -37,6 +37,14 @@ export async function getFacilityName() {
return getSetting('facility_name', 'ShopDB')
}
// Environment channel label (e.g. 'DEV') injected by the backend from
// SHOPDB_ENV_LABEL. Empty on stable/main deployments, which renders no badge.
// Drives the red DEV tag in the sidebar so a bleeding-edge container is
// obviously distinct from a stable one running side by side.
export async function getEnvLabel() {
return getSetting('env_label', '')
}
// Main site logo (sidebar, login, dashboard header). Fallback = shipped GE mark.
export async function getSiteLogo() {
return withBase(await getSetting('site_logo', '/ge-aerospace-logo.svg'))

View File

@@ -4,6 +4,9 @@
<div class="sidebar-header">
<img :src="siteLogo" alt="Site logo" class="sidebar-logo" />
<h1>{{ facilityName }}</h1>
<!-- Only present when the container sets SHOPDB_ENV_LABEL (edge/dev
channel). Stable leaves it empty, so nothing renders. --><span
v-if="envLabel" class="env-badge">{{ envLabel }}</span>
<!-- Under the site name, where the classic site put it. -->
<div class="fiscal-week">
<span class="fiscal-week-label">Fiscal Week</span>
@@ -122,7 +125,7 @@ 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 { getFacilityName, getSiteLogo, getServicenowUrls, getEnvLabel } from '../utils/siteSettings'
import { withBase } from '../utils/basePath'
const router = useRouter()
@@ -155,6 +158,7 @@ const cansearch = ref(false)
const navItems = ref([])
const activeNotifications = ref([])
const facilityName = ref('ShopDB')
const envLabel = ref('')
// A tab left open overnight would otherwise show last week's number until
// somebody reloaded. Recomputed on a timer rather than only at mount.
@@ -286,6 +290,7 @@ onMounted(async () => {
fiscalWeekTimer = setInterval(() => { currentFiscalWeek.value = fiscalWeek() }, 30 * 60 * 1000)
getFacilityName().then(name => { facilityName.value = name })
getEnvLabel().then(label => { envLabel.value = label })
getSiteLogo().then(logo => { siteLogo.value = logo })
getServicenowUrls().then(config => { servicenowConfig.value = config })
@@ -352,6 +357,21 @@ onUnmounted(() => {
</script>
<style scoped>
/* Bleeding-edge channel badge. Loud red on purpose so a dev container is never
mistaken for stable when the two run side by side. Only rendered when the
backend reports a non-empty env_label (SHOPDB_ENV_LABEL). */
.env-badge {
display: inline-block;
margin-top: 0.35rem;
padding: 0.1rem 0.5rem;
border-radius: 3px;
background: #c62828;
color: #fff;
font-size: 0.62rem;
font-weight: 700;
letter-spacing: 0.12em;
text-transform: uppercase;
}
/* 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 {