Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision
  • 2.11.0-privacyPolicy
  • APIv2
  • Docs/Setup
  • Experiment/fix-debugging
  • Experimental/Heinrichs-cypress
  • Feature/xxxx-turnOffDataPub
  • Fix/xxxx-ToS400Error
  • Fix/xxxx-migrateLogin
  • Fix/xxxx-tokenUploadButton
  • Hotfix/0038-correctDownload
  • Hotfix/1917-PublicFilesVisibility
  • Hotfix/1963-fixOrganizationField
  • Hotfix/2015-PublicFilesVisibility
  • Hotfix/2130-uiv2ContactChange
  • Hotfix/2144-invitationCall
  • Hotfix/2150-fixUpload
  • Hotfix/2160-userOrgsInst
  • Hotfix/2190-requiredFieldsForUserProfile
  • Hotfix/2196-RCVTableTranslation
  • Hotfix/2212-fixFiles
  • Hotfix/2226-userProfileSaveButton
  • Hotfix/2232-dependencyFix
  • Hotfix/2233-fixMe
  • Hotfix/2258-saveButtonWorksAsExpected
  • Hotfix/2296-selectedValuesNotReturned
  • Hotfix/2308-defaultLicense
  • Hotfix/2335-fixingSearchRCV
  • Hotfix/2353-dropShape
  • Hotfix/2370-fixDeleteButton
  • Hotfix/2378-linkedFix
  • Hotfix/2379-filesDragAndDrop
  • Hotfix/2382-guestStillBuggy
  • Hotfix/2384-guestsAndLinked
  • Hotfix/2427-adminTrouble
  • Hotfix/2459-EncodingPath
  • Hotfix/2465-orcidLink
  • Hotfix/2465-orcidLink-v1.25.1
  • Hotfix/2504-formGen
  • Hotfix/2541-resCreate
  • Hotfix/2601-correctMetadataIdentity
  • Hotfix/2611-feedback
  • Hotfix/2618-turtle
  • Hotfix/2681-validationErrors
  • Hotfix/2684-correctEncoding
  • Hotfix/2684-fixSubMetadata
  • Hotfix/2713-validateEntryName
  • Hotfix/2734-allowEmptyLicense
  • Hotfix/2765-encodingAgain
  • Hotfix/2852-adaptTextForToSUi
  • Hotfix/2853-optimizationV4
  • Hotfix/2943-reloadingResources
  • Hotfix/2943-searchHighlighting
  • Hotfix/2957-styleAndUpgrade
  • Hotfix/2971-fixTextInDataPub
  • Hotfix/2989-cookieLength
  • Hotfix/662-keepSidebarExpanded
  • Hotfix/xxxx-correctLinking
  • Hotfix/xxxx-folderRecursive
  • Hotfix/xxxx-fullscreenCss
  • Hotfix/xxxx-homepageDisplay
  • Hotfix/xxxx-liveReleaseFixes
  • Hotfix/xxxx-partnerProjects
  • Hotfix/xxxx-workingFileIndex
  • Issue/1782-structualDataIntegration
  • Issue/1792-newMetadataStructure
  • Issue/1822-coscineUIv2App
  • Issue/1824-componentsUIv2
  • Issue/1824-routerAdditions
  • Issue/1825-codeQualityPipelines
  • Issue/1833-newLogin
  • Issue/1843-multipleFilesValidation
  • Issue/1860-searchScoping
  • Issue/1861-searchMetadata
  • Issue/1862-searchFacets
  • Issue/1863-paginationForSearch
  • Issue/1926-userProfile
  • Issue/1927-projectAppMigration
  • Issue/1928-sidebarmenuAddition
  • Issue/1929-vuexToPinia
  • Issue/1938-internalHandling
  • Issue/1951-quotaImplementation
  • Issue/1953-owlImports
  • Issue/1957-resourceAppMigration
  • Issue/1957-resourceAppMigrationNew
  • Issue/1962-SearchAppUI2
  • Issue/1964-tokenExpiryUIv2
  • Issue/1965-userListMigration
  • Issue/1970-breadcrumbs
  • Issue/1971-projectEditCreateMigration
  • Issue/1972-homeDepot
  • Issue/1974-shibbolethLogout
  • Issue/1976-resouceCreationVaildEmail
  • Issue/1979-supportAdminUIv2Migration
  • Issue/1980-userManagement
  • Issue/1985-adaptSidebar
  • Issue/2002-migrateResourceCreate
  • Issue/2003-resourceSettings
  • Issue/2008-quotaManagement
  • Issue/2011-pathConfig
  • Issue/2016-BannerMigration
  • 1.28.0-pilot
  • v1.0.0
  • v1.1.0
  • v1.10.0
  • v1.10.1
  • v1.10.2
  • v1.10.3
  • v1.11.0
  • v1.11.1
  • v1.11.2
  • v1.11.3
  • v1.11.4
  • v1.11.5
  • v1.11.6
  • v1.11.7
  • v1.12.0
  • v1.13.0
  • v1.14.0
  • v1.14.1
  • v1.14.2
  • v1.14.3
  • v1.15.0
  • v1.15.1
  • v1.16.0
  • v1.16.1
  • v1.16.2
  • v1.16.3
  • v1.17.0
  • v1.17.1
  • v1.17.2
  • v1.18.0
  • v1.18.1
  • v1.19.0
  • v1.2.0
  • v1.20.0
  • v1.20.1
  • v1.20.2
  • v1.20.3
  • v1.20.4
  • v1.20.5
  • v1.21.0
  • v1.22.0
  • v1.22.1
  • v1.22.2
  • v1.23.0
  • v1.23.1
  • v1.23.2
  • v1.23.3
  • v1.23.4
  • v1.23.5
  • v1.23.6
  • v1.23.6-patch-2417-2427
  • v1.24.0
  • v1.24.1
  • v1.25.0
  • v1.25.1
  • v1.26.0
  • v1.26.1
  • v1.27.0
  • v1.27.1
  • v1.27.1-pilot
  • v1.28.0
  • v1.29.0
  • v1.29.1
  • v1.29.2
  • v1.3.0
  • v1.30.0
  • v1.30.1
  • v1.30.2
  • v1.31.0
  • v1.32.0
  • v1.4.0
  • v1.4.1
  • v1.5.0
  • v1.6.0
  • v1.6.1
  • v1.6.2
  • v1.7.0
  • v1.8.0
  • v1.8.1
  • v1.8.2
  • v1.9.0
  • v2.0.0
  • v2.1.0
  • v2.10.0
  • v2.10.1
  • v2.11.0
  • v2.12.0
  • v2.12.1
  • v2.12.2
  • v2.12.3
  • v2.12.4
  • v2.12.5
  • v2.13.0
  • v2.13.1
  • v2.13.2
  • v2.13.3
  • v2.13.4
  • v2.14.0
  • v2.15.0
