Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 26 additions & 19 deletions js/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -199,31 +199,38 @@ document.addEventListener('keydown', e => {
}
});

// ── Bootstrap: fetch config.json then start ───────────────────────────────────
// ── Bootstrap: load config.json, then start ───────────────────────────────────
function bootstrapWithConfig(config) {
setOrgData(config.orgData);
setDefaultAffinities(config.defaultAffinities);
setOwnerColors(config.ownerColors);
// Stamp each task with its original config name as a stable identity key.
// User edits can rename tasks later, so this hidden key lets storage.js
// reconnect saved progress to the correct starter task on reload.
config.orgData.departments.forEach(dept => {
dept.tasks.forEach(task => { task._configName = task.name; });
});
initApp();
}

document.addEventListener('DOMContentLoaded', () => {
// GitHub Pages serves this repo directly from source. Keep index.html loading
// this file as type="module", keep imports browser-compatible, and keep
// config.json reachable at the repo root for this fetch.
//
// config.json is the starter operating system for the app: departments,
// tasks, default role affinities, and owner colors all come from there.
// The production build (npm run build) inlines config.json into the page as
// window.__PM_OPS_CONFIG__ so the downloaded dist/ folder works by double-
// clicking index.html — fetch() of a local file is blocked by every modern
// browser's file:// CORS policy, so a runtime fetch can never work there.
// The raw source (GitHub Pages, npm start, python -m http.server) has no
// such global and falls through to the original fetch, unchanged.
if (window.__PM_OPS_CONFIG__) {
bootstrapWithConfig(window.__PM_OPS_CONFIG__);
return;
}

fetch('config.json')
.then(r => {
if (!r.ok) throw new Error(`config.json fetch failed: ${r.status}`);
return r.json();
})
.then(config => {
setOrgData(config.orgData);
setDefaultAffinities(config.defaultAffinities);
setOwnerColors(config.ownerColors);
// Stamp each task with its original config name as a stable identity key.
// User edits can rename tasks later, so this hidden key lets storage.js
// reconnect saved progress to the correct starter task on reload.
config.orgData.departments.forEach(dept => {
dept.tasks.forEach(task => { task._configName = task.name; });
});
initApp();
})
.then(bootstrapWithConfig)
.catch(err => {
console.error('PM Ops Map: could not load config.json —', err);
document.getElementById('departments').innerHTML =
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"name": "pm-ops-map",
"version": "1.0.0",
"description": "A visual task ownership map for property management companies",
"license": "MIT",
"private": true,
"engines": {
"node": ">=20.9"
Expand Down
1 change: 1 addition & 0 deletions server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
"name": "pm-ops-map-sync-server",
"version": "1.0.0",
"description": "Optional self-hosted sync server for PM Ops Map — lets a team share one workspace across devices without a full backend.",
"license": "MIT",
"private": true,
"main": "index.js",
"engines": {
Expand Down
30 changes: 30 additions & 0 deletions webpack.config.prod.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,45 @@
const fs = require('fs');
const path = require('path');
const { merge } = require('webpack-merge');
const common = require('./webpack.common.js');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CopyPlugin = require('copy-webpack-plugin');

// Downloading the ZIP and double-clicking index.html opens it over file://,
// where every modern browser's CORS policy blocks fetch() of local files —
// runtime `fetch('config.json')` can never work there, regardless of script
// type. This plugin inlines config.json as a global for the built HTML only
// (the raw source at the repo root is untouched and keeps using fetch(), for
// GitHub Pages / npm start / any real HTTP server) and swaps the entry
// script from type="module" to a plain deferred script — the webpack bundle
// has already been compiled down to a classic IIFE with no import/export
// left in it, so the module wrapper serves no purpose in the built output.
class InlineConfigForFileProtocolPlugin {
apply(compiler) {
compiler.hooks.compilation.tap('InlineConfigForFileProtocolPlugin', (compilation) => {
HtmlWebpackPlugin.getHooks(compilation).beforeEmit.tapAsync(
'InlineConfigForFileProtocolPlugin',
(data, cb) => {
const configJson = fs.readFileSync(path.resolve(__dirname, 'config.json'), 'utf8');
data.html = data.html.replace(
'<script type="module" src="js/app.js"></script>',
`<script>window.__PM_OPS_CONFIG__=${JSON.stringify(JSON.parse(configJson))};</script>\n<script src="js/app.js" defer></script>`
);
cb(null, data);
}
);
});
}
}

module.exports = merge(common, {
mode: 'production',
plugins: [
new HtmlWebpackPlugin({
template: './index.html',
inject: false,
}),
new InlineConfigForFileProtocolPlugin(),
new CopyPlugin({
patterns: [
{ from: 'config.json', to: 'config.json' },
Expand Down
Loading