diff --git a/app/graphing/components/node-detail-wrapper.tsx b/app/graphing/components/node-detail-wrapper.tsx index 328c8c2..1276099 100644 --- a/app/graphing/components/node-detail-wrapper.tsx +++ b/app/graphing/components/node-detail-wrapper.tsx @@ -1,5 +1,5 @@ 'use client'; -import React, { Fragment, PropsWithChildren } from 'react'; +import React, { FC, Fragment, PropsWithChildren } from 'react'; import { Disclosure } from '@headlessui/react'; import { ChevronDownIcon } from '@heroicons/react/20/solid'; import { DataNode } from '../../api/zod-mods'; @@ -12,12 +12,18 @@ import { StarIcon as StarIconOutline } from '@heroicons/react/24/outline'; import { StarIcon } from '@heroicons/react/24/solid'; import { HasNumberIdDto } from '../../api/dtos/HasNumberIdDtoSchema'; import { Button } from '@nextui-org/button'; +import { NodeDetailsUiComponentProps } from '../graph-types/work-task-types/work-task-type-dto-details'; export function NodeDetailWrapper({ label, children, - node -}: { label: string; node: DataNode } & PropsWithChildren) { + node, + detailsUiComponent: Details +}: { + label: string; + node: DataNode; + detailsUiComponent?: FC>; +} & PropsWithChildren) { const { dispatch } = useNodeInteractionContext(); const isSelected = useNodeSelectedListener(node.id); @@ -50,7 +56,9 @@ export function NodeDetailWrapper({ - {children} + + {Details &&
} + )} diff --git a/app/graphing/components/node-details.tsx b/app/graphing/components/node-details.tsx index 7afec3e..82d13ca 100644 --- a/app/graphing/components/node-details.tsx +++ b/app/graphing/components/node-details.tsx @@ -1,14 +1,17 @@ -import React from 'react'; +import React, { FC } from 'react'; import { NodeDetailWrapper } from './node-detail-wrapper'; import { NodePayload } from '../force-graph-page'; import { HasNumberIdDto } from '../../api/dtos/HasNumberIdDtoSchema'; +import { NodeDetailsUiComponentProps } from '../graph-types/work-task-types/work-task-type-dto-details'; export default function NodeDetails({ nodeDetailElements, - labels + labels, + detailsUiComponent }: { nodeDetailElements: NodePayload[]; labels: string[]; + detailsUiComponent?: FC>; }) { return (
@@ -17,9 +20,8 @@ export default function NodeDetails({ key={`${index}-${labels[index]}`} label={`${labels[index]}`} node={detailElement.node} - > - {detailElement.payload} - + detailsUiComponent={detailsUiComponent} + /> ))}
); diff --git a/app/graphing/force-graph-page.tsx b/app/graphing/force-graph-page.tsx index e11c4cb..37aa6d0 100644 --- a/app/graphing/force-graph-page.tsx +++ b/app/graphing/force-graph-page.tsx @@ -12,7 +12,7 @@ import { ShowNodeEditing } from './show-node-editing'; export interface NodePayload { node: DataNode; - payload: React.JSX.Element; + payload?: React.JSX.Element; } export default function ForceGraphPage({ dataGraph: graphDto, diff --git a/app/graphing/graph-types/work-task-types/assign-item-from-object-entries.tsx b/app/graphing/graph-types/work-task-types/assign-item-from-object-entries.tsx new file mode 100644 index 0000000..1601ad6 --- /dev/null +++ b/app/graphing/graph-types/work-task-types/assign-item-from-object-entries.tsx @@ -0,0 +1,62 @@ +import { StringMap } from '../../../contexts/string-map-context/string-map-reducer'; +import { ColumnOne, ColumnsTwoToFour } from './rename-work-task-type'; +import { Listbox } from '@headlessui/react'; +import { + NodeDetailsListBoxButton, + NodeDetailsListBoxOption, + NodeDetailsListBoxOptions +} from '../organization/curriculum-delivery-details'; +import React, { Fragment } from 'react'; +import { isNotUndefined } from '../../../api/main'; + +export function AssignItemFromObjectEntries({ + itemDescriptor, + currentAssignment, + onChange, + optionsMap, + labelAccessor, + idAccessor +}: { + itemDescriptor: string; + currentAssignment: string; + onChange: (value: string) => void; + optionsMap: StringMap; + labelAccessor: (item: T) => string; + idAccessor: (item: T) => string; +}) { + console.log(optionsMap); + return ( + <> + {itemDescriptor} + + + + {isNotUndefined(optionsMap[currentAssignment]) + ? labelAccessor(optionsMap[currentAssignment]) + : 'Unassigned'} + + + {Object.values(optionsMap) + .filter(isNotUndefined) + .map((option) => ( + + {({ selected, active }) => ( + + {labelAccessor(option)} + + )} + + ))} + + + + + ); +} diff --git a/app/graphing/graph-types/work-task-types/lesson-type-hierarchy-graph.tsx b/app/graphing/graph-types/work-task-types/lesson-type-hierarchy-graph.tsx index 68d6ec5..adee0e7 100644 --- a/app/graphing/graph-types/work-task-types/lesson-type-hierarchy-graph.tsx +++ b/app/graphing/graph-types/work-task-types/lesson-type-hierarchy-graph.tsx @@ -12,9 +12,12 @@ import React from 'react'; import WorkTaskTypeDtoDetails from './work-task-type-dto-details'; import NodeDetails from '../../components/node-details'; import { putGraph } from '../../../api/READ-ONLY-generated-actions/WorkTaskType'; +import { useStringMapContextController } from './use-string-map-context-controller'; const graphUpdater = getGraphUpdaterWithNameDeDuplication(putGraph); +const ListenerKey = `lessonTypeHierarchyGraph`; + export function LessonTypeHierarchyGraph() { const { nodes, nodesRef, linksRef } = useNodeAndLinkRefs(); @@ -26,6 +29,16 @@ export function LessonTypeHierarchyGraph() { CloneFunctionWrapper, graphUpdater ); + const { currentState: stringMapKd } = useStringMapContextController( + 'knowledgeDomain', + ListenerKey + ); + const { currentState: stringMapKl } = useStringMapContextController( + 'knowledgeLevel', + ListenerKey + ); + + console.log(stringMapKd); nodes.forEach((n: DataNode) => { lessonTypeList.push(n.data.name); @@ -38,13 +51,7 @@ export function LessonTypeHierarchyGraph() { const nodeDetailElements: NodePayload[] = nodesRef.current.map((node) => { return { - node: node, - payload: ( - - ) + node: node }; }); @@ -59,6 +66,7 @@ export function LessonTypeHierarchyGraph() { ); diff --git a/app/graphing/graph-types/work-task-types/use-string-map-context-controller.ts b/app/graphing/graph-types/work-task-types/use-string-map-context-controller.ts new file mode 100644 index 0000000..38e9333 --- /dev/null +++ b/app/graphing/graph-types/work-task-types/use-string-map-context-controller.ts @@ -0,0 +1,51 @@ +import { + useSelectiveContextAnyController, + useSelectiveContextGlobalListener +} from '../../../selective-context/components/global/selective-context-manager-global'; +import { + getIdListContextKey, + getNameSpacedKey +} from '../../../selective-context/components/controllers/dto-id-list-controller'; +import { useSelectiveContextListenerReadAll } from '../../../selective-context/components/base/generic-selective-context-creator'; +import { SelectiveContextGlobal } from '../../../selective-context/components/global/selective-context-creator-global'; +import { useMemo } from 'react'; +import { getEntityNamespaceContextKey } from '../../../selective-context/hooks/dtoStores/use-dto-store'; +import { EmptyArray, isNotUndefined } from '../../../api/main'; +import { StringMap } from '../../../contexts/string-map-context/string-map-reducer'; +import { KnowledgeDomainDto } from '../../../api/dtos/KnowledgeDomainDtoSchema'; + +export function useStringMapContextController( + entityName: string, + listenerKey: string +) { + const { currentState: idList } = useSelectiveContextGlobalListener({ + contextKey: getIdListContextKey(entityName), + listenerKey: listenerKey, + initialValue: EmptyArray + }); + + const selectiveContextReadAll = useSelectiveContextListenerReadAll( + SelectiveContextGlobal + ); + + const stringMap = useMemo(() => { + return idList + .map((id) => + selectiveContextReadAll(getEntityNamespaceContextKey(entityName, id)) + ) + .filter(isNotUndefined) + .reduce((prev, curr) => { + const next = { ...prev }; + next[curr.id] = curr; + return next; + }, {}); + }, [idList, selectiveContextReadAll, entityName]); + + const nameSpacedKey = getNameSpacedKey(entityName, 'stringMap'); + console.log(nameSpacedKey, stringMap); + return useSelectiveContextAnyController>({ + contextKey: nameSpacedKey, + listenerKey: listenerKey, + initialValue: stringMap + }); +} diff --git a/app/graphing/graph-types/work-task-types/work-task-type-dto-details.tsx b/app/graphing/graph-types/work-task-types/work-task-type-dto-details.tsx index 43f4e71..c9b39cc 100644 --- a/app/graphing/graph-types/work-task-types/work-task-type-dto-details.tsx +++ b/app/graphing/graph-types/work-task-types/work-task-type-dto-details.tsx @@ -1,39 +1,57 @@ 'use client'; import { DataNode } from '../../../api/zod-mods'; -import React, { Fragment } from 'react'; +import React from 'react'; import { WorkTaskTypeDto } from '../../../api/dtos/WorkTaskTypeDtoSchema'; -import { - NodeDetailsListBoxButton, - NodeDetailsListBoxOption, - NodeDetailsListBoxOptions -} from '../organization/curriculum-delivery-details'; -import { useServiceCategoryContext } from '../../../work-types/lessons/use-service-category-context'; -import { Listbox } from '@headlessui/react'; import { StringMap } from '../../../contexts/string-map-context/string-map-reducer'; import { useDirectSimRefEditsDispatch } from '../../editing/functions/use-graph-edit-button-hooks'; import { - ColumnOne, - ColumnsTwoToFour, RenameWorkTaskType, WorkTaskTypeDtoDetailsListenerKey } from './rename-work-task-type'; +import { useSelectiveContextGlobalListener } from '../../../selective-context/components/global/selective-context-manager-global'; +import { getNameSpacedKey } from '../../../selective-context/components/controllers/dto-id-list-controller'; +import { KnowledgeDomainDto } from '../../../api/dtos/KnowledgeDomainDtoSchema'; +import { KnowledgeLevelDto } from '../../../api/dtos/KnowledgeLevelDtoSchema'; +import { AssignItemFromObjectEntries } from './assign-item-from-object-entries'; +import { HasNumberIdDto } from '../../../api/dtos/HasNumberIdDtoSchema'; +import { ObjectPlaceholder } from '../../../api/main'; + +export interface NodeDetailsUiComponentProps { + node: DataNode; +} export default function WorkTaskTypeDtoDetails({ node -}: { - node: DataNode; -}) { +}: NodeDetailsUiComponentProps) { const { id, data } = node; const { knowledgeDomainId, knowledgeLevelId } = data; - const { domainMap, levelMap } = useServiceCategoryContext(); + const { currentState: kDomainMap } = useSelectiveContextGlobalListener< + StringMap + >({ + contextKey: getNameSpacedKey('knowledgeDomain', 'stringMap'), + listenerKey: `workTaskType:${node.id}:details`, + initialValue: ObjectPlaceholder + }); + const { currentState: kLevelMap } = useSelectiveContextGlobalListener< + StringMap + >({ + contextKey: getNameSpacedKey('knowledgeLevel', 'stringMap'), + listenerKey: `workTaskType:${node.id}:details`, + initialValue: ObjectPlaceholder + }); + + console.log(kLevelMap, kDomainMap, node); + + console.log(knowledgeDomainId, knowledgeLevelId); + const editListenerKey = `${WorkTaskTypeDtoDetailsListenerKey}-${id}`; const { incrementSimVersion, nodeListRef } = useDirectSimRefEditsDispatch(editListenerKey); const handleKnowledgeDomainChange = (domainId: string) => { - const updatedDomain = domainMap[domainId]; + const updatedDomain: KnowledgeDomainDto = kDomainMap[domainId]; if (nodeListRef === null) return; const find = nodeListRef.current.find((n) => n.id === id); if (find === undefined) return; @@ -51,17 +69,17 @@ export default function WorkTaskTypeDtoDetails({ kd.name} idAccessor={(kd) => kd.id.toString()} /> kl.name} idAccessor={(kl) => kl.id.toString()} /> @@ -69,47 +87,3 @@ export default function WorkTaskTypeDtoDetails({ ); } - -function AssignItemFromObjectEntries({ - itemDescriptor, - currentAssignment, - onChange, - optionsMap, - labelAccessor, - idAccessor -}: { - itemDescriptor: string; - currentAssignment: string; - onChange: (value: string) => void; - optionsMap: StringMap; - labelAccessor: (item: T) => string; - idAccessor: (item: T) => string; -}) { - return ( - <> - {itemDescriptor} - - - - {labelAccessor(optionsMap[currentAssignment])} - - - {Object.values(optionsMap).map((option) => ( - - {({ selected, active }) => ( - - {labelAccessor(option)} - - )} - - ))} - - - - - ); -} diff --git a/app/selective-context/components/controllers/dto-id-list-controller.tsx b/app/selective-context/components/controllers/dto-id-list-controller.tsx index 6034d2f..fc36799 100644 --- a/app/selective-context/components/controllers/dto-id-list-controller.tsx +++ b/app/selective-context/components/controllers/dto-id-list-controller.tsx @@ -27,6 +27,8 @@ export default function DtoIdListController({ initialValue: idListArray }); + console.log(entityName, currentState); + const { currentState: changedDtos } = useSelectiveContextAnyController< (string | number)[] >({ @@ -98,7 +100,7 @@ export interface DtoListControllerProps { deleteServerAction?: (idList: any[]) => ActionResponsePromise; } -function getNameSpacedKey(entityName: string, keyType: string) { +export function getNameSpacedKey(entityName: string, keyType: string) { return `${entityName}:${keyType}`; } diff --git a/app/selective-context/components/global/selective-context-manager-global.tsx b/app/selective-context/components/global/selective-context-manager-global.tsx index 0d75cfb..007ec2c 100644 --- a/app/selective-context/components/global/selective-context-manager-global.tsx +++ b/app/selective-context/components/global/selective-context-manager-global.tsx @@ -8,6 +8,7 @@ import { } from '../../hooks/generic/use-selective-context-controller'; import { useSelectiveContextListener } from '../../hooks/generic/use-selective-context-listener'; import { useSelectiveContextDispatch } from '../../hooks/generic/use-selective-context-dispatch'; +import { ObjectPlaceholder } from '../../../api/main'; export default function SelectiveContextManagerGlobal({ children @@ -21,8 +22,8 @@ export default function SelectiveContextManagerGlobal({ export function useSelectiveContextAnyController({ contextKey, - initialValue, - listenerKey + listenerKey, + initialValue }: UseSelectiveContextParams) { return useSelectiveContextController( contextKey, @@ -36,7 +37,7 @@ export function useSelectiveContextAnyController({ export function useSelectiveContextAnyDispatch({ contextKey, listenerKey, - initialValue + initialValue = ObjectPlaceholder as T }: UseSelectiveContextParams) { return useSelectiveContextDispatch( contextKey, @@ -51,8 +52,10 @@ export function useSelectiveContextAnyDispatch({ export function useSelectiveContextGlobalListener({ contextKey, listenerKey, - initialValue + initialValue = ObjectPlaceholder as T }: UseSelectiveContextParams) { + console.log(contextKey, listenerKey); + return useSelectiveContextListener( contextKey, listenerKey, diff --git a/app/selective-context/hooks/generic/use-selective-context-controller.ts b/app/selective-context/hooks/generic/use-selective-context-controller.ts index fd6f17e..9894ba4 100644 --- a/app/selective-context/hooks/generic/use-selective-context-controller.ts +++ b/app/selective-context/hooks/generic/use-selective-context-controller.ts @@ -64,11 +64,11 @@ export function useSelectiveContextController( const dispatch = (action: UpdateAction) => dispatchUpdate(action); const [isInitialized, setIsInitialized] = useState(false); - useEffect(() => { - if (freshRef[contextKey] === undefined) { - freshRef[contextKey] = initialValue; - } - }, [freshRef, initialValue, contextKey]); + // useEffect(() => { + if (freshRef[contextKey] === undefined) { + freshRef[contextKey] = initialValue; + } + // }, [freshRef, initialValue, contextKey]); useEffect(() => { if (!isInitialized) { diff --git a/app/work-types/lessons/[typeNameLike]/page.tsx b/app/work-types/lessons/[typeNameLike]/page.tsx index bb4c684..4713e55 100644 --- a/app/work-types/lessons/[typeNameLike]/page.tsx +++ b/app/work-types/lessons/[typeNameLike]/page.tsx @@ -2,12 +2,13 @@ import { ActionResponsePromise } from '../../../api/actions/actionResponse'; import { GraphDto } from '../../../api/zod-mods'; import { WorkTaskTypeDto } from '../../../api/dtos/WorkTaskTypeDtoSchema'; import LessonTypeGraphPage from '../lesson-type-graph-page'; -import ServiceCategoryContextInit from '../service-category-context-init'; -import { EmptyArray, SECONDARY_EDUCATION_CATEGORY_ID } from '../../../api/main'; +import { SECONDARY_EDUCATION_CATEGORY_ID } from '../../../api/main'; import { DataNotFoundCard } from '../../../timetables/students/[schedule]/data-not-found-card'; import { getGraph } from '../../../api/READ-ONLY-generated-actions/WorkTaskType'; import { getDtoListByExampleList } from '../../../api/READ-ONLY-generated-actions/KnowledgeDomain'; -import { getDtoListByExampleList as getKnowledgeLevelsByExampleList } from '../../../api/READ-ONLY-generated-actions/KnowledgeLevel'; +import { getAll } from '../../../api/READ-ONLY-generated-actions/KnowledgeLevel'; +import { getAll as getAllKnowledgeDomains } from '../../../api/READ-ONLY-generated-actions/KnowledgeDomain'; +import DtoControllerArray from '../../../selective-context/components/controllers/dto-controller-array'; export const dynamic = 'force-dynamic'; @@ -21,13 +22,9 @@ export default async function LessonTypesPage({ GraphDto > = getGraph(); - const { data: kLevels } = await getKnowledgeLevelsByExampleList([ - { serviceCategoryId: SECONDARY_EDUCATION_CATEGORY_ID } - ]); + const { data: kLevels } = await getAll(); - const { data: kDomains } = await getDtoListByExampleList([ - { serviceCategoryId: SECONDARY_EDUCATION_CATEGORY_ID } - ]); + const { data: kDomains } = await getAllKnowledgeDomains(); if (kLevels === undefined || kDomains === undefined) { return Category data missing.; @@ -38,16 +35,14 @@ export default async function LessonTypesPage({ const allOptions = ['All', ...yearGroupOptions, ...subjectOptions]; return ( - + <> + + - + ); }