Skip to content

Commit

Permalink
Merge pull request #40 from alpaca-tc/add_memo
Browse files Browse the repository at this point in the history
Add memo
  • Loading branch information
alpaca-tc authored May 13, 2024
2 parents d776477 + 101d17c commit 2ff0a7c
Show file tree
Hide file tree
Showing 25 changed files with 502 additions and 112 deletions.
3 changes: 3 additions & 0 deletions frontend/components/ui/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export {
DefinitionList,
EmptyTableBody,
FONT_FAMILY,
FaCircleInfoIcon,
FaCirclePlusIcon,
FaGearIcon,
FaPencilIcon,
Expand All @@ -31,7 +32,9 @@ export {
TableReel,
Td,
Text,
Textarea,
Th,
Tooltip,
UnstyledButton,
createTheme,
defaultBorder,
Expand Down
3 changes: 3 additions & 0 deletions frontend/constants/path.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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`,
},
Expand Down
9 changes: 3 additions & 6 deletions frontend/models/combinedDefinition.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Module } from './module'
import { Source } from './source'

type BaseDotMetadata = {
id: string
Expand All @@ -7,6 +8,7 @@ type BaseDotMetadata = {
export type DotSourceMetadata = {
type: 'source'
sourceName: string
memo: string
modules: Module[]
} & BaseDotMetadata

Expand Down Expand Up @@ -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[]
}
1 change: 1 addition & 0 deletions frontend/models/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export type SpecificModule = {
}>
sources: Array<{
sourceName: string
memo: string
}>
relatedDefinitions: Array<{
id: number
Expand Down
7 changes: 3 additions & 4 deletions frontend/models/source.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Module } from './module'

export type Source = {
sourceName: string
memo: string
modules: Module[]
}

Expand All @@ -18,6 +19,7 @@ type ReverseDependency = {

export type SpecificSource = {
sourceName: string
memo: string
modules: Module[]
relatedDefinitions: RelatedDefinition[]
reverseDependencies: ReverseDependency[]
Expand Down Expand Up @@ -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('-')),
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,9 +82,10 @@ export const List = forwardRef<HTMLLIElement, Props>((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}`,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -26,7 +40,8 @@ type SortState = {

export const DefinitionSources: FC<Props> = ({ combinedDefinition, mutateCombinedDefinition }) => {
const [sortState, setSortState] = useState<SortState>({ key: 'sourceName', sort: 'asc' })
const [editingSourceNames, setEditingSourceNames] = useState<string[]>([])
const [editingModulesSourceNames, setEditingModulesSourceNames] = useState<string[]>([])
const [editingMemoSourceNames, setEditingMemoSourceNames] = useState<string[]>([])

const setNextSortType = useCallback(
(key: SortState['key']) => {
Expand Down Expand Up @@ -58,6 +73,7 @@ export const DefinitionSources: FC<Props> = ({ combinedDefinition, mutateCombine
<Th sort={sortState.key === 'sourceName' ? sortState.sort : 'none'} onSort={() => setNextSortType('sourceName')}>
Source name
</Th>
<Th>Memo</Th>
<Th fixed sort={sortState.key === 'modules' ? sortState.sort : 'none'} onSort={() => setNextSortType('modules')}>
Modules
</Th>
Expand All @@ -75,17 +91,54 @@ export const DefinitionSources: FC<Props> = ({ combinedDefinition, mutateCombine
<Td>
<Link to={path.sources.show(source.sourceName)}>{source.sourceName}</Link>
</Td>
{editingSourceNames.includes(source.sourceName) ? (
<Td>
{editingMemoSourceNames.includes(source.sourceName) ? (
<SourceMemoInput
sourceName={source.sourceName}
initialMemo={source.memo}
onUpdate={() => {
setEditingMemoSourceNames((prev) => prev.filter((name) => name !== source.sourceName))
mutateCombinedDefinition()
}}
onClose={() => {
setEditingMemoSourceNames((prev) => prev.filter((name) => name !== source.sourceName))
}}
/>
) : (
<FixedWidthMemo align="center">
{source.memo !== '' ? (
<Tooltip message={source.memo} horizontal="center" vertical="bottom">
<FaCircleInfoIcon />
</Tooltip>
) : (
<Transparent>
<FaCircleInfoIcon />
</Transparent>
)}
<div>
<Button
square={true}
onClick={() => setEditingMemoSourceNames((prev) => [...prev, source.sourceName])}
size="s"
>
<FaPencilIcon alt="Edit" />
</Button>
</div>
</FixedWidthMemo>
)}
</Td>
{!editingMemoSourceNames.includes(source.sourceName) &&
editingModulesSourceNames.includes(source.sourceName) ? (
<Td fixed colSpan={2}>
<SourceModulesComboBox
sourceName={source.sourceName}
initialModules={source.modules}
onUpdate={() => {
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))
}}
/>
</Td>
Expand All @@ -104,7 +157,7 @@ export const DefinitionSources: FC<Props> = ({ combinedDefinition, mutateCombine
<div>
<Button
square={true}
onClick={() => setEditingSourceNames((prev) => [...prev, source.sourceName])}
onClick={() => setEditingModulesSourceNames((prev) => [...prev, source.sourceName])}
size="s"
>
<FaPencilIcon alt="Edit" />
Expand Down Expand Up @@ -141,6 +194,14 @@ const TableWrapper = styled.div`
overflow-x: scroll;
`

const Transparent = styled.span`
opacity: 0;
`

const FixedWidthMemo = styled(Cluster)`
width: 4em;
`

const StyledTable = styled(Table)`
border-left: 1px ${color.BORDER} solid;
`
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,26 @@ import { ComponentProps, FC, useMemo, useState } from 'react'
import styled from 'styled-components'

import { Link } from '@/components/Link'
import { Button, Cluster, DefinitionList, FaPencilIcon, Heading, ModelessDialog, Stack, Table, Td, Th } from '@/components/ui'
import {
Button,
Cluster,
DefinitionList,
FaPencilIcon,
Heading,
ModelessDialog,
Stack,
Table,
Text,
Td,
Th,
} from '@/components/ui'
import { path } from '@/constants/path'
import { spacing } from '@/constants/theme'
import { DotDependencyMetadata, DotMetadata, DotModuleMetadata, DotSourceMetadata } from '@/models/combinedDefinition'

import { SourceModulesComboBox } from '../SourceModulesComboBox'
import { DialogProps } from '../dialog'
import { SourceMemoInput } from '../SourceMemoInput'

type Props = {
dotMetadata: DotMetadata | null
Expand All @@ -25,11 +38,45 @@ const SourceDotMetadataContent: FC<{ metadata: DotSourceMetadata } & Pick<Props,
mutateCombinedDefinition,
}) => {
const [editingModules, setEditingModules] = useState<boolean>(false)
const [editingMemo, setEditingMemo] = useState<boolean>(false)
const items: ComponentProps<typeof DefinitionList>['items'] = [
{
term: 'Source Name',
description: <Link to={path.sources.show(metadata.sourceName)}>{metadata.sourceName}</Link>,
},
{
term: 'Memo',
description: (
<Cluster>
{editingMemo ? (
<SourceMemoInput
sourceName={metadata.sourceName}
initialMemo={metadata.memo}
onUpdate={() => {
setEditingMemo(false)
mutateCombinedDefinition()
}}
onClose={() => {
setEditingMemo(false)
}}
/>
) : (
<>
<Text>{metadata.memo}</Text>
<Button
square={true}
onClick={() => {
setEditingMemo(true)
}}
size="s"
>
<FaPencilIcon alt="編集" />
</Button>
</>
)}
</Cluster>
),
},
{
term: 'Modules',
description: (
Expand Down Expand Up @@ -69,7 +116,7 @@ const SourceDotMetadataContent: FC<{ metadata: DotSourceMetadata } & Pick<Props,
},
]

return <DefinitionList items={items} />
return <DefinitionList maxColumns={1} items={items} />
}

const DependencyDotMetadataContent: FC<{ metadata: DotDependencyMetadata }> = ({ metadata }) => (
Expand Down Expand Up @@ -132,7 +179,7 @@ export const MetadataDialog: FC<Props> = ({ dotMetadata, isOpen, onClose, top, l
return (
<ModelessDialog
isOpen={!!(isOpen && dotMetadata)}
header={<ModelessHeading>Description</ModelessHeading>}
header={<ModelessHeading>Memo</ModelessHeading>}
onClickClose={onClose}
onPressEscape={onClose}
top={top}
Expand Down
Original file line number Diff line number Diff line change
@@ -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<Props> = ({ sourceName, initialMemo, onClose, onUpdate }) => {
const { trigger } = useSourceMemo(sourceName)

const [memo, setMemo] = useState<string>(initialMemo)

const handleUpdate = useCallback(async () => {
await trigger({ memo })
onUpdate()
}, [trigger, memo, onUpdate])

const onInputMemo = useCallback(
(event: React.ChangeEvent<HTMLTextAreaElement | HTMLInputElement>) => {
setMemo(event.target.value)
},
[setMemo],
)

return (
<Cluster>
<div>
<FormControl title="Memo" helpMessage="Free memo field.">
<Textarea onChange={onInputMemo} value={memo} autoResize />
</FormControl>
</div>
<Button square={true} variant="primary" onClick={handleUpdate} size="s">
Update
</Button>
<Button square={true} onClick={onClose} size="s">
<FaXmarkIcon alt="Cancel" />
</Button>
</Cluster>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { SourceMemoInput } from './SourceMemoInput'
Loading

0 comments on commit 2ff0a7c

Please sign in to comment.