@forwardimpact/pathway 0.22.0 → 0.23.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/fit-pathway.js +8 -4
- package/package.json +6 -2
- package/src/commands/agent.js +6 -3
- package/src/commands/behaviour.js +11 -1
- package/src/commands/build.js +11 -2
- package/src/commands/command-factory.js +4 -2
- package/src/commands/dev.js +9 -2
- package/src/commands/discipline.js +25 -10
- package/src/commands/driver.js +11 -1
- package/src/commands/job.js +127 -28
- package/src/commands/level.js +25 -3
- package/src/commands/skill.js +11 -1
- package/src/commands/stage.js +11 -1
- package/src/commands/tool.js +6 -3
- package/src/commands/track.js +20 -4
- package/src/components/card.js +8 -104
- package/src/components/comparison-radar.js +1 -1
- package/src/components/detail.js +16 -118
- package/src/components/error-page.js +8 -68
- package/src/components/grid.js +12 -106
- package/src/components/list.js +7 -116
- package/src/components/nav.js +7 -60
- package/src/css/bundles/app.css +25 -21
- package/src/css/bundles/handout.css +33 -33
- package/src/css/bundles/slides.css +25 -25
- package/src/formatters/discipline/markdown.js +16 -1
- package/src/formatters/interview/shared.js +3 -3
- package/src/formatters/job/description.js +2 -2
- package/src/formatters/progress/shared.js +3 -3
- package/src/formatters/skill/shared.js +1 -1
- package/src/formatters/track/markdown.js +14 -0
- package/src/formatters/track/shared.js +1 -1
- package/src/handout.html +32 -13
- package/src/index.html +32 -13
- package/src/lib/error-boundary.js +3 -66
- package/src/lib/errors.js +7 -45
- package/src/lib/job-cache.js +1 -1
- package/src/lib/markdown.js +2 -109
- package/src/lib/reactive.js +7 -73
- package/src/lib/render.js +49 -197
- package/src/lib/router-core.js +2 -156
- package/src/lib/router-pages.js +2 -11
- package/src/lib/router-slides.js +2 -197
- package/src/lib/state.js +14 -63
- package/src/lib/utils.js +3 -10
- package/src/lib/yaml-loader.js +13 -71
- package/src/pages/agent-builder.js +1 -1
- package/src/pages/assessment-results.js +1 -1
- package/src/pages/job-builder.js +1 -1
- package/src/pages/job.js +1 -1
- package/src/pages/skill.js +1 -1
- package/src/slide-main.js +1 -1
- package/src/slides/index.js +1 -1
- package/src/slides/job.js +1 -1
- package/src/slides/overview.js +1 -1
- package/src/slides.html +32 -13
- package/src/css/base.css +0 -56
- package/src/css/components/badges.css +0 -232
- package/src/css/components/buttons.css +0 -101
- package/src/css/components/forms.css +0 -191
- package/src/css/components/layout.css +0 -218
- package/src/css/components/nav.css +0 -206
- package/src/css/components/progress.css +0 -166
- package/src/css/components/states.css +0 -82
- package/src/css/components/surfaces.css +0 -347
- package/src/css/components/tables.css +0 -362
- package/src/css/components/top-bar.css +0 -180
- package/src/css/components/typography.css +0 -121
- package/src/css/components/utilities.css +0 -41
- package/src/css/pages/detail.css +0 -119
- package/src/css/reset.css +0 -50
- package/src/css/tokens.css +0 -162
- package/src/css/views/handout.css +0 -30
- package/src/css/views/print.css +0 -634
- package/src/css/views/slide-animations.css +0 -113
- package/src/css/views/slide-base.css +0 -331
- package/src/css/views/slide-sections.css +0 -597
- package/src/css/views/slide-tables.css +0 -275
package/src/lib/router-core.js
CHANGED
|
@@ -1,161 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Core Router Factory
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* Re-exports from @forwardimpact/libui/router-core.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* @typedef {Object} Route
|
|
11
|
-
* @property {string} pattern - Route pattern with :params
|
|
12
|
-
* @property {RegExp} regex - Compiled pattern
|
|
13
|
-
* @property {string[]} paramNames - Extracted param names
|
|
14
|
-
* @property {Function} handler - Route handler
|
|
15
|
-
*/
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* @typedef {Object} RouteMatch
|
|
19
|
-
* @property {Function} handler - Matched handler
|
|
20
|
-
* @property {Object} params - Extracted parameters
|
|
21
|
-
*/
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* @typedef {Object} Router
|
|
25
|
-
* @property {(pattern: string, handler: Function) => void} on - Register route
|
|
26
|
-
* @property {(path: string) => void} navigate - Navigate to path
|
|
27
|
-
* @property {() => string} currentPath - Get current hash path
|
|
28
|
-
* @property {() => void} handleRoute - Process current route
|
|
29
|
-
* @property {() => void} start - Begin listening for hash changes
|
|
30
|
-
* @property {() => void} stop - Stop listening for hash changes
|
|
31
|
-
* @property {() => string[]} patterns - Get registered patterns
|
|
32
|
-
*/
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Parse route pattern into regex and param names
|
|
36
|
-
* @param {string} pattern
|
|
37
|
-
* @returns {{ regex: RegExp, paramNames: string[] }}
|
|
38
|
-
*/
|
|
39
|
-
function parsePattern(pattern) {
|
|
40
|
-
const paramNames = [];
|
|
41
|
-
const regexStr = pattern
|
|
42
|
-
.replace(/:([^/]+)/g, (_, name) => {
|
|
43
|
-
paramNames.push(name);
|
|
44
|
-
return "([^/]+)";
|
|
45
|
-
})
|
|
46
|
-
.replace(/\//g, "\\/");
|
|
47
|
-
return {
|
|
48
|
-
regex: new RegExp(`^${regexStr}$`),
|
|
49
|
-
paramNames,
|
|
50
|
-
};
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* Create a router instance
|
|
55
|
-
* @param {{ onNotFound?: (path: string) => void, onError?: (error: Error) => void, renderError?: (title: string, message: string) => void }} options
|
|
56
|
-
* @returns {Router}
|
|
57
|
-
*/
|
|
58
|
-
export function createRouter(options = {}) {
|
|
59
|
-
const { onNotFound = () => {}, onError, renderError } = options;
|
|
60
|
-
/** @type {Route[]} */
|
|
61
|
-
const routes = [];
|
|
62
|
-
let hashChangeHandler = null;
|
|
63
|
-
|
|
64
|
-
/**
|
|
65
|
-
* Match a path to a route
|
|
66
|
-
* @param {string} path
|
|
67
|
-
* @returns {RouteMatch|null}
|
|
68
|
-
*/
|
|
69
|
-
function matchRoute(path) {
|
|
70
|
-
for (const route of routes) {
|
|
71
|
-
const match = path.match(route.regex);
|
|
72
|
-
if (match) {
|
|
73
|
-
const params = {};
|
|
74
|
-
route.paramNames.forEach((name, i) => {
|
|
75
|
-
params[name] = decodeURIComponent(match[i + 1]);
|
|
76
|
-
});
|
|
77
|
-
return { handler: route.handler, params };
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
return null;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
/**
|
|
84
|
-
* Get current path from hash (including query string)
|
|
85
|
-
* @returns {string}
|
|
86
|
-
*/
|
|
87
|
-
function currentPath() {
|
|
88
|
-
return window.location.hash.slice(1) || "/";
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
* Handle the current route
|
|
93
|
-
*/
|
|
94
|
-
function handleRoute() {
|
|
95
|
-
const fullPath = currentPath();
|
|
96
|
-
const path = fullPath.split("?")[0];
|
|
97
|
-
const matched = matchRoute(path);
|
|
98
|
-
if (matched) {
|
|
99
|
-
window.scrollTo(0, 0);
|
|
100
|
-
matched.handler(matched.params);
|
|
101
|
-
} else {
|
|
102
|
-
onNotFound(path);
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
return {
|
|
107
|
-
/**
|
|
108
|
-
* Register a route
|
|
109
|
-
* @param {string} pattern - Route pattern (e.g., '/skills/:id')
|
|
110
|
-
* @param {Function} handler - Handler function
|
|
111
|
-
*/
|
|
112
|
-
on(pattern, handler) {
|
|
113
|
-
const { regex, paramNames } = parsePattern(pattern);
|
|
114
|
-
const wrappedHandler = withErrorBoundary(handler, {
|
|
115
|
-
onError,
|
|
116
|
-
backPath: "/",
|
|
117
|
-
backText: "← Back to Home",
|
|
118
|
-
renderErrorFn: renderError,
|
|
119
|
-
});
|
|
120
|
-
routes.push({ pattern, regex, paramNames, handler: wrappedHandler });
|
|
121
|
-
},
|
|
122
|
-
|
|
123
|
-
/**
|
|
124
|
-
* Navigate to a path
|
|
125
|
-
* @param {string} path
|
|
126
|
-
*/
|
|
127
|
-
navigate(path) {
|
|
128
|
-
window.location.hash = path;
|
|
129
|
-
},
|
|
130
|
-
|
|
131
|
-
currentPath,
|
|
132
|
-
handleRoute,
|
|
133
|
-
|
|
134
|
-
/**
|
|
135
|
-
* Start listening for hash changes
|
|
136
|
-
*/
|
|
137
|
-
start() {
|
|
138
|
-
hashChangeHandler = () => handleRoute();
|
|
139
|
-
window.addEventListener("hashchange", hashChangeHandler);
|
|
140
|
-
handleRoute();
|
|
141
|
-
},
|
|
142
|
-
|
|
143
|
-
/**
|
|
144
|
-
* Stop listening for hash changes
|
|
145
|
-
*/
|
|
146
|
-
stop() {
|
|
147
|
-
if (hashChangeHandler) {
|
|
148
|
-
window.removeEventListener("hashchange", hashChangeHandler);
|
|
149
|
-
hashChangeHandler = null;
|
|
150
|
-
}
|
|
151
|
-
},
|
|
152
|
-
|
|
153
|
-
/**
|
|
154
|
-
* Get all registered route patterns
|
|
155
|
-
* @returns {string[]}
|
|
156
|
-
*/
|
|
157
|
-
patterns() {
|
|
158
|
-
return routes.map((r) => r.pattern);
|
|
159
|
-
},
|
|
160
|
-
};
|
|
161
|
-
}
|
|
7
|
+
export { createRouter } from "@forwardimpact/libui/router-core";
|
package/src/lib/router-pages.js
CHANGED
|
@@ -1,16 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Pages Router
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* Re-exports from @forwardimpact/libui/router-pages.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Create the pages router for the main app
|
|
11
|
-
* @param {{ onNotFound?: (path: string) => void }} options
|
|
12
|
-
* @returns {import('./router-core.js').Router}
|
|
13
|
-
*/
|
|
14
|
-
export function createPagesRouter(options = {}) {
|
|
15
|
-
return createRouter(options);
|
|
16
|
-
}
|
|
7
|
+
export { createPagesRouter } from "@forwardimpact/libui/router-pages";
|
package/src/lib/router-slides.js
CHANGED
|
@@ -1,202 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Slide Router
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* Re-exports from @forwardimpact/libui/router-slides.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* @typedef {Object} SlideRouter
|
|
11
|
-
* @property {(pattern: string, handler: Function) => void} on - Register route
|
|
12
|
-
* @property {(path: string) => void} navigate - Navigate to path
|
|
13
|
-
* @property {() => string} currentPath - Get current hash path
|
|
14
|
-
* @property {() => void} handleRoute - Process current route
|
|
15
|
-
* @property {() => void} start - Begin listening for hash changes
|
|
16
|
-
* @property {() => void} stop - Stop listening for hash changes
|
|
17
|
-
* @property {() => string[]} patterns - Get registered patterns
|
|
18
|
-
* @property {(paths: string[]) => void} setSlideOrder - Define navigation order
|
|
19
|
-
* @property {() => void} next - Navigate to next slide
|
|
20
|
-
* @property {() => void} prev - Navigate to previous slide
|
|
21
|
-
* @property {() => void} home - Navigate to index
|
|
22
|
-
* @property {() => number} currentIndex - Current position in order
|
|
23
|
-
* @property {() => number} totalSlides - Total slide count
|
|
24
|
-
* @property {() => void} startKeyboardNav - Enable keyboard shortcuts
|
|
25
|
-
* @property {() => void} stopKeyboardNav - Disable keyboard shortcuts
|
|
26
|
-
*/
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Create a slide router with navigation capabilities
|
|
30
|
-
* @param {{ onNotFound?: (path: string) => void, renderError?: (title: string, message: string) => void }} options
|
|
31
|
-
* @returns {SlideRouter}
|
|
32
|
-
*/
|
|
33
|
-
export function createSlideRouter(options = {}) {
|
|
34
|
-
const router = createRouter(options);
|
|
35
|
-
let slideOrder = [];
|
|
36
|
-
let chapterBoundaries = [];
|
|
37
|
-
let keyHandler = null;
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* Find current position in slide order
|
|
41
|
-
* @returns {number}
|
|
42
|
-
*/
|
|
43
|
-
function findCurrentIndex() {
|
|
44
|
-
const path = router.currentPath();
|
|
45
|
-
return slideOrder.indexOf(path);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Navigate to slide at given index
|
|
50
|
-
* @param {number} index
|
|
51
|
-
*/
|
|
52
|
-
function navigateToIndex(index) {
|
|
53
|
-
if (index >= 0 && index < slideOrder.length) {
|
|
54
|
-
router.navigate(slideOrder[index]);
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
const slideRouter = {
|
|
59
|
-
// Expose core router methods
|
|
60
|
-
on: router.on,
|
|
61
|
-
navigate: router.navigate,
|
|
62
|
-
currentPath: router.currentPath,
|
|
63
|
-
handleRoute: router.handleRoute,
|
|
64
|
-
start: router.start,
|
|
65
|
-
stop: router.stop,
|
|
66
|
-
patterns: router.patterns,
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
* Define the slide navigation order
|
|
70
|
-
* @param {string[]} paths
|
|
71
|
-
* @param {number[]} boundaries - Indices where chapters start
|
|
72
|
-
*/
|
|
73
|
-
setSlideOrder(paths, boundaries = []) {
|
|
74
|
-
slideOrder = paths;
|
|
75
|
-
chapterBoundaries = boundaries;
|
|
76
|
-
},
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* Navigate to next slide
|
|
80
|
-
*/
|
|
81
|
-
next() {
|
|
82
|
-
const idx = findCurrentIndex();
|
|
83
|
-
navigateToIndex(idx + 1);
|
|
84
|
-
},
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* Navigate to previous slide
|
|
88
|
-
*/
|
|
89
|
-
prev() {
|
|
90
|
-
const idx = findCurrentIndex();
|
|
91
|
-
navigateToIndex(idx - 1);
|
|
92
|
-
},
|
|
93
|
-
|
|
94
|
-
/**
|
|
95
|
-
* Navigate to previous chapter
|
|
96
|
-
*/
|
|
97
|
-
prevChapter() {
|
|
98
|
-
const idx = findCurrentIndex();
|
|
99
|
-
// Find the previous chapter boundary before current position
|
|
100
|
-
const prevBoundary = chapterBoundaries.filter((b) => b < idx).pop();
|
|
101
|
-
if (prevBoundary !== undefined) {
|
|
102
|
-
navigateToIndex(prevBoundary);
|
|
103
|
-
} else if (idx > 0) {
|
|
104
|
-
navigateToIndex(0);
|
|
105
|
-
}
|
|
106
|
-
},
|
|
107
|
-
|
|
108
|
-
/**
|
|
109
|
-
* Navigate to next chapter
|
|
110
|
-
*/
|
|
111
|
-
nextChapter() {
|
|
112
|
-
const idx = findCurrentIndex();
|
|
113
|
-
// Find the next chapter boundary after current position
|
|
114
|
-
const nextBoundary = chapterBoundaries.find((b) => b > idx);
|
|
115
|
-
if (nextBoundary !== undefined) {
|
|
116
|
-
navigateToIndex(nextBoundary);
|
|
117
|
-
}
|
|
118
|
-
},
|
|
119
|
-
|
|
120
|
-
/**
|
|
121
|
-
* Navigate to first slide (index)
|
|
122
|
-
*/
|
|
123
|
-
home() {
|
|
124
|
-
navigateToIndex(0);
|
|
125
|
-
},
|
|
126
|
-
|
|
127
|
-
/**
|
|
128
|
-
* Get current slide index
|
|
129
|
-
* @returns {number}
|
|
130
|
-
*/
|
|
131
|
-
currentIndex() {
|
|
132
|
-
return findCurrentIndex();
|
|
133
|
-
},
|
|
134
|
-
|
|
135
|
-
/**
|
|
136
|
-
* Get total number of slides
|
|
137
|
-
* @returns {number}
|
|
138
|
-
*/
|
|
139
|
-
totalSlides() {
|
|
140
|
-
return slideOrder.length;
|
|
141
|
-
},
|
|
142
|
-
|
|
143
|
-
/**
|
|
144
|
-
* Start keyboard navigation
|
|
145
|
-
*/
|
|
146
|
-
startKeyboardNav() {
|
|
147
|
-
keyHandler = (e) => {
|
|
148
|
-
// Ignore if typing in an input
|
|
149
|
-
if (e.target.tagName === "INPUT" || e.target.tagName === "TEXTAREA") {
|
|
150
|
-
return;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
switch (e.key) {
|
|
154
|
-
case "ArrowRight":
|
|
155
|
-
case " ":
|
|
156
|
-
case "PageDown":
|
|
157
|
-
e.preventDefault();
|
|
158
|
-
slideRouter.next();
|
|
159
|
-
break;
|
|
160
|
-
case "ArrowLeft":
|
|
161
|
-
case "PageUp":
|
|
162
|
-
e.preventDefault();
|
|
163
|
-
slideRouter.prev();
|
|
164
|
-
break;
|
|
165
|
-
case "ArrowDown":
|
|
166
|
-
e.preventDefault();
|
|
167
|
-
slideRouter.nextChapter();
|
|
168
|
-
break;
|
|
169
|
-
case "ArrowUp":
|
|
170
|
-
e.preventDefault();
|
|
171
|
-
slideRouter.prevChapter();
|
|
172
|
-
break;
|
|
173
|
-
case "Home":
|
|
174
|
-
e.preventDefault();
|
|
175
|
-
slideRouter.home();
|
|
176
|
-
break;
|
|
177
|
-
case "End":
|
|
178
|
-
e.preventDefault();
|
|
179
|
-
navigateToIndex(slideOrder.length - 1);
|
|
180
|
-
break;
|
|
181
|
-
case "Escape":
|
|
182
|
-
e.preventDefault();
|
|
183
|
-
slideRouter.home();
|
|
184
|
-
break;
|
|
185
|
-
}
|
|
186
|
-
};
|
|
187
|
-
document.addEventListener("keydown", keyHandler);
|
|
188
|
-
},
|
|
189
|
-
|
|
190
|
-
/**
|
|
191
|
-
* Stop keyboard navigation
|
|
192
|
-
*/
|
|
193
|
-
stopKeyboardNav() {
|
|
194
|
-
if (keyHandler) {
|
|
195
|
-
document.removeEventListener("keydown", keyHandler);
|
|
196
|
-
keyHandler = null;
|
|
197
|
-
}
|
|
198
|
-
},
|
|
199
|
-
};
|
|
200
|
-
|
|
201
|
-
return slideRouter;
|
|
202
|
-
}
|
|
7
|
+
export { createSlideRouter } from "@forwardimpact/libui/router-slides";
|
package/src/lib/state.js
CHANGED
|
@@ -1,15 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Application state management
|
|
3
|
+
*
|
|
4
|
+
* Uses generic store from @forwardimpact/libui/state
|
|
5
|
+
* with Pathway-specific state shape and accessors.
|
|
3
6
|
*/
|
|
4
7
|
|
|
5
|
-
|
|
6
|
-
* @typedef {Object} AppState
|
|
7
|
-
* @property {Object} data - Loaded data from YAML files
|
|
8
|
-
* @property {Object} ui - UI state
|
|
9
|
-
*/
|
|
8
|
+
import { createStore } from "@forwardimpact/libui/state";
|
|
10
9
|
|
|
11
|
-
|
|
12
|
-
const state = {
|
|
10
|
+
const store = createStore({
|
|
13
11
|
data: {
|
|
14
12
|
skills: [],
|
|
15
13
|
behaviours: [],
|
|
@@ -35,48 +33,18 @@ const state = {
|
|
|
35
33
|
drivers: { search: "" },
|
|
36
34
|
},
|
|
37
35
|
},
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
/** @type {Set<Function>} */
|
|
41
|
-
const listeners = new Set();
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Get the current state
|
|
45
|
-
* @returns {AppState}
|
|
46
|
-
*/
|
|
47
|
-
export function getState() {
|
|
48
|
-
return state;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Get a specific path from state
|
|
53
|
-
* @param {string} path - Dot-notation path (e.g., 'data.skills')
|
|
54
|
-
* @returns {*}
|
|
55
|
-
*/
|
|
56
|
-
export function getStatePath(path) {
|
|
57
|
-
return path.split(".").reduce((obj, key) => obj?.[key], state);
|
|
58
|
-
}
|
|
36
|
+
});
|
|
59
37
|
|
|
60
|
-
|
|
61
|
-
* Update state at a specific path
|
|
62
|
-
* @param {string} path - Dot-notation path
|
|
63
|
-
* @param {*} value - New value
|
|
64
|
-
*/
|
|
65
|
-
export function updateState(path, value) {
|
|
66
|
-
const keys = path.split(".");
|
|
67
|
-
const lastKey = keys.pop();
|
|
68
|
-
const target = keys.reduce((obj, key) => obj[key], state);
|
|
69
|
-
target[lastKey] = value;
|
|
70
|
-
notifyListeners();
|
|
71
|
-
}
|
|
38
|
+
export const { getState, getStatePath, updateState, subscribe } = store;
|
|
72
39
|
|
|
73
40
|
/**
|
|
74
41
|
* Merge data into state
|
|
75
42
|
* @param {Object} data - Data to merge
|
|
76
43
|
*/
|
|
77
44
|
export function setData(data) {
|
|
45
|
+
const state = getState();
|
|
78
46
|
Object.assign(state.data, data, { loaded: true, error: null });
|
|
79
|
-
|
|
47
|
+
updateState("data", state.data);
|
|
80
48
|
}
|
|
81
49
|
|
|
82
50
|
/**
|
|
@@ -84,25 +52,7 @@ export function setData(data) {
|
|
|
84
52
|
* @param {Error} error
|
|
85
53
|
*/
|
|
86
54
|
export function setError(error) {
|
|
87
|
-
|
|
88
|
-
notifyListeners();
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
* Subscribe to state changes
|
|
93
|
-
* @param {Function} listener
|
|
94
|
-
* @returns {Function} Unsubscribe function
|
|
95
|
-
*/
|
|
96
|
-
export function subscribe(listener) {
|
|
97
|
-
listeners.add(listener);
|
|
98
|
-
return () => listeners.delete(listener);
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
/**
|
|
102
|
-
* Notify all listeners of state change
|
|
103
|
-
*/
|
|
104
|
-
function notifyListeners() {
|
|
105
|
-
listeners.forEach((listener) => listener(state));
|
|
55
|
+
updateState("data.error", error.message);
|
|
106
56
|
}
|
|
107
57
|
|
|
108
58
|
/**
|
|
@@ -112,9 +62,10 @@ function notifyListeners() {
|
|
|
112
62
|
* @param {*} value - Filter value
|
|
113
63
|
*/
|
|
114
64
|
export function setFilter(entity, filterKey, value) {
|
|
65
|
+
const state = getState();
|
|
115
66
|
if (state.ui.filters[entity]) {
|
|
116
67
|
state.ui.filters[entity][filterKey] = value;
|
|
117
|
-
|
|
68
|
+
updateState("ui.filters", state.ui.filters);
|
|
118
69
|
}
|
|
119
70
|
}
|
|
120
71
|
|
|
@@ -124,7 +75,7 @@ export function setFilter(entity, filterKey, value) {
|
|
|
124
75
|
* @returns {Object}
|
|
125
76
|
*/
|
|
126
77
|
export function getFilters(entity) {
|
|
127
|
-
return
|
|
78
|
+
return getState().ui.filters[entity] || {};
|
|
128
79
|
}
|
|
129
80
|
|
|
130
81
|
/**
|
|
@@ -140,7 +91,7 @@ export function getFilters(entity) {
|
|
|
140
91
|
* @returns {Branding}
|
|
141
92
|
*/
|
|
142
93
|
export function getBranding() {
|
|
143
|
-
const { framework } =
|
|
94
|
+
const { framework } = getState().data;
|
|
144
95
|
return {
|
|
145
96
|
title: framework.title || "Engineering Pathway",
|
|
146
97
|
tag: framework.tag || "#BenchTools",
|
package/src/lib/utils.js
CHANGED
|
@@ -1,14 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* General utility functions
|
|
3
|
+
*
|
|
4
|
+
* Re-exports from @forwardimpact/libui/utils.
|
|
3
5
|
*/
|
|
4
6
|
|
|
5
|
-
|
|
6
|
-
* Get an array of items by their IDs
|
|
7
|
-
* @param {Array} items - Array of items with id property
|
|
8
|
-
* @param {string[]} ids - Array of IDs to find
|
|
9
|
-
* @returns {Array} - Found items, filtered to remove nulls
|
|
10
|
-
*/
|
|
11
|
-
export function getItemsByIds(items, ids) {
|
|
12
|
-
if (!ids) return [];
|
|
13
|
-
return ids.map((id) => items.find((item) => item.id === id)).filter(Boolean);
|
|
14
|
-
}
|
|
7
|
+
export { getItemsByIds } from "@forwardimpact/libui/utils";
|