200 results

Target

Select target project
  • coscine/frontend/apps/ui
1 result
Select Git revision
  • 2.11.0-privacyPolicy
  • APIv2
  • Docs/Setup
  • Experiment/fix-debugging
  • Experimental/Heinrichs-cypress
  • Feature/xxxx-turnOffDataPub
  • Fix/xxxx-ToS400Error
  • Fix/xxxx-migrateLogin
  • Fix/xxxx-tokenUploadButton
  • Hotfix/0038-correctDownload
  • Hotfix/1917-PublicFilesVisibility
  • Hotfix/1963-fixOrganizationField
  • Hotfix/2015-PublicFilesVisibility
  • Hotfix/2130-uiv2ContactChange
  • Hotfix/2144-invitationCall
  • Hotfix/2150-fixUpload
  • Hotfix/2160-userOrgsInst
  • Hotfix/2190-requiredFieldsForUserProfile
  • Hotfix/2196-RCVTableTranslation
  • Hotfix/2212-fixFiles
  • Hotfix/2226-userProfileSaveButton
  • Hotfix/2232-dependencyFix
  • Hotfix/2233-fixMe
  • Hotfix/2258-saveButtonWorksAsExpected
  • Hotfix/2296-selectedValuesNotReturned
  • Hotfix/2308-defaultLicense
  • Hotfix/2335-fixingSearchRCV
  • Hotfix/2353-dropShape
  • Hotfix/2370-fixDeleteButton
  • Hotfix/2378-linkedFix
  • Hotfix/2379-filesDragAndDrop
  • Hotfix/2382-guestStillBuggy
  • Hotfix/2384-guestsAndLinked
  • Hotfix/2427-adminTrouble
  • Hotfix/2459-EncodingPath
  • Hotfix/2465-orcidLink
  • Hotfix/2465-orcidLink-v1.25.1
  • Hotfix/2504-formGen
  • Hotfix/2541-resCreate
  • Hotfix/2601-correctMetadataIdentity
  • Hotfix/2611-feedback
  • Hotfix/2618-turtle
  • Hotfix/2681-validationErrors
  • Hotfix/2684-correctEncoding
  • Hotfix/2684-fixSubMetadata
  • Hotfix/2713-validateEntryName
  • Hotfix/2734-allowEmptyLicense
  • Hotfix/2765-encodingAgain
  • Hotfix/2852-adaptTextForToSUi
  • Hotfix/2853-optimizationV4
  • Hotfix/2943-reloadingResources
  • Hotfix/2943-searchHighlighting
  • Hotfix/2957-styleAndUpgrade
  • Hotfix/2971-fixTextInDataPub
  • Hotfix/2989-cookieLength
  • Hotfix/662-keepSidebarExpanded
  • Hotfix/xxxx-correctLinking
  • Hotfix/xxxx-folderRecursive
  • Hotfix/xxxx-fullscreenCss
  • Hotfix/xxxx-homepageDisplay
  • Hotfix/xxxx-liveReleaseFixes
  • Hotfix/xxxx-partnerProjects
  • Hotfix/xxxx-workingFileIndex
  • Issue/1782-structualDataIntegration
  • Issue/1792-newMetadataStructure
  • Issue/1822-coscineUIv2App
  • Issue/1824-componentsUIv2
  • Issue/1824-routerAdditions
  • Issue/1825-codeQualityPipelines
  • Issue/1833-newLogin
  • Issue/1843-multipleFilesValidation
  • Issue/1860-searchScoping
  • Issue/1861-searchMetadata
  • Issue/1862-searchFacets
  • Issue/1863-paginationForSearch
  • Issue/1926-userProfile
  • Issue/1927-projectAppMigration
  • Issue/1928-sidebarmenuAddition
  • Issue/1929-vuexToPinia
  • Issue/1938-internalHandling
  • Issue/1951-quotaImplementation
  • Issue/1953-owlImports
  • Issue/1957-resourceAppMigration
  • Issue/1957-resourceAppMigrationNew
  • Issue/1962-SearchAppUI2
  • Issue/1964-tokenExpiryUIv2
  • Issue/1965-userListMigration
  • Issue/1970-breadcrumbs
  • Issue/1971-projectEditCreateMigration
  • Issue/1972-homeDepot
  • Issue/1974-shibbolethLogout
  • Issue/1976-resouceCreationVaildEmail
  • Issue/1979-supportAdminUIv2Migration
  • Issue/1980-userManagement
  • Issue/1985-adaptSidebar
  • Issue/2002-migrateResourceCreate
  • Issue/2003-resourceSettings
  • Issue/2008-quotaManagement
  • Issue/2011-pathConfig
  • Issue/2016-BannerMigration
  • 1.28.0-pilot
  • v1.0.0
  • v1.1.0
  • v1.10.0
  • v1.10.1
  • v1.10.2
  • v1.10.3
  • v1.11.0
  • v1.11.1
  • v1.11.2
  • v1.11.3
  • v1.11.4
  • v1.11.5
  • v1.11.6
  • v1.11.7
  • v1.12.0
  • v1.13.0
  • v1.14.0
  • v1.14.1
  • v1.14.2
  • v1.14.3
  • v1.15.0
  • v1.15.1
  • v1.16.0
  • v1.16.1
  • v1.16.2
  • v1.16.3
  • v1.17.0
  • v1.17.1
  • v1.17.2
  • v1.18.0
  • v1.18.1
  • v1.19.0
  • v1.2.0
  • v1.20.0
  • v1.20.1
  • v1.20.2
  • v1.20.3
  • v1.20.4
  • v1.20.5
  • v1.21.0
  • v1.22.0
  • v1.22.1
  • v1.22.2
  • v1.23.0
  • v1.23.1
  • v1.23.2
  • v1.23.3
  • v1.23.4
  • v1.23.5
  • v1.23.6
  • v1.23.6-patch-2417-2427
  • v1.24.0
  • v1.24.1
  • v1.25.0
  • v1.25.1
  • v1.26.0
  • v1.26.1
  • v1.27.0
  • v1.27.1
  • v1.27.1-pilot
  • v1.28.0
  • v1.29.0
  • v1.29.1
  • v1.29.2
  • v1.3.0
  • v1.30.0
  • v1.30.1
  • v1.30.2
  • v1.31.0
  • v1.32.0
  • v1.4.0
  • v1.4.1
  • v1.5.0
  • v1.6.0
  • v1.6.1
  • v1.6.2
  • v1.7.0
  • v1.8.0
  • v1.8.1
  • v1.8.2
  • v1.9.0
  • v2.0.0
  • v2.1.0
  • v2.10.0
  • v2.10.1
  • v2.11.0
  • v2.12.0
  • v2.12.1
  • v2.12.2
  • v2.12.3
  • v2.12.4
  • v2.12.5
  • v2.13.0
  • v2.13.1
  • v2.13.2
  • v2.13.3
  • v2.13.4
  • v2.14.0
  • v2.15.0
