Compare commits

...

5 Commits

Author SHA1 Message Date
D. Berge
6b6f5ab511 Link from group summary to individual projects 2025-08-20 12:06:20 +02:00
D. Berge
7d8c78648d Don't request summaries in ProjectList.
Those will be populated directly by Vuex.
2025-08-20 12:05:44 +02:00
D. Berge
faf7e9c98f Try to improve responsiveness when refreshing project list 2025-08-20 12:05:05 +02:00
D. Berge
abf2709705 Expand groups router definition 2025-08-20 12:04:26 +02:00
D. Berge
f5dfafd85a Make event handler more specific 2025-08-20 12:03:53 +02:00
5 changed files with 66 additions and 41 deletions

View File

@@ -85,7 +85,9 @@ export default {
}, },
handleProject (context, {payload}) { handleProject (context, {payload}) {
if (payload?.table == "public") {
this.refreshProjects(); this.refreshProjects();
}
}, },
registerNotificationHandlers () { registerNotificationHandlers () {

View File

@@ -211,7 +211,7 @@ Vue.use(VueRouter)
component: GroupList, component: GroupList,
meta: { meta: {
breadcrumbs: [ breadcrumbs: [
{ text: "Groups", href: "/groups", disabled: true } { text: "Comparisons", href: "/groups", disabled: true }
], ],
appBarExtension: { appBarExtension: {
// component: DougalAppBarExtensionProjectList // component: DougalAppBarExtensionProjectList
@@ -230,8 +230,22 @@ Vue.use(VueRouter)
component: Group, component: Group,
meta: { meta: {
breadcrumbs: [ breadcrumbs: [
{ text: "Groups", href: "/groups" }, { text: "Comparisons", href: "/groups" },
{ text: (ctx) => ctx.$route.params.group }
/*
{
text: (ctx) => ctx.$store.state.project.projectName || "…",
href: (ctx) => `/projects/${ctx.$store.state.project.projectId || ctx.$route.params.project || ""}/`,
title: (ctx) => Object.entries(ctx.$store.getters.projectConfiguration?.organisations ?? {}).map( ([org, ops]) => `* ${org}: ${Object.entries(ops).filter( ([k, v]) => v ).map( ([k, v]) => k ).join(", ")}`).join("\n"),
organisations: (ctx) => ctx.$store.getters.projectConfiguration?.organisations ?? {}
}
*/
], ],
/*
appBarExtension: {
component: DougalAppBarExtensionGroup
}
*/
}, },
children: [ children: [
] ]

View File

@@ -5,6 +5,17 @@ import { duration_to_ms, ms_to_duration, normalise_duration, add_durations } fro
*/ */
async function refreshProjects ({commit, dispatch, state, rootState}) { async function refreshProjects ({commit, dispatch, state, rootState}) {
async function getSummary (project) {
const url = `/project/${project.pid}/summary`;
const init = {};
const summary = await dispatch('api', [url, init, null, {silent:true}]);
if (summary) {
return {...project, ...summary};
} else {
return project;
}
}
if (state.loading) { if (state.loading) {
commit('abortProjectsLoading'); commit('abortProjectsLoading');
} }
@@ -20,23 +31,22 @@ async function refreshProjects ({commit, dispatch, state, rootState}) {
const res = await dispatch('api', [url, init, null, {silent:true}]); const res = await dispatch('api', [url, init, null, {silent:true}]);
if (res) { if (res) {
for (let index in res) {
const project = res[index];
if (!project.pid) { let projects;
console.warn("Project has no Project ID!");
continue; if (res.some( project => project.pid == null )) {
console.warn("At least one project found with no PID!");
projects = res.filter( project => project.pid != null );
} else {
projects = res;
} }
const url = `/project/${project.pid}/summary`; commit('setProjects', projects); // First without summaries
const init = {};
const summary = await dispatch('api', [url, init, null, {silent:true}]);
if (summary) {
res[index] = {...project, ...summary};
}
}
commit('setProjects', res);
commit('setProjectsTimestamp', tstamp); commit('setProjectsTimestamp', tstamp);
projects = await Promise.all(projects.map( getSummary ));
commit('setProjects', projects); // Then with summaries
} }
commit('clearProjectsLoading'); commit('clearProjectsLoading');
dispatch('prepareGroups'); dispatch('prepareGroups');

View File

@@ -48,7 +48,13 @@
</template> </template>
<template v-slot:item.pid="{item, value}"> <template v-slot:item.pid="{item, value}">
<v-chip label small outlined>{{ value }}</v-chip> <v-chip
label
small
outlined
:href="`/projects/${item.pid}`"
:color="!item.archived ? 'primary' : ''"
>{{ value }}</v-chip>
</template> </template>
<template v-slot:item.fsp="{item, value}"> <template v-slot:item.fsp="{item, value}">

View File

@@ -189,19 +189,20 @@ export default {
...mapGetters(['loading', 'projects']) ...mapGetters(['loading', 'projects'])
}, },
watch: {
async projects () {
await this.load();
}
},
methods: { methods: {
async list () { async list () {
this.items = [...this.projects]; this.items = [...this.projects];
}, },
async summary (item) {
const details = await this.api([`/project/${item.pid}/summary`]);
if (details) {
return Object.assign({}, details, item);
}
},
title (item) { title (item) {
if (item.organisations) { if (item.organisations) {
return "Access:\n" + Object.entries(item.organisations).map( org => return "Access:\n" + Object.entries(item.organisations).map( org =>
@@ -212,30 +213,22 @@ export default {
}, },
async load () { async load () {
await this.refreshProjects(); if (!this.projects.length) {
await this.list(); this.refreshProjects();
const promises = [];
for (const key in this.items) {
const item = this.items[key];
const promise = this.summary(item)
.then( expanded => {
if (expanded) {
this.$set(this.items, key, expanded);
} }
}); await this.list();
promises.push(promise); },
handlerLoad (context, {payload}) {
if (payload?.table == "public") {
this.load();
} }
}, },
registerNotificationHandlers () { registerNotificationHandlers () {
this.$store.dispatch('registerHandler', { this.$store.dispatch('registerHandler', {
table: 'project`', table: 'project`',
handler: this.handlerLoad
handler: (context, message) => {
if (message.payload?.table == "public") {
this.load();
}
}
}); });
}, },