Skip to content

Commits on Source 9

{
"name": "ui",
"version": "2.2.1",
"version": "2.2.2",
"private": true,
"scripts": {
"dev": "vite",
......
......@@ -205,8 +205,8 @@ export default defineComponent({
*/
addParentProjects(breadcrumbs: RouteLink[]): RouteLink[] {
if (this.project && this.parentProjects) {
const parentBreadCrumbs: RouteLink[] = this.parentProjects.map(
(parentProject) => {
const parentBreadCrumbs: RouteLink[] = this.parentProjects
.map((parentProject) => {
return {
to: {
name: "project-page",
......@@ -216,8 +216,8 @@ export default defineComponent({
projectName: parentProject.displayName,
}).toString(),
} as RouteLink;
}
);
})
.reverse(); // Reverse the order
breadcrumbs.unshift(...parentBreadCrumbs);
}
return breadcrumbs;
......
......@@ -5,9 +5,11 @@
<script lang="ts">
import { defineComponent } from "vue";
import useNotificationStore from "@/store/notification";
import type { NotificationToast } from "@/store/types";
import useLoginStore from "@/modules/login/store";
import type { I18nOptions, NotificationToast } from "@/store/types";
import type { VNode } from "vue";
export default defineComponent({
setup() {
const notificationStore = useNotificationStore();
......@@ -45,13 +47,37 @@ export default defineComponent({
methods: {
async makeToast(toast: NotificationToast) {
const h = this.$createElement;
let toastBody: VNode;
if (!toast.toaster) {
toast.toaster = "b-toaster-bottom-right";
}
this.$root.$bvToast.toast(toast.body, toast);
// Build the toast body from the i18n options object
if (typeof toast.body === "object" && toast.body.path) {
const options: I18nOptions = toast.body;
const placeholders = options.placeholders || {};
const childNodes = Object.entries(placeholders).map(
([place, value]) => {
return h("template", { slot: place }, [
h(value.el, { class: value.class }, value.value),
]);
}
);
toastBody = h(
"i18n",
{ props: { path: options.path, tag: options.tag } },
childNodes
);
}
// Build the toast body from a string
else {
toastBody = h("div", {}, toast.body as string);
}
this.$root.$bvToast.toast(toastBody, toast);
},
},
});
</script>
<style></style>
......@@ -136,7 +136,7 @@ export default {
},
specific: {
title: "Ein Fehler ist aufgetreten",
body: "Es ist ein Fehler aufgetreten {error}. Bitte versuchen Sie es erneut. Falls der Fehler weiterhin auftritt, wenden Sie sich bitte an Ihre Organization und geben Sie die folgende Fehler-ID an: {errorId}.",
body: "Es ist ein Fehler aufgetreten {error}. Bitte versuchen Sie es erneut. Falls der Fehler weiterhin auftritt, wenden Sie sich bitte an Ihre Organization und geben Sie die folgende Fehler-ID an: {traceId}.",
},
},
},
......
......@@ -134,7 +134,7 @@ export default {
},
specific: {
title: "An error occurred",
body: "Something went wrong {error}. Please try again. If the issue persists, contact your organization and provide the following Error ID: {errorId}.",
body: "Something went wrong {error}. Please try again. If the issue persists, contact your organization and provide the following Trace ID: {traceId}.",
},
},
},
......
......@@ -28,7 +28,7 @@ export default {
"@:(page.pid.form.persistentId)@:(page.pid.form.labelSymbol)",
persistentIdPopover:
"Für weitere Informationen zum @:(page.pid.form.persistentId) siehe",
persistentIdPopoverUrl: "https://docs.coscine.de/de/rdm/publishing/",
persistentIdPopoverUrl: "https://docs.coscine.de/de/resources/pid/",
yourName: "Ihr Name",
yourNameLabel:
......
......@@ -28,7 +28,7 @@ export default {
"@:(page.pid.form.persistentId)@:(page.pid.form.labelSymbol)",
persistentIdPopover:
"For more information on @:(page.pid.form.persistentId) see",
persistentIdPopoverUrl: "https://docs.coscine.de/en/rdm/publishing/",
persistentIdPopoverUrl: "https://docs.coscine.de/en/resources/pid/",
yourName: "Your Name",
yourNameLabel:
......
......@@ -216,7 +216,7 @@ export default {
projectPersistentIdPopover:
"Für weitere Informationen zum @:(form.project.projectPersistentId) siehe",
projectPersistentIdPopoverUrl:
"https://docs.coscine.de/de/rdm/publishing/",
"https://docs.coscine.de/de/resources/pid/",
activatedImportFromParent: "Projekt-Metadaten",
......
......@@ -211,7 +211,7 @@ export default {
projectPersistentIdPopover:
"For more information on @:(form.project.projectPersistentId) see",
projectPersistentIdPopoverUrl:
"https://docs.coscine.de/en/rdm/publishing/",
"https://docs.coscine.de/en/resources/pid/",
activatedImportFromParent: "Project Metadata",
......
......@@ -68,7 +68,6 @@ export default defineComponent({
openProject(to: RawLocation, project: ProjectDto) {
if (project.slug) {
this.projectStore.currentSlug = project.slug;
this.projectStore.setProjectAsVisited(project);
this.$router.push(to);
} else {
console.error(
......
......@@ -199,7 +199,6 @@ export default defineComponent({
openProject(to: RawLocation, project: ProjectDto) {
if (project.slug) {
// Add project here, to save an unnecessary API request
this.projectStore.setProjectAsVisited(project);
this.$router.push(to);
} else {
console.error(
......
......@@ -228,7 +228,7 @@ export const useProjectStore = defineStore({
const notificationStore = useNotificationStore();
try {
this.allProjects = await wrapListRequest((pageNumber: number) =>
ProjectApi.getProjects(false, pageNumber)
ProjectApi.getProjects(false, pageNumber, 150)
);
} catch (error) {
// Handle other Status Codes
......@@ -240,7 +240,7 @@ export const useProjectStore = defineStore({
const notificationStore = useNotificationStore();
try {
this.topLevelProjects = await wrapListRequest((pageNumber: number) =>
ProjectApi.getProjects(true, pageNumber)
ProjectApi.getProjects(true, pageNumber, 150)
);
} catch (error) {
// Handle other Status Codes
......@@ -683,7 +683,7 @@ export const useProjectStore = defineStore({
this.retrieveAllProjects(),
this.retrieveTopLevelProjects(),
]);
if (parentProject && parentProject.slug) {
if (parentProject?.slug) {
await this.retrieveProjectBySlug(parentProject.slug);
await Promise.all([this.retrieveResources(parentProject)]);
}
......@@ -700,7 +700,10 @@ export const useProjectStore = defineStore({
roles: null,
quotas: null,
});
this.visitedProjects[project.slug] = visitedProject;
this.visitedProjects = {
...this.visitedProjects,
[project.slug]: visitedProject,
};
} else {
Object.assign(this.visitedProjects[project.slug], project);
}
......
......@@ -36,9 +36,9 @@
:multiple="false"
:hide-selected="false"
label="displayName"
track-by="baseUri"
track-by="uri"
group-values="profiles"
group-label="baseUri"
group-label="uri"
:group-select="false"
:show-labels="false"
:placeholder="
......@@ -100,12 +100,11 @@
<b-spinner variant="primary" />
</b-row>
<FormGenerator
v-else
:key="resourceForCreation.applicationProfile"
:key="resourceForCreation.applicationProfile?.uri"
:fixed-value-mode="fixedValueMode"
:fixed-values="resourceForCreation.fixedValues"
:locale="$root.$i18n.locale"
:selected-shape="resourceForCreation.applicationProfile"
:selected-shape="resourceForCreation.applicationProfile?.uri"
:shapes="applicationProfile"
:class-receiver="getVocabularyInstances"
:user-receiver="async () => user"
......@@ -129,6 +128,7 @@ import type {
} from "../../types";
import type {
ApplicationProfileDto,
ApplicationProfileForResourceCreationDto,
ResourceForCreationDto,
UserDto,
} from "@coscine/api-client/dist/types/Coscine.Api";
......@@ -178,7 +178,9 @@ export default defineComponent({
});
const rules = {
resourceForCreation: {
applicationProfile: { required, url },
applicationProfile: {
uri: [required, url],
},
},
};
const v$ = useVuelidate(rules, state);
......@@ -189,8 +191,9 @@ export default defineComponent({
return {
resourceForCreation: this.value,
fixedValueMode: true,
selectedApplicationProfile: undefined as
selectedApplicationProfile: {} as
| ApplicationProfileDto
| ApplicationProfileForResourceCreationDto
| undefined,
showModal: false,
};
......@@ -268,8 +271,8 @@ export default defineComponent({
for (const applicationProfile of applicationProfiles) {
// Extract AP hierarchy from URIs
const baseUri = applicationProfile.uri || "";
let text = baseUri.replace("https://purl.org/coscine/ap/", "");
const uri = applicationProfile.uri || "";
let text = uri.replace("https://purl.org/coscine/ap/", "");
if (text.charAt(text.length - 1) === "/") {
text = text.substring(0, text.lastIndexOf("/"));
}
......@@ -279,14 +282,14 @@ export default defineComponent({
const groupName = text.substring(0, indexOfSlash).toUpperCase();
const groupEntry = groupedProfiles.find(
(element) => element.baseUri === groupName
(element) => element.uri === groupName
);
if (groupEntry && groupEntry.profiles) {
groupEntry.profiles.push(applicationProfile);
} else {
groupedProfiles.push({
baseUri: groupName,
uri: groupName,
profiles: [applicationProfile],
});
}
......@@ -294,7 +297,7 @@ export default defineComponent({
// The Application Profile does not have a dedicated group
text = text.charAt(0).toUpperCase() + text.slice(1);
groupedProfiles.push({
baseUri: text,
uri: text,
profiles: [applicationProfile],
});
}
......@@ -307,11 +310,9 @@ export default defineComponent({
* Sets the selected application profile.
*/
setSelectedApplicationProfile() {
this.$set(
this.resourceForCreation,
"applicationProfile",
this.selectedApplicationProfile?.uri
);
this.$set(this.resourceForCreation, "applicationProfile", {
uri: this.selectedApplicationProfile?.uri,
} as ApplicationProfileForResourceCreationDto);
this.v$.resourceForCreation.applicationProfile.$touch();
},
},
......
......@@ -59,12 +59,12 @@
</b-row>
<FormGenerator
v-else
:key="resourceForCreation.applicationProfile"
:key="resourceForCreation.applicationProfile?.uri"
:fixed-value-mode="fixedValueMode"
:fixed-values="resourceForCreation.fixedValues"
:locale="$root.$i18n.locale"
:disabled-mode="true"
:selected-shape="resourceForCreation.applicationProfile"
:selected-shape="resourceForCreation.applicationProfile?.uri"
:shapes="applicationProfile"
:class-receiver="getVocabularyInstances"
:user-receiver="async () => user"
......
......@@ -19,7 +19,7 @@ export default {
resourceTypePopover:
"Für weitere Informationen zu Ressourcentypen siehe",
resourceTypePopoverUrl:
"https://docs.coscine.de/de/rdm/resources/about/",
"https://docs.coscine.de/de/resources/types/",
hintTextSSO:
"Die von Ihnen erstellbaren Ressourcentypen werden durch Ihre Zugehörigkeit zu Organisation {organizationName} beeinflusst. Bedienen Sie das Dropdown-Menü, um die für Sie verfügbaren Optionen zu sehen.",
hintTextORCiD:
......@@ -48,7 +48,7 @@ export default {
applicationProfilePopover:
"Für weitere Informationen zu Applikationsprofilen siehe",
applicationProfilePopoverUrl:
"https://docs.coscine.de/de/rdm/metadata/about/",
"https://docs.coscine.de/de/metadata/profiles/",
},
resourceMetadata: {
title: "Schritt 3: @:(form.steps.second)",
......@@ -203,7 +203,7 @@ export default {
"@:(page.settings.overview.persistentId)@:(form.labelSymbol)",
persistentIdPopover:
"Für weitere Informationen zum @:(page.settings.overview.persistentId) siehe",
persistentIdPopoverUrl: "https://docs.coscine.de/de/rdm/publishing/",
persistentIdPopoverUrl: "https://docs.coscine.de/de/resources/pid/",
quota: "Quota",
quotaLabel: "@:(page.settings.overview.quota)@:(form.labelSymbol)",
......@@ -283,7 +283,7 @@ export default {
archivePopover:
"Setzt die Ressource auf read-only. Für weitere Informationen siehe ",
archivePopoverUrl:
"https://docs.coscine.de/de/rdm/resources/edit/#tab-aktionen/",
"https://docs.coscine.de/de/resources/archiving/",
delete: {
modal: {
title: "Ressource wirklich entfernen?",
......@@ -440,14 +440,14 @@ export default {
resourceMetadataPopover:
"Für weitere Informationen zur Sichtbarkeit siehe",
resourceMetadataPopoverUrl:
"https://docs.coscine.de/de/rdm/resources/create/",
"https://docs.coscine.de/de/resources/create/",
resourceLicense: "Lizenz",
resourceLicenseLabel:
"@:(form.resource.resourceLicense)@:(form.labelSymbol)",
resourceLicensePopover: "Für weitere Informationen zu Lizenzen siehe ",
resourceLicensePopoverUrl:
"https://docs.coscine.de/de/rdm/resources/create/",
"https://docs.coscine.de/de/resources/create/",
resourceLicenseSelect: "Bitte wählen Sie eine Lizenz aus",
resourceLicenseHelp:
"Dieses Feld besitzt eine Maximallänge von {maxLength} Zeichen.",
......@@ -457,7 +457,7 @@ export default {
resourceReusePopover:
"Für weitere Informationen zu internen Regeln zur Nachnutzung siehe ",
resourceReusePopoverUrl:
"https://docs.coscine.de/de/rdm/resources/create/",
"https://docs.coscine.de/de/resources/create/",
resourceReuseHelp:
"Dieses Feld besitzt eine Maximallänge von {maxLength} Zeichen.",
},
......
......@@ -19,7 +19,7 @@ export default {
size: "Resource Size:",
resourceTypePopover: "For more information on resource types see",
resourceTypePopoverUrl:
"https://docs.coscine.de/en/rdm/resources/about/",
"https://docs.coscine.de/en/resources/types/",
hintTextSSO:
"The resource types you can create are impacted by your affiliation with {organizationName}. Check the dropdown menu to see the options available to you.",
hintTextORCiD:
......@@ -48,7 +48,7 @@ export default {
applicationProfilePopover:
"For more information on application profiles see",
applicationProfilePopoverUrl:
"https://docs.coscine.de/en/rdm/metadata/about/",
"https://docs.coscine.de/en/metadata/profiles/",
},
resourceMetadata: {
title: "Step 3: @:(form.steps.second)",
......@@ -199,7 +199,7 @@ export default {
"@:(page.settings.overview.persistentId)@:(form.labelSymbol)",
persistentIdPopover:
"For more information on @:(page.settings.overview.persistentId) see",
persistentIdPopoverUrl: "https://docs.coscine.de/en/rdm/publishing/",
persistentIdPopoverUrl: "https://docs.coscine.de/en/resources/pid/",
quota: "Quota",
quotaLabel: "@:(page.settings.overview.quota)@:(form.labelSymbol)",
......@@ -278,7 +278,7 @@ export default {
},
archivePopover: "Sets resource to read-only. For more information see ",
archivePopoverUrl:
"https://docs.coscine.de/en/rdm/resources/edit/#tab-actions/",
"https://docs.coscine.de/en/resources/archiving/",
delete: {
modal: {
title: "Do you really want to delete this resource?",
......@@ -427,14 +427,14 @@ export default {
"@:(form.resource.resourceMetadataVisibility)@:(form.labelSymbol)",
resourceMetadataPopover: "For more information on visibility see",
resourceMetadataPopoverUrl:
"https://docs.coscine.de/en/rdm/resources/create/",
"https://docs.coscine.de/en/resources/create/",
resourceLicense: "License",
resourceLicenseLabel:
"@:(form.resource.resourceLicense)@:(form.labelSymbol)",
resourceLicensePopover: "For more information on licenses see ",
resourceLicensePopoverUrl:
"https://docs.coscine.de/en/rdm/resources/create/",
"https://docs.coscine.de/en/resources/create/",
resourceLicenseSelect: "Please select a License",
resourceLicenseHelp:
"This field can only contain up to {maxLength} characters.",
......@@ -444,7 +444,7 @@ export default {
resourceReusePopover:
"For more information on internal rules for reuse see ",
resourceReusePopoverUrl:
"https://docs.coscine.de/en/rdm/resources/create/",
"https://docs.coscine.de/en/resources/create/",
resourceReuseHelp:
"This field can only contain up to {maxLength} characters.",
},
......
......@@ -88,6 +88,7 @@ import type { Dataset } from "@rdfjs/types";
import type { ResourceCreationTab } from "../types";
import type {
ApplicationProfileDto,
ApplicationProfileForResourceCreationDto,
DisciplineForResourceManipulationDto,
ProjectDto,
ResourceForCreationDto,
......@@ -117,7 +118,9 @@ export default defineComponent({
usageRights: "",
disciplines: [] as DisciplineForResourceManipulationDto[],
visibility: {} as VisibilityForResourceManipulationDto,
applicationProfile: { uri: "" },
applicationProfile: {
uri: "",
} as ApplicationProfileForResourceCreationDto,
resourceTypeId: "",
resourceTypeOptions: {} as ResourceTypeOptionsForCreationDto,
} as ResourceForCreationDto,
......@@ -174,8 +177,10 @@ export default defineComponent({
this.isLoadingFormGenerator = false;
}
},
"resourceForCreation.applicationProfile"() {
"resourceForCreation.applicationProfile.uri"(newValue, _oldValue) {
if (newValue) {
this.getApplicationProfile();
}
},
},
......
......@@ -40,7 +40,7 @@ export interface ResourceCreationTab {
}
export interface GroupedApplicationProfile {
baseUri: string;
uri: string;
profiles: ApplicationProfileDto[];
}
......
......@@ -24,7 +24,7 @@ export const routes: RouteConfig[] = [
];
const router = new VueRouter({
base: '/coscine/apps/uiv2/',
base: "/coscine/apps/uiv2/",
mode: "history",
routes,
});
......
import { defineStore } from "pinia";
import { type CoscineErrorResponse, NotificationState, NotificationToast } from "./types";
import {
type CoscineErrorResponse,
NotificationState,
NotificationToast,
} from "./types";
import i18n from "@/plugins/vue-i18n";
import { getReasonPhrase } from "http-status-codes";
import type { AxiosError } from "axios";
......@@ -44,20 +48,30 @@ export const useNotificationStore = defineStore({
postApiErrorNotification(error: AxiosError) {
let notification: NotificationToast;
if (error.response) {
const coscineErrorResponse = error.response.data as CoscineErrorResponse;
const coscineErrorResponse = error.response
.data as CoscineErrorResponse;
// The request was made and the server responded with a status code that falls out of the range of 2xx
const notificationBody = i18n
.t("toast.apiError.specific.body", {
error: `(${error.response.status}: ${getReasonPhrase(
error.response.status
)})`,
errorId: coscineErrorResponse.data.errorId ?? "",
})
.toString();
notification = {
title: i18n.t("toast.apiError.specific.title").toString(),
body: notificationBody,
body: {
path: "toast.apiError.specific.body",
tag: "span",
placeholders: {
error: {
el: "span",
class: "",
value: `(${error.response.status}: ${getReasonPhrase(
error.response.status
)})`,
},
traceId: {
el: "span",
class: "text-monospace",
value: coscineErrorResponse.traceId ?? "",
},
},
},
variant: error.response.status >= 500 ? "danger" : "warning",
noAutoHide: true,
appendToast: false,
......