update position page design

This commit is contained in:
2026-08-13 21:41:14 +05:30
parent 6340962907
commit 1c653264b5
124 changed files with 5746 additions and 596 deletions

View File

@@ -0,0 +1,56 @@
import { useMemo } from 'react';
import { usePreferences } from '@/lib/krowHooks';
import {
isSkillOnPage, pageDefinitionForSkillId, skillStatesForPage, workforceSkillsForPage,
} from './pageSkills';
/**
* A page's skill attachments, resolved once.
*
* Every consuming page needs the same two things before it can ask anything
* else: the account's custom definitions (a skill added in Settings is as real
* as one on disk) and the ids it has switched off. Reading those in one hook
* means no page can forget either, and none has to know where they are stored.
*
* The hook is deliberately thin — it resolves attachment and hands back the
* helpers already bound to this page. The rules themselves stay in
* `pageSkills.js`, which is what keeps them testable without React.
*
* Usage is always the same shape:
*
* const { skills, statesFor } = usePageSkills('profile');
* if (!skills.length) return null; // nothing attached: no section
*
* `attaches(skillId)` is the gate for a contextual addition to something that
* already exists — a training prompt beside a position requirement, say — where
* the page is not listing skills but is about to mention one.
*/
export function usePageSkills(pageId) {
const preferences = usePreferences();
/* `usePreferences` rebuilds its object on every read, so the arrays inside it
are new identities each render. Keying the memo on their content is what
stops every consumer from recomputing — and re-rendering — continuously. */
const customKey = JSON.stringify(preferences.customSkills || []);
const disabledKey = JSON.stringify(preferences.disabledSkills || []);
return useMemo(() => {
const customSources = JSON.parse(customKey);
const disabled = JSON.parse(disabledKey);
const options = { customSources, disabled };
const skills = workforceSkillsForPage(pageId, options);
return {
/** The definitions attached here. Empty means render nothing. */
skills,
/** True when this page may surface that capability at all. */
attaches: (skillId) => isSkillOnPage(skillId, pageId, options),
/** The definition behind a capability, but only if attached here. */
definitionFor: (skillId) => pageDefinitionForSkillId(skillId, pageId, options),
/** Each attached definition paired with one person's state in it. */
statesFor: (courses, profile) => skillStatesForPage(pageId, courses, profile, options),
/** Passed through to components that resolve attachment themselves. */
customSources,
};
}, [pageId, customKey, disabledKey]);
}