The app can run as an IIS Application under an existing site (e.g. https://host/ops/) instead of its own site + port: - frontend: vite base via VITE_BASE_PATH; router history, axios baseURL, and root-absolute asset/route paths resolve through utils/basePath.js withBase() - backend: MOUNT_PATH (env or .env) wraps the app in a WSGI middleware that shifts the prefix into SCRIPT_NAME, so one knob serves API + SPA under the mount - docs: INSTALL-WINDOWS-IIS.md section 7b runbook + troubleshooting rows; DEPLOY-WINDOWS-IIS.md pointer; commented examples in deploy/windows/web.config and .env.example Root deployment unchanged (MOUNT_PATH unset, base '/'). Also folds two stray root-absolute callers into the shared plumbing (MachineForm relationship-types fetch, reports CSV window.open).
70 lines
2.4 KiB
JavaScript
70 lines
2.4 KiB
JavaScript
// Facility floor-map blueprint config, read from the settings table so each
|
|
// site instance (ADR-004) renders its own floor plan instead of a hardcoded
|
|
// one. Keys: map_blueprint_light, map_blueprint_dark, map_width, map_height.
|
|
// Missing keys fall back to the generic placeholder so a fresh or offline
|
|
// install still renders; each site uploads its own blueprint in Settings.
|
|
import { reactive } from 'vue'
|
|
import { settingsApi } from '../api'
|
|
import { withBase } from '../utils/basePath'
|
|
|
|
// Fallback defaults - match the seeded map_blueprint_* setting defaults.
|
|
const DEFAULTS = {
|
|
blueprintLight: '/static/images/floorplan-placeholder.svg',
|
|
blueprintDark: '/static/images/floorplan-placeholder.svg',
|
|
width: 3300,
|
|
height: 2550
|
|
}
|
|
|
|
// Shared reactive config. Import as `state` to read width/height/blueprint.
|
|
export const state = reactive({ ...DEFAULTS, loaded: false })
|
|
|
|
let inflight = null
|
|
|
|
function applySetting(key, value) {
|
|
if (value === null || value === undefined || value === '') return
|
|
if (key === 'map_blueprint_light') state.blueprintLight = value
|
|
else if (key === 'map_blueprint_dark') state.blueprintDark = value
|
|
else if (key === 'map_width') {
|
|
const n = parseInt(value, 10)
|
|
if (!isNaN(n) && n > 0) state.width = n
|
|
} else if (key === 'map_height') {
|
|
const n = parseInt(value, 10)
|
|
if (!isNaN(n) && n > 0) state.height = n
|
|
}
|
|
}
|
|
|
|
function fetchConfig() {
|
|
inflight = settingsApi.list({ category: 'map' })
|
|
.then(({ data }) => {
|
|
;(data.data || []).forEach(s => applySetting(s.key, s.value))
|
|
state.loaded = true
|
|
})
|
|
.catch(() => { state.loaded = true })
|
|
.finally(() => { inflight = null })
|
|
return inflight
|
|
}
|
|
|
|
// Fetch the map config once (shared across all map components). Returns a
|
|
// promise that resolves when state is populated, so a caller can await it
|
|
// before initializing a Leaflet map that needs the dimensions.
|
|
export function loadMapConfig() {
|
|
if (state.loaded) return Promise.resolve()
|
|
if (inflight) return inflight
|
|
return fetchConfig()
|
|
}
|
|
|
|
// Re-read config from the server after a map setting changes.
|
|
export function reloadMapConfig() {
|
|
return fetchConfig()
|
|
}
|
|
|
|
// Blueprint image URL for the given theme ('light' | 'dark').
|
|
export function blueprintUrlFor(theme) {
|
|
return withBase(theme === 'light' ? state.blueprintLight : state.blueprintDark)
|
|
}
|
|
|
|
export function useMapConfig() {
|
|
loadMapConfig()
|
|
return { state, blueprintUrlFor }
|
|
}
|