From 5ca3cc0fadcde431a8a0e1f7b73a528428de5d23 Mon Sep 17 00:00:00 2001 From: alpaca-tc Date: Mon, 13 May 2024 17:06:41 +0900 Subject: [PATCH 1/4] Modify the structure so that module_store can store multiple types of data --- lib/diver_down/web/action.rb | 16 ++++---- lib/diver_down/web/definition_to_dot.rb | 12 +++--- lib/diver_down/web/module_store.rb | 8 ++-- spec/diver_down/web/definition_to_dot_spec.rb | 30 +++++++-------- spec/diver_down/web/module_store_spec.rb | 38 +++++++++---------- spec/diver_down/web_spec.rb | 18 ++++----- 6 files changed, 61 insertions(+), 61 deletions(-) diff --git a/lib/diver_down/web/action.rb b/lib/diver_down/web/action.rb index afbc487..40dc84d 100644 --- a/lib/diver_down/web/action.rb +++ b/lib/diver_down/web/action.rb @@ -33,13 +33,13 @@ def sources end # rubocop:enable Style/HashEachMethods - classified_sources_count = source_names.count { @module_store.include?(_1) } + classified_sources_count = source_names.count { @module_store.classified?(_1) } json( sources: source_names.sort.map do |source_name| { source_name:, - modules: @module_store.get(source_name).map do |module_name| + modules: @module_store.get_modules(source_name).map do |module_name| { module_name: } end, } @@ -56,7 +56,7 @@ def modules # rubocop:disable Style/HashEachMethods @store.each do |_, definition| definition.sources.each do |source| - modules = @module_store.get(source.source_name) + modules = @module_store.get_modules(source.source_name) module_set.add(modules) unless modules.empty? end end @@ -83,7 +83,7 @@ def module(module_names) # rubocop:disable Style/HashEachMethods @store.each do |_, definition| definition.sources.each do |source| - source_module_names = @module_store.get(source.source_name) + source_module_names = @module_store.get_modules(source.source_name) next unless source_module_names[0..module_names.size - 1] == module_names @@ -137,7 +137,7 @@ def definitions(page:, per:, title:, source:, definition_group:) definition_group: definition.definition_group, title: definition.title, sources_count: definition.sources.size, - unclassified_sources_count: definition.sources.reject { @module_store.include?(_1.source_name) }.size, + unclassified_sources_count: definition.sources.reject { @module_store.classified?(_1.source_name) }.size, } end, pagination: pagination.to_h @@ -195,7 +195,7 @@ def combine_definitions(bit_id, compound, concentrate, only_module) sources: definition.sources.map do { source_name: _1.source_name, - modules: @module_store.get(_1.source_name).map do |module_name| + modules: @module_store.get_modules(_1.source_name).map do |module_name| { module_name: } end, } @@ -246,7 +246,7 @@ def source(source_name) [] else source = DiverDown::Definition::Source.combine(*found_sources) - @module_store.get(source.source_name) + @module_store.get_modules(source.source_name) end json( @@ -287,7 +287,7 @@ def set_modules(source_name, modules) end if found_source - @module_store.set(source_name, modules) + @module_store.set_modules(source_name, modules) @module_store.flush json({}) diff --git a/lib/diver_down/web/definition_to_dot.rb b/lib/diver_down/web/definition_to_dot.rb index d571ac2..081f6fa 100644 --- a/lib/diver_down/web/definition_to_dot.rb +++ b/lib/diver_down/web/definition_to_dot.rb @@ -31,7 +31,7 @@ def to_h private def source_to_h - modules = module_store.get(data.source_name).map do + modules = module_store.get_modules(data.source_name).map do { module_name: _1, } @@ -185,11 +185,11 @@ def render_only_modules dependency_map = Hash.new { |h, k| h[k] = Hash.new { |hi, ki| hi[ki] = [] } } definition.sources.sort_by(&:source_name).each do |source| - source_modules = module_store.get(source.source_name) + source_modules = module_store.get_modules(source.source_name) next if source_modules.empty? source.dependencies.each do |dependency| - dependency_modules = module_store.get(dependency.source_name) + dependency_modules = module_store.get_modules(dependency.source_name) next if dependency_modules.empty? dependency_map[source_modules][dependency_modules].push(dependency) @@ -268,7 +268,7 @@ def render_only_modules def render_sources by_modules = definition.sources.group_by do |source| - module_store.get(source.source_name) + module_store.get_modules(source.source_name) end # Remove duplicated prefix modules @@ -326,8 +326,8 @@ def insert_source(source) def insert_dependencies(source) source.dependencies.each do attributes = {} - ltail = module_label(*module_store.get(source.source_name)) - lhead = module_label(*module_store.get(_1.source_name)) + ltail = module_label(*module_store.get_modules(source.source_name)) + lhead = module_label(*module_store.get_modules(_1.source_name)) if @compound && (ltail || lhead) # Rendering of dependencies between modules is done only once diff --git a/lib/diver_down/web/module_store.rb b/lib/diver_down/web/module_store.rb index f000673..afaa78e 100644 --- a/lib/diver_down/web/module_store.rb +++ b/lib/diver_down/web/module_store.rb @@ -17,7 +17,7 @@ def initialize(path) # @param source_name [String] # @param module_names [Array] - def set(source_name, module_names) + def set_modules(source_name, module_names) @store[source_name] = module_names.dup.reject do BLANK_RE.match?(_1) end.freeze @@ -25,14 +25,14 @@ def set(source_name, module_names) # @param source_name [String] # @return [Array] - def get(source_name) + def get_modules(source_name) @store[source_name] || BLANK_ARRAY end # @param source_name [String] # @return [Boolean] - def include?(source_name) - get(source_name).any? + def classified?(source_name) + get_modules(source_name).any? end # @return [Hash] diff --git a/spec/diver_down/web/definition_to_dot_spec.rb b/spec/diver_down/web/definition_to_dot_spec.rb index 30d7e43..0b8ebca 100644 --- a/spec/diver_down/web/definition_to_dot_spec.rb +++ b/spec/diver_down/web/definition_to_dot_spec.rb @@ -136,7 +136,7 @@ def build_definition(title: 'title', sources: []) ] ) - module_store.set('a.rb', ['A', 'B']) + module_store.set_modules('a.rb', ['A', 'B']) instance = described_class.new(definition, module_store) @@ -207,9 +207,9 @@ def build_definition(title: 'title', sources: []) ] ) - module_store.set('a.rb', ['A']) - module_store.set('b.rb', ['B']) - module_store.set('c.rb', ['B']) + module_store.set_modules('a.rb', ['A']) + module_store.set_modules('b.rb', ['B']) + module_store.set_modules('c.rb', ['B']) instance = described_class.new(definition, module_store, compound: true) expect(instance.to_s).to eq(<<~DOT) @@ -320,9 +320,9 @@ def build_definition(title: 'title', sources: []) ] ) - module_store.set('a.rb', ['A']) - module_store.set('b.rb', ['B']) - module_store.set('c.rb', ['B']) + module_store.set_modules('a.rb', ['A']) + module_store.set_modules('b.rb', ['B']) + module_store.set_modules('c.rb', ['B']) instance = described_class.new(definition, module_store, compound: true) expect(instance.to_s).to eq(<<~DOT) @@ -454,11 +454,11 @@ def build_definition(title: 'title', sources: []) ] ) - module_store.set('a.rb', ['A']) - module_store.set('b.rb', ['B']) - module_store.set('c.rb', ['B']) - module_store.set('d.rb', ['B', 'C']) - module_store.set('unknown.rb', ['Unknown']) + module_store.set_modules('a.rb', ['A']) + module_store.set_modules('b.rb', ['B']) + module_store.set_modules('c.rb', ['B']) + module_store.set_modules('d.rb', ['B', 'C']) + module_store.set_modules('unknown.rb', ['Unknown']) instance = described_class.new(definition, module_store, only_module: true) expect(instance.to_s).to eq(<<~DOT) @@ -559,9 +559,9 @@ def build_definition(title: 'title', sources: []) ] ) - module_store.set('b.rb', ['A']) - module_store.set('c.rb', ['A', 'C']) - module_store.set('d.rb', ['B']) + module_store.set_modules('b.rb', ['A']) + module_store.set_modules('c.rb', ['A', 'C']) + module_store.set_modules('d.rb', ['B']) instance = described_class.new(definition, module_store, concentrate: true) expect(instance.to_s).to eq(<<~DOT) diff --git a/spec/diver_down/web/module_store_spec.rb b/spec/diver_down/web/module_store_spec.rb index e039885..25d563f 100644 --- a/spec/diver_down/web/module_store_spec.rb +++ b/spec/diver_down/web/module_store_spec.rb @@ -9,42 +9,42 @@ end end - describe '#set' do - it 'set modules to source' do + describe '#set_modules' do + it 'set_modules modules to source' do tempfile = Tempfile.new(['test', '.yaml']) instance = described_class.new(tempfile.path) expect { - instance.set('a.rb', ['A', 'B']) + instance.set_modules('a.rb', ['A', 'B']) }.to change { - instance.get('a.rb') + instance.get_modules('a.rb') }.from([]).to(['A', 'B']) end end - describe '#get' do - it 'returns blank array if source is not set' do + describe '#get_modules' do + it 'returns blank array if source is not set_modules' do tempfile = Tempfile.new(['test', '.yaml']) instance = described_class.new(tempfile.path) - expect(instance.get('a.rb')).to eq([]) + expect(instance.get_modules('a.rb')).to eq([]) - instance.set('a.rb', ['A', 'B']) - expect(instance.get('a.rb')).to eq(['A', 'B']) + instance.set_modules('a.rb', ['A', 'B']) + expect(instance.get_modules('a.rb')).to eq(['A', 'B']) end end - describe '#include?' do + describe '#classified?' do it 'returns bool' do tempfile = Tempfile.new(['test', '.yaml']) instance = described_class.new(tempfile.path) - instance.set('a.rb', []) - instance.set('b.rb', ['A']) + instance.set_modules('a.rb', []) + instance.set_modules('b.rb', ['A']) - expect(instance.include?('a.rb')).to be(false) - expect(instance.include?('b.rb')).to be(true) - expect(instance.include?('c.rb')).to be(false) + expect(instance.classified?('a.rb')).to be(false) + expect(instance.classified?('b.rb')).to be(true) + expect(instance.classified?('c.rb')).to be(false) end end @@ -52,12 +52,12 @@ it 'writes modules to path' do tempfile = Tempfile.new(['test', '.yaml']) instance = described_class.new(tempfile.path) - instance.set('a.rb', ['A', 'B']) + instance.set_modules('a.rb', ['A', 'B']) expect { instance.flush }.to change { - described_class.new(tempfile.path).get('a.rb') + described_class.new(tempfile.path).get_modules('a.rb') }.from([]).to(['A', 'B']) end @@ -68,7 +68,7 @@ sources = ['a.rb', 'b.rb', 'c.rb'] sources.shuffle.each do |source| - instance.set(source, ['A']) + instance.set_modules(source, ['A']) end expect { @@ -87,7 +87,7 @@ sources = ['a.rb', 'b.rb', 'c.rb'] sources.shuffle.each do |source| - instance.set(source, ['A']) + instance.set_modules(source, ['A']) end expect(instance.to_h.keys).to eq(sources) diff --git a/spec/diver_down/web_spec.rb b/spec/diver_down/web_spec.rb index 629167e..3cbb6fb 100644 --- a/spec/diver_down/web_spec.rb +++ b/spec/diver_down/web_spec.rb @@ -76,7 +76,7 @@ def app ] ) store.set(definition_1, definition_2) - module_store.set('a.rb', ['A']) + module_store.set_modules('a.rb', ['A']) get '/api/definitions.json' @@ -315,7 +315,7 @@ def assert_definition_group(definition_group, expected_ids) ) store.set(definition) - module_store.set('a.rb', ['A']) + module_store.set_modules('a.rb', ['A']) get '/api/sources.json' @@ -366,8 +366,8 @@ def assert_definition_group(definition_group, expected_ids) ] ) store.set(definition) - module_store.set('a.rb', ['A', 'B']) - module_store.set('b.rb', ['B', 'C']) + module_store.set_modules('a.rb', ['A', 'B']) + module_store.set_modules('b.rb', ['B', 'C']) get '/api/modules.json' @@ -418,8 +418,8 @@ def assert_definition_group(definition_group, expected_ids) ) ids = store.set(definition_1, definition_2) - module_store.set('a.rb', ['A']) - module_store.set('b.rb', ['A', 'B']) + module_store.set_modules('a.rb', ['A']) + module_store.set_modules('b.rb', ['A', 'B']) get '/api/modules/A.json' @@ -486,7 +486,7 @@ def assert_definition_group(definition_group, expected_ids) ) ids = store.set(definition) - module_store.set('a.rb', ['グローバル']) + module_store.set_modules('a.rb', ['グローバル']) get "/api/modules/#{CGI.escape('グローバル')}.json" @@ -637,7 +637,7 @@ def assert_definition_group(definition_group, expected_ids) expect(last_response.status).to eq(200) - expect(module_store.get('a.rb')).to eq(['A', 'B']) + expect(module_store.get_modules('a.rb')).to eq(['A', 'B']) end it 'ignores blank modules' do @@ -655,7 +655,7 @@ def assert_definition_group(definition_group, expected_ids) expect(last_response.status).to eq(200) - expect(module_store.get('a.rb')).to eq(['B']) + expect(module_store.get_modules('a.rb')).to eq(['B']) end end end From f8eea992c22dfec3e1e6ba1a7760280c97c72937 Mon Sep 17 00:00:00 2001 From: alpaca-tc Date: Mon, 13 May 2024 19:12:52 +0900 Subject: [PATCH 2/4] ModuleStore supports memo --- lib/diver_down/web/module_store.rb | 25 ++++++++++++++-- spec/diver_down/web/module_store_spec.rb | 38 ++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 2 deletions(-) diff --git a/lib/diver_down/web/module_store.rb b/lib/diver_down/web/module_store.rb index afaa78e..6875ab0 100644 --- a/lib/diver_down/web/module_store.rb +++ b/lib/diver_down/web/module_store.rb @@ -6,6 +6,7 @@ module DiverDown class Web class ModuleStore BLANK_ARRAY = [].freeze + BLANK_MEMO = '' BLANK_RE = /\A\s*\z/ private_constant(:BLANK_RE) @@ -18,7 +19,9 @@ def initialize(path) # @param source_name [String] # @param module_names [Array] def set_modules(source_name, module_names) - @store[source_name] = module_names.dup.reject do + source = (@store[source_name] ||= {}) + + source[:modules] = module_names.dup.reject do BLANK_RE.match?(_1) end.freeze end @@ -26,7 +29,25 @@ def set_modules(source_name, module_names) # @param source_name [String] # @return [Array] def get_modules(source_name) - @store[source_name] || BLANK_ARRAY + return BLANK_ARRAY unless @store.key?(source_name) + + @store[source_name][:modules] || BLANK_ARRAY + end + + # @param source_name [String] + # @param memo [String] + # @return [void] + def set_memo(source_name, memo) + source = (@store[source_name] ||= {}) + source[:memo] = memo.strip + end + + # @param source_name [String] + # @return [String] + def get_memo(source_name) + return BLANK_MEMO unless @store.key?(source_name) + + @store[source_name][:memo] || BLANK_MEMO end # @param source_name [String] diff --git a/spec/diver_down/web/module_store_spec.rb b/spec/diver_down/web/module_store_spec.rb index 25d563f..e9918b1 100644 --- a/spec/diver_down/web/module_store_spec.rb +++ b/spec/diver_down/web/module_store_spec.rb @@ -34,6 +34,31 @@ end end + describe '#set_memo' do + it 'sets memo' do + tempfile = Tempfile.new(['test', '.yaml']) + instance = described_class.new(tempfile.path) + + expect { + instance.set_memo('a.rb', ' memo ') + }.to change { + instance.get_memo('a.rb') + }.from('').to('memo') + end + end + + describe '#get_memo' do + it 'returns blank string if source is not set_modules' do + tempfile = Tempfile.new(['test', '.yaml']) + instance = described_class.new(tempfile.path) + + expect(instance.get_memo('a.rb')).to eq('') + + instance.set_memo('a.rb', 'a') + expect(instance.get_memo('a.rb')).to eq('a') + end + end + describe '#classified?' do it 'returns bool' do tempfile = Tempfile.new(['test', '.yaml']) @@ -61,6 +86,18 @@ }.from([]).to(['A', 'B']) end + it 'writes memo to path' do + tempfile = Tempfile.new(['test', '.yaml']) + instance = described_class.new(tempfile.path) + instance.set_memo('a.rb', 'memo') + + expect { + instance.flush + }.to change { + described_class.new(tempfile.path).get_memo('a.rb') + }.from('').to('memo') + end + it 'sorts by source name' do tempfile = Tempfile.new(['test', '.yaml']) instance = described_class.new(tempfile.path) @@ -88,6 +125,7 @@ sources.shuffle.each do |source| instance.set_modules(source, ['A']) + instance.set_memo(source, 'memo') end expect(instance.to_h.keys).to eq(sources) From d811418d1288a7edf48d760ecc7bbae8fa78b908 Mon Sep 17 00:00:00 2001 From: alpaca-tc Date: Mon, 13 May 2024 19:13:11 +0900 Subject: [PATCH 3/4] Backend supports source's memo --- lib/diver_down/web.rb | 4 ++++ lib/diver_down/web/action.rb | 25 +++++++++++++++++++++++++ lib/diver_down/web/definition_to_dot.rb | 1 + 3 files changed, 30 insertions(+) diff --git a/lib/diver_down/web.rb b/lib/diver_down/web.rb index 1c81c96..a922eb7 100644 --- a/lib/diver_down/web.rb +++ b/lib/diver_down/web.rb @@ -68,6 +68,10 @@ def call(env) source = Regexp.last_match[:source] modules = request.params['modules'] || [] action.set_modules(source, modules) + in ['POST', %r{\A/api/sources/(?[^/]+)/memo.json\z}] + source = Regexp.last_match[:source] + memo = request.params['memo'] || '' + action.set_memo(source, memo) in ['GET', %r{\A/api/pid\.json\z}] action.pid in ['GET', %r{\A/api/initialization_status\.json\z}] diff --git a/lib/diver_down/web/action.rb b/lib/diver_down/web/action.rb index 40dc84d..646cb60 100644 --- a/lib/diver_down/web/action.rb +++ b/lib/diver_down/web/action.rb @@ -39,6 +39,7 @@ def sources sources: source_names.sort.map do |source_name| { source_name:, + memo: @module_store.get_memo(source_name), modules: @module_store.get_modules(source_name).map do |module_name| { module_name: } end, @@ -108,6 +109,7 @@ def module(module_names) sources: source_names.sort.map do |source_name| { source_name:, + memo: @module_store.get_memo(source_name), } end, related_definitions: related_definitions.map do |definition| @@ -195,6 +197,7 @@ def combine_definitions(bit_id, compound, concentrate, only_module) sources: definition.sources.map do { source_name: _1.source_name, + memo: @module_store.get_memo(_1.source_name), modules: @module_store.get_modules(_1.source_name).map do |module_name| { module_name: } end, @@ -251,6 +254,7 @@ def source(source_name) json( source_name:, + memo: @module_store.get_memo(source_name), modules: module_names.map do { module_name: _1 } end, @@ -296,6 +300,27 @@ def set_modules(source_name, modules) end end + # POST /api/sources/:source_name/memo.json + # + # @param source_name [String] + # @param memo [String] + def set_memo(source_name, memo) + found_source = @store.any? do |_, definition| + definition.sources.any? do |source| + source.source_name == source_name + end + end + + if found_source + @module_store.set_memo(source_name, memo) + @module_store.flush + + json({}) + else + not_found + end + end + # @return [Array[Integer, Hash, Array]] def not_found [404, { 'content-type' => 'text/plain' }, ['not found']] diff --git a/lib/diver_down/web/definition_to_dot.rb b/lib/diver_down/web/definition_to_dot.rb index 081f6fa..95c1c07 100644 --- a/lib/diver_down/web/definition_to_dot.rb +++ b/lib/diver_down/web/definition_to_dot.rb @@ -41,6 +41,7 @@ def source_to_h id:, type: 'source', source_name: data.source_name, + memo: module_store.get_memo(data.source_name), modules:, } end From 101d17c1c5412b2c8a49ad7976ce03073f1daa4c Mon Sep 17 00:00:00 2001 From: alpaca-tc Date: Mon, 13 May 2024 19:13:19 +0900 Subject: [PATCH 4/4] Frontend supports source's memo --- frontend/components/ui/index.ts | 3 + frontend/constants/path.ts | 3 + frontend/models/combinedDefinition.ts | 9 +-- frontend/models/module.ts | 1 + frontend/models/source.ts | 7 +- .../components/DefinitionList/List.tsx | 7 +- .../DefinitionSources/DefinitionSources.tsx | 73 +++++++++++++++++-- .../MetadataDialog/MetadataDialog.tsx | 53 +++++++++++++- .../SourceMemoInput/SourceMemoInput.tsx | 45 ++++++++++++ .../components/SourceMemoInput/index.ts | 1 + .../SourceModulesComboBox.tsx | 39 +++++----- frontend/pages/Modules/Show.tsx | 6 ++ frontend/pages/Sources/List.tsx | 7 +- frontend/pages/Sources/Show.tsx | 9 +++ .../combinedDefinitionRepository.ts | 4 + frontend/repositories/moduleRepository.ts | 11 ++- frontend/repositories/sourceMemoRepository.ts | 21 ++++++ frontend/repositories/sourceRepository.ts | 6 +- spec/diver_down/web/definition_to_dot_spec.rb | 24 +++++- spec/diver_down/web_spec.rb | 70 ++++++++++++++++++ 20 files changed, 350 insertions(+), 49 deletions(-) create mode 100644 frontend/pages/DefinitionList/components/SourceMemoInput/SourceMemoInput.tsx create mode 100644 frontend/pages/DefinitionList/components/SourceMemoInput/index.ts create mode 100644 frontend/repositories/sourceMemoRepository.ts 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 ( + +
+ +