update the workspace and skills flow
This commit is contained in:
@@ -438,6 +438,7 @@ export const DATA_SOURCES = [
|
||||
context: 'positionId',
|
||||
summary: 'Applications to this position, counted over time.',
|
||||
shapes: ['flow', 'stats', 'timeline', 'table', 'insight', 'card'],
|
||||
options: ['periods'],
|
||||
},
|
||||
{
|
||||
id: 'position.pipeline',
|
||||
@@ -452,6 +453,7 @@ export const DATA_SOURCES = [
|
||||
context: 'positionId',
|
||||
summary: 'Candidates matched to this position, best first.',
|
||||
shapes: ['list', 'table', 'stats', 'card'],
|
||||
options: ['limit'],
|
||||
},
|
||||
{
|
||||
/**
|
||||
@@ -469,6 +471,7 @@ export const DATA_SOURCES = [
|
||||
context: 'positionId',
|
||||
summary: 'The candidate pool scored against this position, best first.',
|
||||
shapes: ['list', 'table', 'stats', 'card', 'insight'],
|
||||
options: ['limit'],
|
||||
},
|
||||
{
|
||||
id: 'position.requirements',
|
||||
@@ -504,6 +507,7 @@ export const DATA_SOURCES = [
|
||||
context: null,
|
||||
summary: 'Applications across the workspace, counted over time.',
|
||||
shapes: ['flow', 'stats', 'timeline', 'table', 'card'],
|
||||
options: ['periods'],
|
||||
},
|
||||
{
|
||||
id: 'positions.demand',
|
||||
@@ -511,6 +515,7 @@ export const DATA_SOURCES = [
|
||||
context: null,
|
||||
summary: 'Open positions and what they still need.',
|
||||
shapes: ['list', 'table', 'stats', 'card'],
|
||||
options: ['limit'],
|
||||
},
|
||||
{
|
||||
id: 'workforce.training',
|
||||
@@ -518,6 +523,7 @@ export const DATA_SOURCES = [
|
||||
context: null,
|
||||
summary: 'Training paths and progress against them.',
|
||||
shapes: ['progress', 'list', 'stats', 'table', 'card'],
|
||||
options: ['limit'],
|
||||
},
|
||||
{
|
||||
/* The vetting weights a position is being specified with. `positionId`
|
||||
@@ -548,6 +554,7 @@ export const DATA_SOURCES = [
|
||||
context: null,
|
||||
summary: 'Who was hired, for which role, and when.',
|
||||
shapes: ['list', 'table', 'timeline', 'stats', 'card'],
|
||||
options: ['limit'],
|
||||
},
|
||||
{
|
||||
id: 'hires.performance',
|
||||
@@ -562,6 +569,7 @@ export const DATA_SOURCES = [
|
||||
context: null,
|
||||
summary: 'What has happened across the workspace, most recent first.',
|
||||
shapes: ['timeline', 'list', 'table', 'stats', 'card'],
|
||||
options: ['limit'],
|
||||
},
|
||||
];
|
||||
|
||||
@@ -569,6 +577,60 @@ export const SUPPORTED_DATA_SOURCES = DATA_SOURCES.map((s) => s.id);
|
||||
|
||||
export const dataSourceFor = (id) => DATA_SOURCES.find((s) => s.id === id) || null;
|
||||
|
||||
/* ── Source / shape compatibility ───────────────────────────────────────────
|
||||
One resolver, three consumers: `normalizeSection` refuses on it, the Board
|
||||
editor's source picker filters on it, and the Owliver editor's per-capability
|
||||
picker filters on it. They used to be three readings of `source.shapes` —
|
||||
inline in the normalizer, absent from one editor and approximated in the
|
||||
other — which is how a form could compose `list` against a source that has no
|
||||
list in it and only find out at save. */
|
||||
|
||||
/**
|
||||
* The section type a capability is drawn with, or null when it is prose.
|
||||
*
|
||||
* `summary` is the one capability with no component, so it is compatible with
|
||||
* every source: the figures are read back as sentences rather than drawn.
|
||||
*/
|
||||
export const shapeForCapability = (capability) => owliverCapabilityFor(capability)?.shape || null;
|
||||
|
||||
/** Can this source be drawn as this shape? A null shape is prose, and always can. */
|
||||
export function sourceSupportsShape(sourceId, shape) {
|
||||
const source = dataSourceFor(sourceId);
|
||||
if (!source) return false;
|
||||
if (!shape) return true;
|
||||
return Boolean(source.shapes?.includes(shape));
|
||||
}
|
||||
|
||||
/**
|
||||
* The sources that can fill this shape, optionally narrowed to what a placement
|
||||
* can supply context for.
|
||||
*
|
||||
* `context` is the list a placement provides — `contextSuppliedBy(pages,
|
||||
* placement)`. Passing it is how the Board editor offers only sources that can
|
||||
* actually resolve where the section is mounted; the Owliver editor passes
|
||||
* nothing, because a response with an unmet need asks which record is meant
|
||||
* rather than rendering dead.
|
||||
*/
|
||||
export function sourcesForShape(shape, { context = null } = {}) {
|
||||
return DATA_SOURCES.filter((source) => {
|
||||
if (!sourceSupportsShape(source.id, shape)) return false;
|
||||
if (!context) return true;
|
||||
return !source.context || context.includes(source.context);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether a source reads an option a form can offer — `periods` or `limit`.
|
||||
*
|
||||
* Declared per source rather than inferred from its shapes, because the two do
|
||||
* not line up: `positions.demand` is a list that honours `limit` and has no
|
||||
* periods at all, while `candidates.activity` is the reverse. The editors used
|
||||
* to guess from the shape list and offered period checkboxes that changed
|
||||
* nothing.
|
||||
*/
|
||||
export const sourceSupportsOption = (sourceId, option) =>
|
||||
Boolean(dataSourceFor(sourceId)?.options?.includes(option));
|
||||
|
||||
/**
|
||||
* Can this reading be written back?
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user