diff --git a/app/controllers/api/places_controller.rb b/app/controllers/api/places_controller.rb
index 35016d7..5048b71 100644
--- a/app/controllers/api/places_controller.rb
+++ b/app/controllers/api/places_controller.rb
@@ -1,3 +1,7 @@
class Api::PlacesController < Api::BaseController
+ # Search attributes
search_attributes :name, :city, :state, :country
+
+ # Preloads
+ preloads :qualifications, only: :show
end
diff --git a/app/controllers/api/value_lists_controller.rb b/app/controllers/api/value_lists_controller.rb
index a9f0bb4..abdb859 100644
--- a/app/controllers/api/value_lists_controller.rb
+++ b/app/controllers/api/value_lists_controller.rb
@@ -2,6 +2,9 @@ class Api::ValueListsController < Api::BaseController
# Search columns
search_attributes :object, :group, :human_name, :comment
+ # Preloads
+ preloads :qualifications, only: [:index, :show]
+
def objects_list
objects_list = ValueList
.all
@@ -26,18 +29,6 @@ def groups_list
}
end
- def authorized_vocabularies
- vocabs_list = ValueList
- .where.not(authorized_vocabulary: nil)
- .order(:authorized_vocabulary)
- .distinct
- .pluck(:authorized_vocabulary)
-
- render json: {
- authorized_vocabularies: vocabs_list
- }
- end
-
protected
def apply_filters(query)
diff --git a/app/models/place.rb b/app/models/place.rb
index 37dc510..04bacb0 100644
--- a/app/models/place.rb
+++ b/app/models/place.rb
@@ -1,5 +1,6 @@
class Place < ApplicationRecord
# Includes
+ include Qualifiable
include Recordable
include Search::Place
@@ -11,6 +12,7 @@ class Place < ApplicationRecord
# Resourceable parameters
allow_params :name, :place_type, :lat, :long, :city, :state, :country, :url, :database_value, :notes, :same_as,
- :part_of, locations_attributes: [:id, :locateable_id, :locateable_type, :description, :certainty,
- :notes, :_destroy, qualifications_attributes: [:id, :value_list_id, :notes, :persistent, :_destroy]]
+ :part_of, :authorized_vocabulary_url,
+ locations_attributes: [:id, :locateable_id, :locateable_type, :description, :certainty, :notes, :_destroy,
+ qualifications_attributes: [:id, :value_list_id, :notes, :persistent, :_destroy]]
end
diff --git a/app/models/value_list.rb b/app/models/value_list.rb
index 419403d..1965f3e 100644
--- a/app/models/value_list.rb
+++ b/app/models/value_list.rb
@@ -1,10 +1,8 @@
class ValueList < ApplicationRecord
# Includes
+ include Qualifiable
include Recordable
- # Relationships
- has_many :qualifications
-
# Resource params
allow_params :authorized_vocabulary, :comment, :object, :group, :human_name, :authorized_vocabulary_url, :database_value
diff --git a/app/serializers/people_serializer.rb b/app/serializers/people_serializer.rb
index b049260..da70973 100644
--- a/app/serializers/people_serializer.rb
+++ b/app/serializers/people_serializer.rb
@@ -3,9 +3,8 @@ class PeopleSerializer < BaseSerializer
include LocateableSerializer
index_attributes :id, :name, :display_name, :person_type, qualifications: QualificationsSerializer
- show_attributes :id, :name, :display_name, :person_type, :authorized_vocabulary, :url, :database_value,
- :comment, :part_of, :same_as, :artist_birth_date, :artist_death_date, :years_active,
- qualifications: QualificationsSerializer
+ show_attributes :id, :name, :display_name, :person_type, :url, :database_value, :comment, :part_of, :same_as,
+ :artist_birth_date, :artist_death_date, :years_active, qualifications: QualificationsSerializer
# For unauthenticated users, only display participations for artworks that are published
show_attributes(:participations) do |person, current_user|
diff --git a/app/serializers/places_serializer.rb b/app/serializers/places_serializer.rb
index 036f6cb..d114eb7 100644
--- a/app/serializers/places_serializer.rb
+++ b/app/serializers/places_serializer.rb
@@ -1,6 +1,7 @@
class PlacesSerializer < BaseSerializer
index_attributes :id, :name, :place_type, :lat, :long, :city, :state, :country
- show_attributes :id, :name, :place_type, :lat, :long, :city, :state, :country, :url, :database_value, :notes, :same_as, :part_of
+ show_attributes :id, :name, :place_type, :lat, :long, :city, :state, :country, :url, :database_value, :notes,
+ :same_as, :part_of, :authorized_vocabulary_url, qualifications: QualificationsSerializer
# For unauthenticated users, only display locations for artworks that are published
show_attributes(:locations) do |place, current_user|
diff --git a/app/serializers/value_lists_serializer.rb b/app/serializers/value_lists_serializer.rb
index 1711588..513a2d9 100644
--- a/app/serializers/value_lists_serializer.rb
+++ b/app/serializers/value_lists_serializer.rb
@@ -1,7 +1,7 @@
class ValueListsSerializer < BaseSerializer
- index_attributes :id, :object, :group, :human_name, :authorized_vocabulary, :authorized_vocabulary_url,
- :database_value, :comment, :qualifications_count
+ index_attributes :id, :object, :group, :human_name, :authorized_vocabulary_url, :database_value, :comment,
+ :qualifications_count, qualifications: QualificationsSerializer
- show_attributes :id, :object, :group, :human_name, :authorized_vocabulary, :authorized_vocabulary_url,
- :database_value, :comment, :qualifications_count
+ show_attributes :id, :object, :group, :human_name, :authorized_vocabulary_url, :database_value, :comment,
+ :qualifications_count, qualifications: QualificationsSerializer
end
diff --git a/client/src/components/PersonForm.js b/client/src/components/PersonForm.js
index 077602b..ca319bd 100644
--- a/client/src/components/PersonForm.js
+++ b/client/src/components/PersonForm.js
@@ -63,12 +63,11 @@ const PersonForm = (props: Props) => (
required={props.isRequired('years_active')}
value={props.item.years_active || ''}
/>
-
{
onChange={props.onTextInputChange.bind(this, 'url')}
value={props.item.url || ''}
/>
+
+
{
- const [authorizedVocabulariesList, setAuthorizedVocabulariesList] = useState([]);
-
- useEffect(() => {
- ValueLists.getAuthorizedVocabulariesList().then(({ data }) => {
- setAuthorizedVocabulariesList(data.authorized_vocabularies.map((vocab) => ({
- key: vocab,
- value: vocab,
- text: vocab
- })));
- });
- }, []);
-
- return (
-
- (
+
+
+
+ 0}
+ error={props.isError('object')}
+ label={props.t('ValueList.labels.objectName')}
+ onChange={props.onTextInputChange.bind(this, 'object')}
+ required={props.isRequired('object')}
+ value={props.item.object || ''}
/>
-
- 0}
- error={props.isError('object')}
- label={props.t('ValueList.labels.objectName')}
- onChange={props.onTextInputChange.bind(this, 'object')}
- required={props.isRequired('object')}
- value={props.item.object || ''}
- />
- 0}
- error={props.isError('group')}
- label={props.t('ValueList.labels.groupName')}
- onChange={props.onTextInputChange.bind(this, 'group')}
- required={props.isRequired('group')}
- value={props.item.group || ''}
- />
-
-
-
-
-
-
-
-
- { props.children }
-
- );
-};
+ 0}
+ error={props.isError('group')}
+ label={props.t('ValueList.labels.groupName')}
+ onChange={props.onTextInputChange.bind(this, 'group')}
+ required={props.isRequired('group')}
+ value={props.item.group || ''}
+ />
+
+
+
+
+
+
+ { props.children }
+
+);
export default withTranslation()(ValueListModal);
diff --git a/client/src/i18n/en.json b/client/src/i18n/en.json
index 89e0bdd..87c2909 100644
--- a/client/src/i18n/en.json
+++ b/client/src/i18n/en.json
@@ -396,7 +396,9 @@
"notes": "Notes",
"state": "State",
"type": "Type",
- "url": "Authorized Vocabulary URL"
+ "url": "Institutional URL",
+ "authorizedVocabulary": "Authorized vocabulary",
+ "authorizedVocabularyUrl": "Authorized vocabulary URL"
},
"locations": {
"columns": {
diff --git a/client/src/pages/admin/ValueLists.css b/client/src/pages/admin/ValueLists.css
index e69de29..7bd0619 100644
--- a/client/src/pages/admin/ValueLists.css
+++ b/client/src/pages/admin/ValueLists.css
@@ -0,0 +1,4 @@
+.value-lists > .tab > .ui.menu {
+ overflow: auto;
+ padding-bottom: 0.25em;
+}
\ No newline at end of file
diff --git a/client/src/pages/admin/ValueLists.js b/client/src/pages/admin/ValueLists.js
index 8362902..39fbddc 100644
--- a/client/src/pages/admin/ValueLists.js
+++ b/client/src/pages/admin/ValueLists.js
@@ -1,15 +1,13 @@
// @flow
import type { EditContainerProps } from '@performant-software/shared-components/types';
-import React, { useEffect, useState } from 'react';
+import React, { useCallback, useEffect, useMemo, useState } from 'react';
+import { Container, Header, Tab } from 'semantic-ui-react';
+import _ from 'underscore';
import ValueListsTable from '../../components/ValueListsTable';
import withMenuBar from '../../hooks/MenuBar';
import ValueListsService from '../../services/ValueLists';
-import {
- Container,
- Header,
- Tab
-} from 'semantic-ui-react';
+import './ValueLists.css';
import type { ValueList as ValueListType } from '../../types/ValueList';
import type { Translateable } from '../../types/Translateable';
@@ -21,24 +19,49 @@ type Props = EditContainerProps & Translateable & {
const ValueLists = (props: Props) => {
const [objectsList, setObjectsList] = useState([]);
- useEffect(() => {
- ValueListsService.getObjectsList()
- .then((response) => setObjectsList(response.data.objects));
- }, []);
-
- const panes = objectsList.map((objectName) => ({
+ /**
+ * Memo-izes the tab panes.
+ *
+ * @type {{menuItem: *, render: function(): *}[]}
+ */
+ const panes = useMemo(() => objectsList.map((objectName) => ({
menuItem: objectName,
render: () => (
-
-
+
+
)
- }));
+ })), [objectsList]);
+
+ /**
+ * Sets the tab list on the state.
+ */
+ useEffect(() => {
+ ValueListsService
+ .getObjectsList()
+ .then((response) => setObjectsList(response.data.objects))
+ }, []);
return (
-
- {props.t('Admin.menu.valueLists')}
-
+
+
+ { props.t('Admin.menu.valueLists') }
+
+
);
};
diff --git a/client/src/transforms/Place.js b/client/src/transforms/Place.js
index 83d1c92..4974411 100644
--- a/client/src/transforms/Place.js
+++ b/client/src/transforms/Place.js
@@ -3,6 +3,7 @@
import _ from 'underscore';
import BaseTransform from './BaseTransform';
import Locations from './Locations';
+import Qualifications from './Qualifications';
import type { Place as PlaceType } from '../types/Place';
@@ -28,7 +29,8 @@ class Place extends BaseTransform {
'database_value',
'notes',
'same_as',
- 'part_of'
+ 'part_of',
+ 'authorized_vocabulary_url'
];
}
@@ -59,7 +61,8 @@ class Place extends BaseTransform {
return {
place: {
..._.pick(place, this.getPayloadKeys()),
- ...Locations.toPayload(place)
+ ...Locations.toPayload(place),
+ ...Qualifications.toPayload(place)
}
};
}
diff --git a/client/src/transforms/ValueList.js b/client/src/transforms/ValueList.js
index ea0cb66..1e456bb 100644
--- a/client/src/transforms/ValueList.js
+++ b/client/src/transforms/ValueList.js
@@ -2,6 +2,7 @@
import _ from 'underscore';
import BaseTransform from './BaseTransform';
+import Qualifications from './Qualifications';
import type { ValueList as ValueListType } from '../types/ValueList';
@@ -52,12 +53,16 @@ class ValueList extends BaseTransform {
/**
* Returns the value_list object to be sent to the server on POST/PUT requests.
*
- * @param option
+ * @param valueList
*
* @returns {*}
*/
- toPayload(option: ValueListType) {
- return { value_list: _.pick(option, this.PAYLOAD_KEYS) };
+ toPayload(valueList: ValueListType) {
+ return {
+ value_list: {
+ ..._.pick(valueList, this.PAYLOAD_KEYS),
+ ...Qualifications.toPayload(valueList)
+ }};
}
}
diff --git a/client/src/types/Place.js b/client/src/types/Place.js
index 9c1c236..8d5295b 100644
--- a/client/src/types/Place.js
+++ b/client/src/types/Place.js
@@ -14,6 +14,7 @@ export type Place = {
notes: string,
same_as: number,
part_of: number,
+ authorized_vocabulary_url,
locations: Array
};
diff --git a/config/routes.rb b/config/routes.rb
index 504e13f..430f919 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -19,7 +19,6 @@
resources :visual_contexts, except: :index
resources :users
- get 'authorized_vocabularies' => 'value_lists#authorized_vocabularies'
get 'value_lists_objects', to: 'value_lists#objects_list'
get 'value_lists_groups', to: 'value_lists#groups_list'
diff --git a/db/data/20241211163139_create_authorized_vocabulary_value_lists.rb b/db/data/20241211163139_create_authorized_vocabulary_value_lists.rb
new file mode 100644
index 0000000..bce9f89
--- /dev/null
+++ b/db/data/20241211163139_create_authorized_vocabulary_value_lists.rb
@@ -0,0 +1,41 @@
+# frozen_string_literal: true
+
+class CreateAuthorizedVocabularyValueLists < ActiveRecord::Migration[7.0]
+ def up
+ execute <<-SQL.squish
+ INSERT INTO value_lists (object, "group", human_name, created_at, updated_at)
+ SELECT DISTINCT 'General', 'Authorized Vocabulary', authorized_vocabulary, current_timestamp, current_timestamp
+ FROM value_lists
+ WHERE authorized_vocabulary IS NOT NULL
+ UNION
+ SELECT DISTINCT 'General', 'Authorized Vocabulary', authorized_vocabulary, current_timestamp, current_timestamp
+ FROM people
+ WHERE authorized_vocabulary IS NOT NULL
+ ORDER BY authorized_vocabulary
+ SQL
+
+ execute <<-SQL.squish
+ INSERT INTO qualifications (qualifiable_id, qualifiable_type, value_list_id)
+ SELECT value_lists.id, 'ValueList', v.id
+ FROM value_lists
+ JOIN value_lists v ON v.human_name = value_lists.authorized_vocabulary
+ AND v.object = 'General'
+ AND v.group = 'Authorized Vocabulary'
+ WHERE value_lists.authorized_vocabulary IS NOT NULL
+ SQL
+
+ execute <<-SQL.squish
+ INSERT INTO qualifications (qualifiable_id, qualifiable_type, value_list_id)
+ SELECT people.id, 'Person', value_lists.id
+ FROM people
+ JOIN value_lists ON value_lists.human_name = people.authorized_vocabulary
+ AND value_lists.object = 'General'
+ AND value_lists.group = 'Authorized Vocabulary'
+ WHERE people.authorized_vocabulary IS NOT NULL
+ SQL
+ end
+
+ def down
+ raise ActiveRecord::IrreversibleMigration
+ end
+end
diff --git a/db/data/20241211173335_reset_value_list_qualifications_count.rb b/db/data/20241211173335_reset_value_list_qualifications_count.rb
new file mode 100644
index 0000000..193409d
--- /dev/null
+++ b/db/data/20241211173335_reset_value_list_qualifications_count.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+class ResetValueListQualificationsCount < ActiveRecord::Migration[7.0]
+ def up
+ execute <<-SQL.squish
+ WITH qualification_counts AS (
+ SELECT qualifications.value_list_id, COUNT(*) AS qualifications_count
+ FROM qualifications
+ GROUP BY qualifications.value_list_id
+ )
+ UPDATE value_lists
+ SET qualifications_count = qualification_counts.qualifications_count
+ FROM qualification_counts
+ WHERE qualification_counts.value_list_id = value_lists.id
+ SQL
+ end
+
+ def down
+ raise ActiveRecord::IrreversibleMigration
+ end
+end
diff --git a/db/data_schema.rb b/db/data_schema.rb
index 32c8bb4..b821825 100644
--- a/db/data_schema.rb
+++ b/db/data_schema.rb
@@ -1 +1 @@
-DataMigrate::Data.define(version: 20241127155125)
+DataMigrate::Data.define(version: 20241211173335)
diff --git a/db/migrate/20241211200500_remove_value_lists_authorized_vocabulary.rb b/db/migrate/20241211200500_remove_value_lists_authorized_vocabulary.rb
new file mode 100644
index 0000000..7874624
--- /dev/null
+++ b/db/migrate/20241211200500_remove_value_lists_authorized_vocabulary.rb
@@ -0,0 +1,5 @@
+class RemoveValueListsAuthorizedVocabulary < ActiveRecord::Migration[7.0]
+ def change
+ remove_column :value_lists, :authorized_vocabulary
+ end
+end
diff --git a/db/migrate/20241211201922_remove_people_authorized_vocabulary.rb b/db/migrate/20241211201922_remove_people_authorized_vocabulary.rb
new file mode 100644
index 0000000..3813bba
--- /dev/null
+++ b/db/migrate/20241211201922_remove_people_authorized_vocabulary.rb
@@ -0,0 +1,5 @@
+class RemovePeopleAuthorizedVocabulary < ActiveRecord::Migration[7.0]
+ def change
+ remove_column :people, :authorized_vocabulary
+ end
+end
diff --git a/db/migrate/20241211202543_add_authorized_vocabulary_url_to_places.rb b/db/migrate/20241211202543_add_authorized_vocabulary_url_to_places.rb
new file mode 100644
index 0000000..83c1684
--- /dev/null
+++ b/db/migrate/20241211202543_add_authorized_vocabulary_url_to_places.rb
@@ -0,0 +1,5 @@
+class AddAuthorizedVocabularyUrlToPlaces < ActiveRecord::Migration[7.0]
+ def change
+ add_column :places, :authorized_vocabulary_url, :string
+ end
+end
diff --git a/db/schema.rb b/db/schema.rb
index 560dad0..a37b980 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema[7.0].define(version: 2024_12_03_181033) do
+ActiveRecord::Schema[7.0].define(version: 2024_12_11_202543) do
# These are extensions that must be enabled in order to support this database
enable_extension "pg_stat_statements"
enable_extension "plpgsql"
@@ -180,7 +180,6 @@
t.string "name"
t.string "display_name"
t.string "person_type"
- t.string "authorized_vocabulary"
t.string "url"
t.string "database_value"
t.string "comment"
@@ -236,6 +235,7 @@
t.datetime "updated_at", null: false
t.bigint "created_by_id"
t.bigint "updated_by_id"
+ t.string "authorized_vocabulary_url"
t.index ["created_by_id"], name: "index_places_on_created_by_id"
t.index ["updated_by_id"], name: "index_places_on_updated_by_id"
end
@@ -288,7 +288,6 @@
t.string "human_name"
t.string "authorized_vocabulary_url"
t.text "comment"
- t.string "authorized_vocabulary"
t.string "airtable_id"
t.datetime "airtable_timestamp", precision: nil
t.datetime "created_at", null: false