diff --git a/frontend/components/ui/index.ts b/frontend/components/ui/index.ts index 7ef1810..4e9c228 100644 --- a/frontend/components/ui/index.ts +++ b/frontend/components/ui/index.ts @@ -9,6 +9,7 @@ export { DefinitionList, EmptyTableBody, FONT_FAMILY, + FaCircleInfoIcon, FaCirclePlusIcon, FaGearIcon, FaPencilIcon, @@ -31,7 +32,9 @@ export { TableReel, Td, Text, + Textarea, Th, + Tooltip, UnstyledButton, createTheme, defaultBorder, diff --git a/frontend/constants/path.ts b/frontend/constants/path.ts index cdddc40..33e9415 100644 --- a/frontend/constants/path.ts +++ b/frontend/constants/path.ts @@ -26,6 +26,9 @@ export const path = { sources: { index: () => '/api/sources.json', show: (sourceName: string) => `/api/sources/${sourceName}.json`, + memo: { + update: (sourceName: string) => `/api/sources/${sourceName}/memo.json`, + }, modules: { update: (sourceName: string) => `/api/sources/${sourceName}/modules.json`, }, diff --git a/frontend/models/combinedDefinition.ts b/frontend/models/combinedDefinition.ts index d46ecd3..741eb10 100644 --- a/frontend/models/combinedDefinition.ts +++ b/frontend/models/combinedDefinition.ts @@ -1,4 +1,5 @@ import { Module } from './module' +import { Source } from './source' type BaseDotMetadata = { id: string @@ -7,6 +8,7 @@ type BaseDotMetadata = { export type DotSourceMetadata = { type: 'source' sourceName: string + memo: string modules: Module[] } & BaseDotMetadata @@ -34,10 +36,5 @@ export type CombinedDefinition = { titles: string[] dot: string dotMetadata: DotMetadata[] - sources: Array<{ sourceName: string; modules: Module[] }> -} - -export type DotSource = { - type: 'source' - sourceName: string + sources: Source[] } diff --git a/frontend/models/module.ts b/frontend/models/module.ts index 7a2bfc6..640aed3 100644 --- a/frontend/models/module.ts +++ b/frontend/models/module.ts @@ -8,6 +8,7 @@ export type SpecificModule = { }> sources: Array<{ sourceName: string + memo: string }> relatedDefinitions: Array<{ id: number diff --git a/frontend/models/source.ts b/frontend/models/source.ts index b67a8a3..1717e64 100644 --- a/frontend/models/source.ts +++ b/frontend/models/source.ts @@ -3,6 +3,7 @@ import { Module } from './module' export type Source = { sourceName: string + memo: string modules: Module[] } @@ -18,6 +19,7 @@ type ReverseDependency = { export type SpecificSource = { sourceName: string + memo: string modules: Module[] relatedDefinitions: RelatedDefinition[] reverseDependencies: ReverseDependency[] @@ -48,10 +50,7 @@ export const sortSources = (sources: Source[], key: 'sourceName' | 'modules', so } case 'modules': { sorted = sorted.sort((a, b) => - ascString( - a.modules.map((module) => module.moduleName).join('-'), - b.modules.map((module) => module.moduleName).join('-'), - ), + ascString(a.modules.map((module) => module.moduleName).join('-'), b.modules.map((module) => module.moduleName).join('-')), ) } } diff --git a/frontend/pages/DefinitionList/components/DefinitionList/List.tsx b/frontend/pages/DefinitionList/components/DefinitionList/List.tsx index 3b1ae4b..979d4b5 100644 --- a/frontend/pages/DefinitionList/components/DefinitionList/List.tsx +++ b/frontend/pages/DefinitionList/components/DefinitionList/List.tsx @@ -82,9 +82,10 @@ export const List = forwardRef((props, ref) => { } const isSelected = selectedDefinitionIds.includes(definition.id) - const countLabel = definition.unclassifiedSourcesCount === 0 ? - `(${definition.sourcesCount})` : - `(${definition.unclassifiedSourcesCount}/${definition.sourcesCount})` + const countLabel = + definition.unclassifiedSourcesCount === 0 + ? `(${definition.sourcesCount})` + : `(${definition.unclassifiedSourcesCount}/${definition.sourcesCount})` items.push({ key: `definition-${definition.id}`, diff --git a/frontend/pages/DefinitionList/components/DefinitionSources/DefinitionSources.tsx b/frontend/pages/DefinitionList/components/DefinitionSources/DefinitionSources.tsx index 0071e02..5906640 100644 --- a/frontend/pages/DefinitionList/components/DefinitionSources/DefinitionSources.tsx +++ b/frontend/pages/DefinitionList/components/DefinitionSources/DefinitionSources.tsx @@ -2,13 +2,27 @@ import { FC, useCallback, useMemo, useState } from 'react' import styled from 'styled-components' import { Link } from '@/components/Link' -import { Aside, Button, Cluster, EmptyTableBody, FaPencilIcon, Table, TableReel, Td, Text, Th } from '@/components/ui' +import { + Aside, + Button, + Cluster, + EmptyTableBody, + FaCircleInfoIcon, + FaPencilIcon, + Table, + TableReel, + Td, + Text, + Th, + Tooltip, +} from '@/components/ui' import { path } from '@/constants/path' import { color } from '@/constants/theme' import { CombinedDefinition } from '@/models/combinedDefinition' import { SourceModulesComboBox } from '../SourceModulesComboBox' import { sortSources } from '@/models/source' +import { SourceMemoInput } from '../SourceMemoInput' type Props = { combinedDefinition: CombinedDefinition @@ -26,7 +40,8 @@ type SortState = { export const DefinitionSources: FC = ({ combinedDefinition, mutateCombinedDefinition }) => { const [sortState, setSortState] = useState({ key: 'sourceName', sort: 'asc' }) - const [editingSourceNames, setEditingSourceNames] = useState([]) + const [editingModulesSourceNames, setEditingModulesSourceNames] = useState([]) + const [editingMemoSourceNames, setEditingMemoSourceNames] = useState([]) const setNextSortType = useCallback( (key: SortState['key']) => { @@ -58,6 +73,7 @@ export const DefinitionSources: FC = ({ combinedDefinition, mutateCombine setNextSortType('sourceName')}> Source name + Memo setNextSortType('modules')}> Modules @@ -75,17 +91,54 @@ export const DefinitionSources: FC = ({ combinedDefinition, mutateCombine {source.sourceName} - {editingSourceNames.includes(source.sourceName) ? ( + + {editingMemoSourceNames.includes(source.sourceName) ? ( + { + setEditingMemoSourceNames((prev) => prev.filter((name) => name !== source.sourceName)) + mutateCombinedDefinition() + }} + onClose={() => { + setEditingMemoSourceNames((prev) => prev.filter((name) => name !== source.sourceName)) + }} + /> + ) : ( + + {source.memo !== '' ? ( + + + + ) : ( + + + + )} +
+ +
+
+ )} + + {!editingMemoSourceNames.includes(source.sourceName) && + editingModulesSourceNames.includes(source.sourceName) ? ( { - setEditingSourceNames((prev) => prev.filter((name) => name !== source.sourceName)) + setEditingModulesSourceNames((prev) => prev.filter((name) => name !== source.sourceName)) mutateCombinedDefinition() }} onClose={() => { - setEditingSourceNames((prev) => prev.filter((name) => name !== source.sourceName)) + setEditingModulesSourceNames((prev) => prev.filter((name) => name !== source.sourceName)) }} /> @@ -104,7 +157,7 @@ export const DefinitionSources: FC = ({ combinedDefinition, mutateCombine
+ + )} + + ), + }, { term: 'Modules', description: ( @@ -69,7 +116,7 @@ const SourceDotMetadataContent: FC<{ metadata: DotSourceMetadata } & Pick + return } const DependencyDotMetadataContent: FC<{ metadata: DotDependencyMetadata }> = ({ metadata }) => ( @@ -132,7 +179,7 @@ export const MetadataDialog: FC = ({ dotMetadata, isOpen, onClose, top, l return ( Description} + header={Memo} onClickClose={onClose} onPressEscape={onClose} top={top} diff --git a/frontend/pages/DefinitionList/components/SourceMemoInput/SourceMemoInput.tsx b/frontend/pages/DefinitionList/components/SourceMemoInput/SourceMemoInput.tsx new file mode 100644 index 0000000..0bc32b8 --- /dev/null +++ b/frontend/pages/DefinitionList/components/SourceMemoInput/SourceMemoInput.tsx @@ -0,0 +1,45 @@ +import React, { FC, useCallback, useEffect, useState } from 'react' + +import { Button, Cluster, FaXmarkIcon, FormControl, Input, Textarea } from '@/components/ui' +import { useSourceMemo } from '@/repositories/sourceMemoRepository' + +type Props = { + sourceName: string + initialMemo: string + onClose: () => void + onUpdate: () => void +} + +export const SourceMemoInput: FC = ({ sourceName, initialMemo, onClose, onUpdate }) => { + const { trigger } = useSourceMemo(sourceName) + + const [memo, setMemo] = useState(initialMemo) + + const handleUpdate = useCallback(async () => { + await trigger({ memo }) + onUpdate() + }, [trigger, memo, onUpdate]) + + const onInputMemo = useCallback( + (event: React.ChangeEvent) => { + setMemo(event.target.value) + }, + [setMemo], + ) + + return ( + +
+ +