200 results
Show changes
Commits on Source (11)
Showing
with 735 additions and 272 deletions
......@@ -3,7 +3,7 @@ module.exports = {
env: {
node: true,
},
ignorePatterns: ["node_modules", "build", "coverage"],
ignorePatterns: ["node_modules", "build", "coverage", "components.d.ts"],
plugins: ["eslint-comments", "functional"],
extends: [
"plugin:vue/recommended",
......
// generated by unplugin-vue-components
// We suggest you to commit this file into source control
// Read more: https://github.com/vuejs/vue-next/pull/3399
declare module 'vue' {
export interface GlobalComponents {
BreadCrumbs: typeof import('./components/elements/BreadCrumbs.vue')['default']
CoscineCard: typeof import('./components/coscine/CoscineCard.vue')['default']
CoscineFormGroup: typeof import('./components/coscine/CoscineFormGroup.vue')['default']
CoscineHeadline: typeof import('./components/coscine/CoscineHeadline.vue')['default']
CoscineModal: typeof import('./components/coscine/CoscineModal.vue')['default']
ExpiryToast: typeof import('./components/toasts/ExpiryToast.vue')['default']
LoadingIndicator: typeof import('./components/elements/LoadingIndicator.vue')['default']
LoadingSpinner: typeof import('./components/coscine/LoadingSpinner.vue')['default']
Maintenance: typeof import('./components/banner/Maintenance.vue')['default']
MultiSelect: typeof import('./components/coscine/MultiSelect.vue')['default']
Navbar: typeof import('./components/elements/Navbar.vue')['default']
NotificationToast: typeof import('./components/toasts/NotificationToast.vue')['default']
Pilot: typeof import('./components/banner/Pilot.vue')['default']
SidebarMenu: typeof import('./components/elements/SidebarMenu.vue')['default']
}
}
export { }
{
"name": "ui",
"version": "1.13.0",
"version": "1.14.0",
"private": true,
"scripts": {
"dev": "vite",
......@@ -44,7 +44,7 @@
"uuid": "^8.3.2",
"vite-aliases": "^0.9.1",
"vue": "~2.6.14",
"vue-demi": "^0.12.1",
"vue-demi": "^0.13.6",
"vue-i18n": "^8.27.0",
"vue-multiselect": "^2.1.6",
"vue-router": "^3.5.3",
......
......@@ -2,23 +2,23 @@
// We suggest you to commit this file into source control
// Read more: https://github.com/vuejs/vue-next/pull/3399
declare module "vue" {
declare module 'vue' {
export interface GlobalComponents {
BreadCrumbs: typeof import("./components/elements/BreadCrumbs.vue")["default"];
CoscineCard: typeof import("./components/coscine/CoscineCard.vue")["default"];
CoscineFormGroup: typeof import("./components/coscine/CoscineFormGroup.vue")["default"];
CoscineHeadline: typeof import("./components/coscine/CoscineHeadline.vue")["default"];
CoscineModal: typeof import("./components/coscine/CoscineModal.vue")["default"];
ExpiryToast: typeof import("./components/toasts/ExpiryToast.vue")["default"];
LoadingIndicator: typeof import("./components/elements/LoadingIndicator.vue")["default"];
LoadingSpinner: typeof import("./components/coscine/LoadingSpinner.vue")["default"];
Maintenance: typeof import("./components/banner/Maintenance.vue")["default"];
MultiSelect: typeof import("./components/coscine/MultiSelect.vue")["default"];
Navbar: typeof import("./components/elements/Navbar.vue")["default"];
NotificationToast: typeof import("./components/toasts/NotificationToast.vue")["default"];
Pilot: typeof import("./components/banner/Pilot.vue")["default"];
SidebarMenu: typeof import("./components/elements/SidebarMenu.vue")["default"];
BreadCrumbs: typeof import('./components/elements/BreadCrumbs.vue')['default']
CoscineCard: typeof import('./components/coscine/CoscineCard.vue')['default']
CoscineFormGroup: typeof import('./components/coscine/CoscineFormGroup.vue')['default']
CoscineHeadline: typeof import('./components/coscine/CoscineHeadline.vue')['default']
CoscineModal: typeof import('./components/coscine/CoscineModal.vue')['default']
ExpiryToast: typeof import('./components/toasts/ExpiryToast.vue')['default']
LoadingIndicator: typeof import('./components/elements/LoadingIndicator.vue')['default']
LoadingSpinner: typeof import('./components/coscine/LoadingSpinner.vue')['default']
Maintenance: typeof import('./components/banner/Maintenance.vue')['default']
MultiSelect: typeof import('./components/coscine/MultiSelect.vue')['default']
Navbar: typeof import('./components/elements/Navbar.vue')['default']
NotificationToast: typeof import('./components/toasts/NotificationToast.vue')['default']
Pilot: typeof import('./components/banner/Pilot.vue')['default']
SidebarMenu: typeof import('./components/elements/SidebarMenu.vue')['default']
}
}
export {};
export { }
......@@ -73,6 +73,7 @@ export default {
documentModifiedTitle: "{title} | @:(default.documentTitle)",
archive: "Archivieren",
archived: "Archiviert",
deleted: "Gelöscht",
help: "Hilfe",
loading: "Laden...",
or: "ODER",
......
......@@ -71,6 +71,7 @@ export default {
documentModifiedTitle: "{title} | @:(default.documentTitle)",
archive: "Archive",
archived: "Archived",
deleted: "Deleted",
help: "Help",
loading: "Loading...",
or: "OR",
......
......@@ -32,7 +32,7 @@
:remove-text="$t('buttons.remove')"
:owner-count="ownerCount"
:owner-role="ownerRole"
:is-owner="isOwner"
:interactive="isOwner"
@tableFilteredRows="onFilteredRows"
@setRole="setRole"
@selectedItem="prepareDeletion"
......
......@@ -39,7 +39,7 @@
(ownerCount === 1 &&
ownerRole &&
row.item.role.id === ownerRole.id) ||
!isOwner
!interactive
"
text-field="displayName"
value-field="id"
......@@ -67,7 +67,7 @@
(ownerCount === 1 &&
ownerRole &&
row.item.role.id === ownerRole.id) ||
!isOwner
!interactive
"
@click="deleteItem(row.item)"
>{{ removeText }}</b-button
......@@ -177,7 +177,7 @@ export default defineComponent({
default: "user.surname",
type: String,
},
isOwner: {
interactive: {
default: false,
type: Boolean,
},
......
......@@ -42,11 +42,15 @@
}}
</span>
<span v-else>
{{ option.displayName }}
{{
option.emailAddress
? `${option.displayName} (${option.emailAddress})`
: option.displayName
}}
</span>
<span v-if="option.hasProjectRole">
{{ $t("page.members.alreadyGotRole") }}
</span>
<span v-if="option.hasProjectRole">{{
$t("page.members.alreadyGotRole")
}}</span>
</div>
</template>
<template slot="selected-option" slot-scope="option">
......@@ -72,34 +76,37 @@
text-field="displayName"
value-field="id"
@input="setNewRole"
>
</b-form-select>
/>
</b-col>
<!-- Buttons -->
<b-col id="thirdCol" sm="5">
<!-- Add User -->
<!-- Invite -->
<b-button
v-if="validInvitation && !validSelection"
v-if="emailInvitation && !userInvitation"
name="inviteUserToProject"
:disabled="!validInvitation || !isOwner"
:disabled="!emailInvitation || !isOwner"
@click="prepareInvitation(newUserRole)"
>{{ $t("buttons.invite") }}</b-button
>
<!-- Import -->
{{ $t("buttons.invite") }}
</b-button>
<!-- Add User -->
<b-button
v-else
name="addUserToProject"
:disabled="!validSelection || !isOwner"
:disabled="!userInvitation || !isOwner"
@click="addUser(newUserRole)"
>{{ $t("buttons.addUser") }}</b-button
>
{{ $t("buttons.addUser") }}
</b-button>
<!-- Import -->
<b-button
name="importUser"
:disabled="!isOwner"
@click="$bvModal.show('importUserModal')"
>{{ $t("buttons.import") }}</b-button
>
{{ $t("buttons.import") }}
</b-button>
</b-col>
</b-row>
</div>
......@@ -118,7 +125,7 @@
type="search"
:placeholder="$t('page.members.typeToSearch')"
@input="setFilter"
></b-form-input>
/>
</b-input-group>
</b-col>
</b-row>
......@@ -131,6 +138,7 @@ import { defineComponent, PropType } from "vue-demi";
import vSelect from "vue-select";
import "vue-select/dist/vue-select.css";
// Line bellow is shown as error, but works. No fixes known.
Vue.component("VSelect", vSelect);
import useUserStore from "@/modules/user/store";
......@@ -170,7 +178,17 @@ export default defineComponent({
},
data() {
return {
newUserRole: {} as ProjectRoleObject,
newUserRole: {
projectId: "",
role: {
id: "",
displayName: "",
},
user: {
id: "",
emailAddress: "",
},
} as ProjectRoleObject,
queryTimer: 0,
queriedUsers: [] as UserObject[],
searchString: "",
......@@ -192,41 +210,42 @@ export default defineComponent({
return false;
}
},
validInvitation(): boolean {
if (!this.validEmail) {
return false;
}
emailInvitation(): boolean {
if (
this.newUserRole &&
this.newUserRole.projectId &&
this.newUserRole.role &&
this.newUserRole.role.id &&
this.newUserRole.user &&
!this.newUserRole.user.id &&
this.newUserRole.user.emailAddress
) {
return true;
}
return false;
},
validSelection(): boolean {
if (this.validEmail) {
return false;
}
userInvitation(): boolean {
if (
this.newUserRole &&
this.newUserRole.projectId &&
this.newUserRole.user &&
this.newUserRole.user.id &&
this.newUserRole.role &&
this.newUserRole.role.id &&
this.newUserRole.user.id
this.newUserRole.role.id
) {
return true;
}
return false;
},
},
watch: {
memberRole() {
Object.assign(this.newUserRole.role, this.memberRole);
},
},
mounted() {
if (this.memberRole) {
this.newUserRole.role = {
id: this.memberRole.id,
displayName: this.memberRole.displayName,
};
Object.assign(this.newUserRole.role, this.memberRole);
}
},
methods: {
......@@ -322,4 +341,8 @@ export default defineComponent({
.no-results >>> .vs__actions {
display: none;
}
.adaptSelect >>> .vs__dropdown-menu {
min-width: 100%;
width: unset;
}
</style>
......@@ -40,6 +40,7 @@
:empty-text="$t('page.members.emptyImportTableText')"
:empty-filtered-text="$t('page.members.emptyImportTableFilterText')"
:remove-text="$t('page.members.removeUser')"
:interactive="true"
@selectedItem="removeSelectedRow"
/>
</div>
......
......@@ -516,7 +516,10 @@ export default defineComponent({
if (resultArray.length > 0) {
const result = resultArray[0][Object.keys(resultArray[0])[0]];
if (typeof result === "string") {
newEntry.metadata = await parseRDFDefinition(result);
newEntry.metadata = await parseRDFDefinition(
result,
"application/n-triples"
);
}
}
......
......@@ -370,7 +370,7 @@ export const useResourceStore = defineStore({
const response = await TreeApi.treeGetMetadataWithParameter(
resource.id,
absoluteFilePath,
"text/turtle"
"application/n-triples"
);
return response.data;
} else {
......
......@@ -68,7 +68,9 @@ export default {
) {
const entry = result[objectKeys[0]];
if (typeof entry === "string") {
return callback(await parseRDFDefinition(entry));
return callback(
await parseRDFDefinition(entry, "application/n-triples")
);
}
} else if (
response.data.metadataStorage !== undefined &&
......@@ -76,7 +78,9 @@ export default {
) {
const entry = result;
if (typeof entry === "string") {
return callback(await parseRDFDefinition(entry));
return callback(
await parseRDFDefinition(entry, "application/n-triples")
);
}
}
}
......
......@@ -23,4 +23,45 @@ export default {
allResources: "Alle Resourcen",
},
},
results: {
labels: {
file: "Datei",
resource: "Ressource",
project: "Projekt",
isPublic: "Öffentlich",
isPrivate: "Privat",
date_created: "Erstelldatum",
date_created_year: "Erstellungsjahr",
date_created_month: "Erstellungsmonat",
date_created_day: "Erstellungstag",
subject: "Betreff",
title: "Titel",
graphName: "Graphname",
fileName: "Dateiname",
applicationProfile: "Applikationsprofil",
belongsToProject: "Gehört zu Projekt",
id: "ID",
projectName: "Projektname",
displayName: "Anzeigename",
description: "Beschreibung",
slug: "Projekt-Slug",
principleInvestigators: "Principal Investigators (PIs)",
startDate: "Projektstart",
endDate: "Projektende",
disciplines: "Disziplin",
organizations: "Teilnehmende Organisationen",
keywords: "Schlagwörter",
visibility: "Sichtbarkeit",
grantId: "Grant ID",
parentId: "Übergeordnetes Projekt",
resourceName: "Ressourcenname",
resourceType: "Ressourcentyp",
pid: "Persistente ID",
usageRights: "Interne Regeln zur Nachnutzung",
license: "Lizenz",
creator: "Ersteller",
archived: "Archiviert",
},
},
} as VueI18n.LocaleMessageObject;
......@@ -23,4 +23,41 @@ export default {
allResources: "All Resources",
},
},
results: {
labels: {
file: "File",
resource: "Resource",
project: "Project",
isPublic: "Public",
isPrivate: "Private",
subject: "Subject",
title: "Title",
graphName: "Graph Name",
fileName: "File Name",
applicationProfile: "Application Profile",
belongsToProject: "Belongs To Project",
id: "ID",
projectName: "Project Name",
displayName: "Display Name",
description: "Description",
slug: "Project Slug",
principleInvestigators: "Principal Investigators (PIs)",
startDate: "Project Start",
endDate: "Project End",
disciplines: "Discipline",
organizations: "Participating Organizations",
keywords: "Keywords",
visibility: "Visibility",
grantId: "Grant ID",
parentId: "Parent Project",
resourceName: "Resource Name",
resourceType: "Resource Type",
pid: "Persistent ID",
usageRights: "Internal Rules for Reuse",
license: "License",
creator: "Creator",
archived: "Archived",
},
},
} as VueI18n.LocaleMessageObject;
......@@ -14,7 +14,7 @@
v-model="searchText"
:debounce="1000"
:placeholder="$t('page.search.search')"
></b-form-input>
/>
</b-col>
<b-col id="selectProjCol" sm="2" align-self="center" class="pl-0">
<b-form-select
......@@ -23,8 +23,8 @@
@change="queryData(searchText)"
>
<template #first>
<b-form-select-option :value="null"
>{{ $t("page.search.allProjects") }}
<b-form-select-option :value="null">
{{ $t("page.search.allProjects") }}
</b-form-select-option>
</template>
</b-form-select>
......@@ -36,8 +36,8 @@
@change="queryData(searchText)"
>
<template #first>
<b-form-select-option :value="null"
>{{ $t("page.search.allResources") }}
<b-form-select-option :value="null">
{{ $t("page.search.allResources") }}
</b-form-select-option>
</template>
</b-form-select>
......@@ -90,7 +90,8 @@
pill
class="mr-1"
@remove="removeTag(tag)"
>{{ tag }}
>
{{ tag }}
</b-form-tag>
</div>
</template>
......@@ -114,14 +115,14 @@
class="p-2 border-top"
style="height: 105px"
>
<b-skeleton width="30%" class="m-2 mb-3"></b-skeleton>
<b-skeleton width="95%" class="m-2"></b-skeleton>
<b-skeleton width="40%" class="m-2"></b-skeleton>
<b-skeleton width="30%" class="m-2 mb-3" />
<b-skeleton width="95%" class="m-2" />
<b-skeleton width="40%" class="m-2" />
</div>
</template>
<b-table
id="resultsView"
:items="searchResults"
:items="resultsForTable"
:fields="resultsViewFields"
:per-page="paginationPerPage"
:current-page="paginationCurrentPage"
......@@ -133,7 +134,21 @@
show-empty
>
<template #cell(type)="data">
<Result :result="data.item" />
<FileResult
v-if="data.item.type === 'File'"
:result="data.item.value"
:all-projects="allProjects"
/>
<ProjectResult
v-else-if="data.item.type === 'Project'"
:result="data.item.value"
:all-projects="allProjects"
/>
<ResourceResult
v-else-if="data.item.type === 'Resource'"
:result="data.item.value"
:all-projects="allProjects"
/>
</template>
<template #empty>
......@@ -173,14 +188,14 @@
:per-page="paginationPerPage"
aria-controls="resultsView"
align="center"
></b-pagination>
/>
</b-col>
<b-col align-self="center" class="p-0">
<b-form-select
v-model="paginationPerPage"
:options="paginationPerPageOptions"
style="max-width: 5rem"
></b-form-select>
/>
</b-col>
</b-row>
</div>
......@@ -188,7 +203,9 @@
<script lang="ts">
import { defineComponent } from "vue-demi";
import Result from "./components/Result.vue";
import FileResult from "./components/FileResult.vue";
import ProjectResult from "./components/ProjectResult.vue";
import ResourceResult from "./components/ResourceResult.vue";
import Sidebar from "./components/Sidebar.vue";
// import the store for current module
......@@ -198,20 +215,18 @@ import useMainStore from "@/store/index";
// import the project store
import useProjectStore from "@/modules/project/store";
import type {
EnhancedOldSearchResult,
EnhancedSearchResult,
OldSearchCollection,
} from "../types";
import type { SearchResult } from "@coscine/api-client/dist/types/Coscine.Api.Search";
import type {
ProjectObject,
ResourceObject,
} from "@coscine/api-client/dist/types/Coscine.Api.Project";
SearchResult,
} from "@coscine/api-client/dist/types/Coscine.Api.Search";
import { ExtendedSearchResult, SearchResultType } from "../types";
export default defineComponent({
components: {
Result,
FileResult,
ProjectResult,
ResourceResult,
Sidebar,
},
setup() {
......@@ -237,7 +252,7 @@ export default defineComponent({
resultsViewLoading: true,
paginationCurrentPage: 1,
paginationPerPage: 10,
paginationPerPage: 100,
paginationPerPageOptions: [5, 10, 20, 50, 100],
};
},
......@@ -246,6 +261,9 @@ export default defineComponent({
allProjects(): ProjectObject[] | null {
return this.projectStore.allProjects;
},
allResources(): ResourceObject[] | null {
return null;
},
allProjectsOptions(): {
value: ProjectObject;
text: string | null | undefined;
......@@ -275,72 +293,94 @@ export default defineComponent({
return [];
},
paginationTotalRows(): number {
if (this.searchResults !== null) {
return this.searchResults.length;
let rows = 0;
if (this.searchResults) {
if (this.searchResults.files) {
rows += this.searchResults.files.length;
}
return 0;
},
oldSearchResults(): OldSearchCollection | null {
return this.searchStore.oldSearchResults;
if (this.searchResults.projects) {
rows += this.searchResults.projects.length;
}
if (this.searchResults.resources) {
rows += this.searchResults.resources.length;
}
}
return rows;
},
searchResults(): (EnhancedSearchResult | EnhancedOldSearchResult)[] {
const searchResults: (EnhancedSearchResult | EnhancedOldSearchResult)[] =
[];
if (this.oldSearchResults !== null) {
searchResults.push(
...this.oldSearchResults.Projects.filter(
(project) =>
!this.selectedProject || this.selectedProject.id === project.Id
).map((project) => {
return { ...project, type: "Project" } as EnhancedOldSearchResult;
})
);
searchResults.push(
...this.oldSearchResults.Resources.filter(
(resource) =>
(!this.selectedProject ||
this.projectResources.some(
(projectResource) => projectResource.id === resource.Id
)) &&
resultsForTable(): ExtendedSearchResult[] {
let result: ExtendedSearchResult[] = [];
if (this.searchResults) {
if (this.searchResults.files) {
result.push(
...this.searchResults.files
.filter(
(file) =>
(!this.selectedResource ||
this.selectedResource.id === resource.Id)
).map((resource) => {
return { ...resource, type: "Resource" } as EnhancedOldSearchResult;
!this.selectedResource.id ||
file.graphName?.includes(this.selectedResource.id)) &&
// Logic for omitting files that belong to a deleted project
this.allProjects &&
this.allProjects.some((p) => p.id === file.projectId)
)
.map((file) => {
return {
value: file,
type: SearchResultType.File,
} as ExtendedSearchResult;
})
);
searchResults.push(
...this.oldSearchResults.SubProjects.filter(
(subProject) =>
!this.selectedProject ||
}
if (this.searchResults.projects) {
result.push(
...this.searchResults.projects
.filter(
(project) =>
(!this.selectedProject ||
this.projectSubProjects.some(
(projectSubProject) => projectSubProject.id === subProject.Id
(projectSubProject) => projectSubProject.id === project.id
)) &&
(!this.selectedProject ||
this.selectedProject.id === project.id) &&
// Logic for omitting deleted project
this.allProjects &&
this.allProjects.some((p) => p.id === project.id)
)
).map((subProject) => {
.map((project) => {
return {
...subProject,
type: "SubProject",
} as EnhancedOldSearchResult;
value: project,
type: SearchResultType.Project,
} as ExtendedSearchResult;
})
);
}
if (this.semanticSearchResults !== null) {
searchResults.push(
...this.semanticSearchResults
if (this.searchResults.resources) {
result.push(
...this.searchResults.resources
.filter(
(file) =>
!this.selectedResource ||
!this.selectedResource.id ||
file.graphName?.includes(this.selectedResource.id)
(resource) =>
(!this.selectedProject ||
this.projectResources.some(
(projectResource) => projectResource.id === resource.id
)) &&
(!this.selectedResource ||
this.selectedResource.id === resource.id) &&
// Logic for omitting resources that belong to a deleted project
this.allProjects &&
this.allProjects.some((p) => p.id === resource.projectId)
)
.map((result) => {
return { ...result, type: "File" } as EnhancedSearchResult;
.map((resource) => {
return {
value: resource,
type: SearchResultType.Resource,
} as ExtendedSearchResult;
})
);
}
return searchResults;
}
return result;
},
semanticSearchResults(): SearchResult[] | null {
return this.searchStore.semanticSearchResults;
searchResults(): SearchResult | null {
return this.searchStore.searchResults;
},
},
......@@ -373,13 +413,9 @@ export default defineComponent({
},
async queryData(query: string) {
this.resultsViewLoading = true;
await Promise.all([
this.searchStore.retrieveOldSearchResults(query),
this.searchStore.retrieveSemanticSearchResults(
query,
this.selectedProject
),
]);
// attach search query, resulting in "/search?q=<searchTerm>"
this.$router.push({ name: "search", query: { q: query } });
await this.searchStore.retrieveSearchResults(query, this.selectedProject);
this.resultsViewLoading = false;
},
async retrieveResources() {
......
<template>
<div class="p-2">
<a :href="url">
<div id="resultHeader" class="mb-2 text-left text-primary">
<b>{{ `${$t("results.labels.file")}: ` }}</b>
{{ displayName }}
<b-badge v-if="!parentProject" pill variant="light" class="ml-1">
{{ $t("default.deleted") }}
<b-icon icon="trash" variant="danger" />
</b-badge>
</div>
</a>
<div id="resultBody" class="text-left">
<span
v-for="(element, index) in fields"
:key="index"
class="mr-3 d-inline-block"
>
<span v-if="index === 'isPublic'">
<b>{{ `${$t("results.labels.visibility")}: ` }}</b>
<span v-if="element">
{{ $t("results.labels.isPublic") }}
<b-icon icon="people-fill" />
</span>
<span v-else>
{{ $t("results.labels.isPrivate") }}
<b-icon icon="shield-lock-fill" />
</span>
</span>
<span v-else-if="index === 'belongsToProject'">
<b>{{ `${$t(`results.labels.${index}`)}: ` }}</b>
{{ belongsToProject(element) }}
</span>
<span
v-else-if="
index === 'graphName' ||
index === 'fileName' ||
index === 'applicationProfile'
"
>
<b>{{ `${$t(`results.labels.${index}`)}: ` }}</b>
{{ element }}
</span>
<span v-else>
<b>{{ `${index}: ` }}</b>
{{ element }}
</span>
<!-- Keep the <br/> element at the end to have
double mouse click text selection work properly and
not have a table horizontal scrollbar appear -->
<br />
</span>
</div>
</div>
</template>
<script lang="ts">
import { defineComponent, PropType } from "vue-demi";
// import the store for current module
import useSearchStore from "../../store";
// import the main store
import useMainStore from "@/store/index";
import useResourceStore from "@/modules/resource/store";
import type {
FileSearchResult,
ProjectObject,
ResourceObject,
} from "@coscine/api-client/dist/types/Coscine.Api.Search";
export default defineComponent({
props: {
result: {
required: true,
type: Object as PropType<FileSearchResult>,
},
allProjects: {
required: true,
type: Array as PropType<ProjectObject[]>,
},
},
setup() {
const mainStore = useMainStore();
const resourceStore = useResourceStore();
const searchStore = useSearchStore();
return { mainStore, resourceStore, searchStore };
},
computed: {
archived(): boolean | undefined {
return (this.result as ResourceObject).archived;
},
displayName(): string | null | undefined {
return this.result.graphName
? decodeURIComponent(this.result.graphName.split("@path=%2F")[1])
: "";
},
parentProject(): ProjectObject | undefined {
return this.allProjects.find((p) => p.id === this.result.projectId);
},
fields(): Record<string, string> {
return this.result.source as Record<string, string>;
},
url(): string {
if (this.parentProject) {
return this.$router.resolve({
name: "resource-page",
params: {
guid: this.result.resourceId ?? "",
slug: this.parentProject?.slug ?? "",
},
}).href;
} else {
return this.result.graphName ?? "#";
}
},
},
methods: {
isPublic(value: string): string {
return value
? this.$t("results.labels.isPublic").toString()
: this.$t("results.labels.isPrivate").toString();
},
belongsToProject(value: string): string {
const projectId = value.replace("https://purl.org/coscine/projects/", "");
const project = this.allProjects.find((p) => p.id === projectId);
if (project && project.displayName) {
return project.displayName;
} else return value;
},
},
});
</script>
<template>
<div class="p-2">
<a :href="url">
<div id="resultHeader" class="mb-2 text-left text-primary">
<b>{{ `${$t("results.labels.project")}: ` }}</b>
{{ displayName }}
<b-badge v-if="!project" pill variant="light" class="ml-1">
{{ $t("default.deleted") }}
<b-icon icon="trash" variant="danger" />
</b-badge>
</div>
</a>
<div id="resultBody" class="text-left">
<span
v-for="(element, index) in fields"
:key="index"
class="mr-3 d-inline-block"
>
<span>
<b>{{ `${$t(`results.labels.${index}`)}: ` }}</b>
{{ element }}
</span>
<!-- Keep the <br/> element at the end to have
double mouse click text selection work properly and
not have a table horizontal scrollbar appear -->
<br />
</span>
</div>
</div>
</template>
<script lang="ts">
import { defineComponent, PropType } from "vue-demi";
// import the store for current module
import useSearchStore from "../../store";
// import the main store
import useMainStore from "@/store/index";
import useResourceStore from "@/modules/resource/store";
import type {
DisciplineObject,
ProjectObject,
} from "@coscine/api-client/dist/types/Coscine.Api.Search";
export default defineComponent({
props: {
result: {
required: true,
type: Object as PropType<ProjectObject>,
},
allProjects: {
required: true,
type: Array as PropType<ProjectObject[]>,
},
},
setup() {
const mainStore = useMainStore();
const resourceStore = useResourceStore();
const searchStore = useSearchStore();
return { mainStore, resourceStore, searchStore };
},
computed: {
project(): ProjectObject | undefined {
const project = this.allProjects.find((p) => p.id === this.result.id);
return project;
},
displayName(): string | null | undefined {
return this.result.displayName;
},
disciplineLabel(): string {
let locale = this.$root.$i18n.locale;
locale = locale.charAt(0).toUpperCase() + locale.slice(1);
return `displayName${locale}`;
},
fields(): Record<string, string> {
let record: Record<string, string> = {};
record["id"] = this.result.id ?? this.$t("default.none").toString();
record["projectName"] =
this.result.projectName ?? this.$t("default.none").toString();
record["displayName"] =
this.result.displayName ?? this.$t("default.none").toString();
record["slug"] = this.result.slug ?? this.$t("default.none").toString();
record["principleInvestigators"] =
this.result.principleInvestigators ??
this.$t("default.none").toString();
record["startDate"] =
this.result.startDate ?? this.$t("default.none").toString();
record["endDate"] =
this.result.endDate ?? this.$t("default.none").toString();
record["disciplines"] = this.getDisciplineDisplayNames(
this.result.disciplines
);
record["organizations"] =
this.result.organizations?.map((o) => o.displayName).join(", ") ??
this.$t("default.none").toString();
record["keywords"] =
(this.result.keywords?.trim() !== ""
? this.result.keywords
: this.$t("default.none").toString()) ??
this.$t("default.none").toString();
record["visibility"] =
this.result.visibility?.displayName ??
this.$t("default.none").toString();
record["parentId"] =
this.result.parentId === "00000000-0000-0000-0000-000000000000"
? this.$t("default.none").toString()
: this.allProjects.find((p) => p.id === this.result.parentId)
?.displayName ?? this.$t("default.none").toString();
return record;
},
url(): string {
return this.$router.resolve({
name: "project-page",
params: { slug: this.result.slug ?? "" },
}).href;
},
},
methods: {
isPublic(value: string): string {
return value
? this.$t("results.labels.isPublic").toString()
: this.$t("results.labels.isPrivate").toString();
},
getDisciplineDisplayNames(
value: DisciplineObject[] | null | undefined
): string {
let disciplines: Array<string> = [];
if (value) {
value.forEach((e) => {
const hit = (
Object.entries(e).find(
(e) => e[0] === this.disciplineLabel
) as Array<string>
).at(1);
if (hit) {
disciplines.push(hit);
}
});
return disciplines.join(", ");
} else {
return this.$t("default.none").toString();
}
},
},
});
</script>
<template>
<div class="p-2">
<a :href="url">
<div id="resultHeader" class="mb-2 text-left text-primary">
<b>{{ `${$t("results.labels.resource")}: ` }}</b>
{{ displayName }}
<b-badge v-if="archived" pill variant="warning" class="ml-1">
{{ $t("default.archived") }}
</b-badge>
<b-badge v-if="!parentProject" pill variant="light" class="ml-1">
{{ $t("default.deleted") }}
<b-icon icon="trash" variant="danger" />
</b-badge>
</div>
</a>
<div id="resultBody" class="text-left">
<span
v-for="(element, index) in fields"
:key="index"
class="mr-3 d-inline-block"
>
<span>
<b>{{ `${$t(`results.labels.${index}`)}: ` }}</b>
{{ element }}
</span>
<!-- Keep the <br/> element at the end to have
double mouse click text selection work properly and
not have a table horizontal scrollbar appear -->
<br />
</span>
</div>
</div>
</template>
<script lang="ts">
import { defineComponent, PropType } from "vue-demi";
// import the store for current module
import useSearchStore from "../../store";
// import the main store
import useMainStore from "@/store/index";
import useResourceStore from "@/modules/resource/store";
import type {
DisciplineObject,
ProjectObject,
ResourceSearchResult,
} from "@coscine/api-client/dist/types/Coscine.Api.Search";
export default defineComponent({
props: {
result: {
required: true,
type: Object as PropType<ResourceSearchResult>,
},
allProjects: {
required: true,
type: Array as PropType<ProjectObject[]>,
},
},
setup() {
const mainStore = useMainStore();
const resourceStore = useResourceStore();
const searchStore = useSearchStore();
return { mainStore, resourceStore, searchStore };
},
computed: {
archived(): boolean | undefined {
return this.result.archived;
},
displayName(): string | null | undefined {
return this.result.displayName;
},
disciplineLabel(): string {
let locale = this.$root.$i18n.locale;
locale = locale.charAt(0).toUpperCase() + locale.slice(1);
return `displayName${locale}`;
},
parentProject(): ProjectObject | undefined {
return this.allProjects.find((p) => p.id === this.result.projectId);
},
fields(): Record<string, string> {
let record: Record<string, string> = {};
record["id"] = this.result.id ?? this.$t("default.none").toString();
record["resourceName"] =
this.result.resourceName ?? this.$t("default.none").toString();
record["displayName"] =
this.result.displayName ?? this.$t("default.none").toString();
record["pid"] = this.result.pid ?? this.$t("default.none").toString();
record["resourceType"] =
this.$t(
"resourceTypes." + this.result.type?.displayName + ".displayName"
).toString() ?? this.$t("default.none").toString();
record["applicationProfile"] =
this.result.applicationProfile ?? this.$t("default.none").toString();
record["disciplines"] = this.getDisciplineDisplayNames(
this.result.disciplines
);
record["keywords"] =
(this.result.keywords?.trim() !== ""
? this.result.keywords
: this.$t("default.none").toString()) ??
this.$t("default.none").toString();
record["visibility"] =
this.result.visibility?.displayName ??
this.$t("default.none").toString();
record["license"] =
this.result.license?.displayName ?? this.$t("default.none").toString();
record["usageRights"] =
(this.result.usageRights?.trim() !== ""
? this.result.usageRights
: this.$t("default.none").toString()) ??
this.$t("default.none").toString();
return record;
},
url(): string {
if (this.parentProject) {
return this.$router.resolve({
name: "resource-page",
params: {
guid: this.result.id ?? "",
slug: this.parentProject?.slug ?? "",
},
}).href;
} else {
return "http://hdl.handle.net/" + this.result.pid;
}
},
},
methods: {
getDisciplineDisplayNames(
value: DisciplineObject[] | null | undefined
): string {
let disciplines: Array<string> = [];
if (value) {
value.forEach((e) => {
const hit = (
Object.entries(e).find(
(e) => e[0] === this.disciplineLabel
) as Array<string>
).at(1);
if (hit) {
disciplines.push(hit);
}
});
return disciplines.join(", ");
} else {
return this.$t("default.none").toString();
}
},
},
});
</script>
<template>
<div class="p-2">
<a :href="url">
<div id="resultHeader" class="mb-2 text-left text-primary">
<b>{{ result.type }}</b
>: {{ displayName }}
<b-badge v-if="archived" pill variant="warning" class="ml-1">{{
$t("default.archived")
}}</b-badge>
</div>
</a>
<div id="resultBody" class="text-left">
<span
v-for="(element, index) in fields"
:key="index"
class="mr-3 d-inline-block"
>
<!-- Keep the <br/> element at the end to have
double mouse click text selection work properly and
not have a table horizontal scrollbar appear -->
<b>{{ index }}</b
>: {{ element }}<br />
</span>
</div>
</div>
</template>
<script lang="ts">
import { defineComponent, PropType } from "vue-demi";
// import the store for current module
import useSearchStore from "../../store";
// import the main store
import useMainStore from "@/store/index";
import type {
EnhancedOldSearchResult,
EnhancedSearchResult,
} from "../../types";
export default defineComponent({
props: {
result: {
required: true,
type: Object as PropType<EnhancedOldSearchResult | EnhancedSearchResult>,
},
},
setup() {
const mainStore = useMainStore();
const searchStore = useSearchStore();
return { mainStore, searchStore };
},
data() {
return {};
},
computed: {
archived(): boolean {
// We don't get this information currently without specifically querying it
return false;
},
displayName(): string {
if (this.result.type === "File") {
return this.result.graphName
? decodeURIComponent(this.result.graphName.split("@path=%2F")[1])
: "";
} else {
return this.result.DisplayName;
}
},
fields(): Record<string, string> {
if (this.result.type === "File") {
return this.result.source as Record<string, string>;
} else {
return {
Id: this.result.Id,
Slug: this.result.Slug,
};
}
},
url(): string {
if (this.result.type === "File") {
return this.result.graphName ? this.result.graphName : "#";
} else if (this.result.type === "Resource") {
return this.$router.resolve({
name: "resource-page",
params: { guid: this.result.Id, slug: this.result.Slug },
}).href;
} else {
return this.$router.resolve({
name: "project-page",
params: { slug: this.result.Slug },
}).href;
}
},
},
});
</script>