diff --git a/lxljs/string.js b/lxljs/string.js index b8608f04b8..0470f5bf01 100644 --- a/lxljs/string.js +++ b/lxljs/string.js @@ -159,7 +159,7 @@ export function isLibrisResourceUri(uri, settings) { const baseUri = settings.dataPath; let translatedUri = uri; - if (uri.startsWith('https://id.kb.se')) { + if (uri && uri.startsWith('https://id.kb.se')) { translatedUri = uri.replace('https://id.kb.se', settings.idPath); } @@ -308,3 +308,20 @@ export function getFormattedEntries(list, vocab, language, context) { remove(formatted, value => value === '' || value === null); // Remove empty strings return formatted; } + +export function getSigelLabel(sigel, len) { + if (!sigel.friendly_name) { + return sigel.code; + } + + const sigelPart = ` (${sigel.code})`; + const fName = sigel.friendly_name.length + sigelPart.length > len + ? `${sigel.friendly_name.substr(0, len - sigelPart.length - 3)}...` + : sigel.friendly_name; + + return `${fName}${sigelPart}`; +} + +export function getLibraryUri(sigel) { + return `https://libris.kb.se/library/${sigel}`; +} diff --git a/vue-client/src/components/inspector/reverse-relations.vue b/vue-client/src/components/inspector/reverse-relations.vue index 3a4150b24e..f2ba27bad7 100644 --- a/vue-client/src/components/inspector/reverse-relations.vue +++ b/vue-client/src/components/inspector/reverse-relations.vue @@ -188,7 +188,7 @@ export default { }, mounted() { this.$nextTick(() => { - if (this.$route.name === 'Search' || this.forceLoad) { + if (this.$route.name === 'Search' || this.$route.params.tool === 'changes' || this.forceLoad) { this.getRelationsInfo(); } }); diff --git a/vue-client/src/components/layout/search-bar.vue b/vue-client/src/components/layout/search-bar.vue index 652ddb633b..be62518803 100644 --- a/vue-client/src/components/layout/search-bar.vue +++ b/vue-client/src/components/layout/search-bar.vue @@ -60,7 +60,7 @@ export default { {{ versionInfo }} - + diff --git a/vue-client/src/components/mixins/facet-mixin.vue b/vue-client/src/components/mixins/facet-mixin.vue index ed81e64b0d..0d714ae084 100644 --- a/vue-client/src/components/mixins/facet-mixin.vue +++ b/vue-client/src/components/mixins/facet-mixin.vue @@ -84,6 +84,9 @@ export default { settings() { return this.$store.getters.settings; }, + changeCategories() { + return this.$store.getters.userChangeCategories; + }, }, }; diff --git a/vue-client/src/components/search/facet-controls.vue b/vue-client/src/components/search/facet-controls.vue index 11fcd24f49..3ee1d12cbb 100644 --- a/vue-client/src/components/search/facet-controls.vue +++ b/vue-client/src/components/search/facet-controls.vue @@ -5,6 +5,10 @@ export default { name: 'facet-controls', props: { result: {}, + isChangeView: { + type: Boolean, + default: false, + }, }, data() { return { @@ -50,6 +54,7 @@ export default { v-for="(dimensionValue, dimensionKey, index) in sortedFacets" :key="dimensionKey" :group="dimensionValue" + :is-change-view="isChangeView" :expanded="index < numOfExpanded"/> diff --git a/vue-client/src/components/search/facet-group.vue b/vue-client/src/components/search/facet-group.vue index a1bc98f753..d10c366664 100644 --- a/vue-client/src/components/search/facet-group.vue +++ b/vue-client/src/components/search/facet-group.vue @@ -4,6 +4,7 @@ import { mixin as clickaway } from 'vue-clickaway'; import * as DisplayUtil from 'lxljs/display'; import EncodingLevelIcon from '@/components/shared/encoding-level-icon'; import TypeIcon from '@/components/shared/type-icon'; +import CheckBox from '@/components/shared/check-box'; import FacetMixin from '@/components/mixins/facet-mixin'; import Facet from './facet.vue'; @@ -18,6 +19,10 @@ export default { expanded: { type: Boolean, }, + isChangeView: { + type: Boolean, + default: false, + }, }, data() { return { @@ -47,7 +52,7 @@ export default { this.$store.dispatch('setUser', userObj); }, featuredComparison(facet) { - if (this.group.dimension === '@reverse.itemOf.heldBy.@id') { + if (this.isCollectionGroup(this.group.dimension)) { // Featured code for '@reverse.itemOf.heldBy.@id' const userSigels = this.user.collections.map(item => item.code); if (facet.object.hasOwnProperty('sigel')) { @@ -61,6 +66,20 @@ export default { } return false; }, + defaultChecked(facet) { + if (this.isChangeFacetGroup) { + const id = facet.object['@id']; + return this.checkedCategoriesAndSigels.includes(id); + } + return false; + }, + checked(link, id) { + console.log('link', JSON.stringify(link)); + console.log('id', JSON.stringify(id)); + }, + isCollectionGroup(dim) { + return dim === '@reverse.itemOf.heldBy.@id' || dim === 'concerning.@reverse.itemOf.heldBy.@id'; + }, }, computed: { settings() { @@ -76,6 +95,7 @@ export default { const self = this; const list = this.group.observation.map((o) => { let label; + console.log('observation', JSON.stringify(o)); if (o.object.hasOwnProperty('@id')) { label = DisplayUtil.getItemLabel( o.object, @@ -96,6 +116,7 @@ export default { amount: o.totalItems, link: o.view['@id'], featured: self.featuredComparison(o), + isDefaultChecked: self.defaultChecked(o), }; }); return list; @@ -132,7 +153,7 @@ export default { }, featuredFacets() { let featured = this.facets.filter(o => o.featured === true); - if (this.group.dimension === '@reverse.itemOf.heldBy.@id') { + if (this.isCollectionGroup(this.group.dimension)) { const activeSigel = this.user.settings.activeSigel; featured = sortBy(featured, o => o.object.sigel !== activeSigel && o.label !== activeSigel && o.label !== `library/${activeSigel}`); } @@ -161,11 +182,18 @@ export default { hasScroll() { return !this.revealLevels[this.currentLevel] && this.isExpanded; }, + isChangeFacetGroup() { + return (this.group.dimension === 'category.@id' || this.group.dimension === 'concerning.@reverse.itemOf.heldBy.@id') && this.isChangeView; + }, + checkedCategoriesAndSigels() { + return [...this.changeCategories.map(c => c.heldBy), ...this.changeCategories.find(c => c.hasOwnProperty('triggers')).triggers]; + }, }, components: { Facet, EncodingLevelIcon, TypeIcon, + CheckBox, }, }; @@ -209,7 +237,13 @@ export default { :class="{'is-expanded' : isExpanded, 'has-scroll' : hasScroll}"> + :key="'featured_'+facetItem.link" + :is-link="!isChangeFacetGroup"> + + :key="facetItem.link" + :is-link="!isChangeFacetGroup"> +
  • + diff --git a/vue-client/src/components/search/result-list-item.vue b/vue-client/src/components/search/result-list-item.vue index c45e301736..07b11b419a 100644 --- a/vue-client/src/components/search/result-list-item.vue +++ b/vue-client/src/components/search/result-list-item.vue @@ -126,6 +126,7 @@ export default {
    +
    diff --git a/vue-client/src/components/search/search-form.vue b/vue-client/src/components/search/search-form.vue index c1e0dd91e2..2b5047b42d 100644 --- a/vue-client/src/components/search/search-form.vue +++ b/vue-client/src/components/search/search-form.vue @@ -14,6 +14,10 @@ export default { default: 'libris', type: String, }, + searchTool: { + default: '', + type: String, + }, }, data() { return { @@ -51,13 +55,18 @@ export default { this.helpToggled = !this.helpToggled; }, composeQuery() { - return buildQueryString(this.searchPerimeter === 'libris' + return buildQueryString(this.searchPerimeter === 'libris' || this.searchTool === 'changes' ? this.mergedParams : { q: this.searchPhrase, databases: this.status.remoteDatabases.join() }); }, doSearch() { this.helpToggled = false; - const path = `/search/${this.searchPerimeter}?${this.composeQuery()}`; + let path = ''; + if (this.searchPerimeter === 'libris' || this.searchPerimeter === 'remote') { + path = `/search/${this.searchPerimeter}?${this.composeQuery()}`; + } else if (this.searchTool === 'changes') { + path = `${this.$route.path}?${this.composeQuery()}`; + } this.$router.push({ path }); }, clearInputs() { @@ -98,7 +107,7 @@ export default { return PropertyMappings[0]; }, setType() { - if (this.$route.params.perimeter === 'remote') { + if (this.$route.params.perimeter === 'remote' || this.searchTool === 'changes') { return this.activeSearchType; // Don't change while remote searching } const performedQuery = cloneDeep(this.$route.query); @@ -183,7 +192,13 @@ export default { return this.searchPhrase.length > 0; }, inputPlaceholder() { - return this.searchPerimeter === 'remote' ? 'ISBN eller valfria sökord' : 'Search'; + if (this.searchPerimeter === 'remote') { + return 'ISBN eller valfria sökord'; + } + if (this.searchTool === 'changes') { + return 'Sök bland ändringar'; // TODO: i18n + } + return 'Search'; }, composedSearchParam() { // pair current search param with searchphrase let composed = {}; @@ -197,7 +212,15 @@ export default { return composed; }, composedTypes() { - return this.activeSearchType && this.activeSearchType.length > 0 ? { '@type': this.activeSearchType } : { '@type': null }; + let type = null; + if (this.activeSearchType && this.activeSearchType.length > 0) { + if (this.searchPerimeter === 'libris') { + type = this.activeSearchType; + } else if (this.searchTool === 'changes') { + type = 'AdministrativeNotice'; + } + } + return { '@type': type }; }, prefSort() { if (this.user && this.user.settings.sort) { @@ -238,6 +261,12 @@ export default { this.resetSearchParam(); } }, + searchTool(newVal, oldVal) { + if (newVal !== oldVal && newVal === 'changes') { + this.clearInputs(); + this.doSearch(); + } + }, searchPerimeter(newVal, oldVal) { if (newVal !== oldVal) { this.$nextTick(() => { @@ -337,7 +366,7 @@ export default { + :class="{ 'in-remote': searchPerimeter === 'remote' || searchTool === 'changes' }" tabindex="0" v-show="hasInput" @keyup.enter="clearInputs()" @click="clearInputs()">
  • +
    • +import { mapGetters } from 'vuex'; +import * as StringUtil from 'lxljs/string'; +import LensMixin from '@/components/mixins/lens-mixin'; + +export default { + name: 'change-categories', + mixins: [LensMixin], + data() { + return { + categoriesIsExpanded: false, + sigelIsExpanded: false, + }; + }, + props: { + sigel: Object, + availableCategories: [], + availableSigels: [], + }, + methods: { + updateSigel(e, sigel) { + this.$store.dispatch('updateSubscribedSigel', { libraryId: this.sigelUri(sigel), checked: e.target.checked }); + }, + updateCategory(e, categoryId) { + this.$store.dispatch('updateSubscribedChangeCategory', { categoryId: categoryId, checked: e.target.checked }); + }, + toggleSigelExpanded() { + this.sigelIsExpanded = !this.sigelIsExpanded; + }, + toggleCategoriesExpanded() { + this.categoriesIsExpanded = !this.categoriesIsExpanded; + }, + isActiveCategory(categoryId) { + const obj = this.userChangeCategories[0]; + return obj ? obj.triggers.includes(categoryId) : false; + }, + isActiveSigel(sigel) { + console.log('this.userChangeCategories', JSON.stringify(this.userChangeCategories)); + const obj = this.userChangeCategories.find(c => c.heldBy === this.sigelUri(sigel)); + return !!obj; + }, + label(obj) { + return this.getLabel(obj); + }, + sigelLabel(sigel) { + return StringUtil.getSigelLabel(sigel); + }, + sigelUri(sigel) { + return StringUtil.getLibraryUri(sigel.code); + }, + }, + computed: { + ...mapGetters([ + 'userChangeCategories', + ]), + }, + mounted() { + }, +}; + + + + + diff --git a/vue-client/src/components/usersettings/select-sigel.vue b/vue-client/src/components/usersettings/select-sigel.vue index 46a7d14514..0b5a59f360 100644 --- a/vue-client/src/components/usersettings/select-sigel.vue +++ b/vue-client/src/components/usersettings/select-sigel.vue @@ -1,5 +1,6 @@ + + + + diff --git a/vue-client/src/views/DirectoryCare.vue b/vue-client/src/views/DirectoryCare.vue index 7c8014633e..2836e4bebc 100644 --- a/vue-client/src/views/DirectoryCare.vue +++ b/vue-client/src/views/DirectoryCare.vue @@ -7,10 +7,12 @@ import * as HttpUtil from '@/utils/http'; import TabMenu from '@/components/shared/tab-menu'; import HoldingMover from '@/components/care/holding-mover'; import ModalComponent from '@/components/shared/modal-component'; +import ChangeNotes from './ChangeNotes.vue'; export default { name: 'DirectoryCare', components: { + ChangeNotes, 'tab-menu': TabMenu, 'holding-mover': HoldingMover, 'modal-component': ModalComponent, @@ -23,11 +25,6 @@ export default { removed: [], other: [], }, - tabs: [ - { id: 'holdings', text: 'Move holdings' }, - // { 'id': 'merge', 'text': 'Merge records' }, - // { 'id': 'remove', 'text': 'Batch remove' }, - ], showModal: false, }; }, @@ -41,6 +38,17 @@ export default { flaggedInstances() { return filter(this.fetchedItems, o => VocabUtil.getRecordType(o['@type'], this.resources.vocab, this.resources.context) === 'Instance'); }, + tabs() { + return [ + { id: 'holdings', text: 'Move holdings' }, + { + id: 'changes', + text: StringUtil.getUiPhraseByLang('Changes', this.user.settings.language, this.resources.i18n), + }, + // { 'id': 'merge', 'text': 'Merge records' }, + // { 'id': 'remove', 'text': 'Batch remove' }, + ]; + }, }, watch: { userFlagged(newValue, oldValue) { @@ -129,7 +137,8 @@ export default {
      - +
      diff --git a/vue-client/src/views/Find.vue b/vue-client/src/views/Find.vue index 0bc148da16..488165bdb6 100644 --- a/vue-client/src/views/Find.vue +++ b/vue-client/src/views/Find.vue @@ -51,8 +51,12 @@ export default { this.emptyResults(); if (typeof this.query !== 'undefined') { this.searchInProgress = true; + console.log('this.$route.params.perimeter', this.$route.params.perimeter); if (this.$route.params.perimeter === 'libris') { this.getLocalResult(); + } else if (this.$route.params.tool === 'changes') { + console.log('getChangesResult'); + this.getChangesResult(); } else { this.getRemoteResult(); } @@ -111,6 +115,9 @@ export default { this.searchInProgress = false; }); }, + getChangesResult() { + this.getLocalResult(); + }, convertRemoteResult(result) { let totalResults = 0; @@ -191,10 +198,12 @@ export default { }, mounted() { this.$nextTick(() => { - if (this.$route.params.perimeter !== 'libris' && this.$route.params.perimeter !== 'remote') { + console.log('route name:', this.$route.name); + console.log('route params:', this.$route.params); + if (this.$route.params.perimeter !== 'libris' && this.$route.params.perimeter !== 'remote' && this.$route.params.tool !== 'changes') { this.$router.push({ path: '/search/' }); } - if (!this.user.isLoggedIn && this.$route.params.perimeter === 'remote') { + if (!this.user.isLoggedIn && this.$route.params.perimeter === 'remote' && this.$route.params.tool === 'changes') { this.$router.push({ path: '/search/' }); } this.query = this.$route.fullPath.split('?')[1]; @@ -234,13 +243,17 @@ export default {