11<script setup lang="ts">
2+ import { useHead } from ' #imports'
23import { ref , onMounted , watch , reactive } from ' vue'
34import { useRoute } from ' vue-router'
45import { useRuntimeConfig } from ' #imports'
@@ -9,6 +10,7 @@ import HelpDialog from '@/components/help/HelpDialog.vue'
910import processIdHelp from ' @/components/help/processIdHelp.js'
1011
1112
13+
1214const {
1315 params : { processId }
1416} = useRoute ()
@@ -100,6 +102,7 @@ const typeLabel = (input: any, valForInputId: any) => {
100102 return input ?.schema ?.type || ' literal'
101103}
102104
105+
103106const fetchData = async () => {
104107 try {
105108 data .value = await $fetch (` ${config .public .NUXT_ZOO_BASEURL }/ogc-api/processes/${processId } ` , {
@@ -108,6 +111,8 @@ const fetchData = async () => {
108111 }
109112 })
110113
114+
115+
111116 if (data .value && data .value .inputs ) {
112117 for (const [key, input] of Object .entries (data .value .inputs )) {
113118 if (input .minOccurs === undefined && input .maxOccurs === undefined ) {
@@ -211,6 +216,94 @@ const fetchData = async () => {
211216 }
212217}
213218
219+
220+ watch (
221+ () => data .value ,
222+ (val ) => {
223+ if (! val ) return
224+
225+ const rawMetadata = val .metadata || []
226+
227+ // Extract items by role
228+ const extractByRole = (role ) =>
229+ rawMetadata
230+ .filter (md => md .role === role )
231+ .map (md => md .value )
232+
233+ // Authors (may be multiple)
234+ const authors = extractByRole (" https://schema.org/author" )
235+ .map (a => ({
236+ " @type" : a [" @type" ] || " Person" ,
237+ " name" : a .name || a .fullName || " "
238+ }))
239+
240+ // Contributors
241+ const contributors = extractByRole (" https://schema.org/contributor" )
242+ .map (c => ({
243+ " @type" : c [" @type" ] || " Person" ,
244+ " name" : c .name || c .fullName || " "
245+ }))
246+
247+ // Organizations
248+ const organizations = extractByRole (" https://schema.org/organization" )
249+ .map (org => ({
250+ " @type" : " Organization" ,
251+ " name" : org .name || " "
252+ }))
253+
254+ // Additional metadata (anything NOT author/contributor/organization)
255+ const skipRoles = [
256+ " https://schema.org/author" ,
257+ " https://schema.org/contributor" ,
258+ " https://schema.org/organization"
259+ ]
260+
261+ const additionalProps = rawMetadata
262+ .filter (md => ! skipRoles .includes (md .role ))
263+ .map (md => ({
264+ " @type" : " PropertyValue" ,
265+ " name" : md .role ,
266+ " value" : md .value
267+ }))
268+
269+ // Build final JSON-LD
270+ const jsonLd = {
271+ " @context" : " https://schema.org" ,
272+ " @type" : " SoftwareSourceCode" ,
273+ " name" : val .id ,
274+ " description" : val .description ,
275+ " softwareVersion" : val .version || null ,
276+ " keywords" : val .keywords || [],
277+ " identifier" : processId ,
278+ " url" : ` http://localhost:3058/processes/${processId } ` ,
279+
280+ // New metadata mapping
281+ " author" : authors ,
282+ " contributor" : contributors ,
283+ " provider" : organizations .length ? organizations [0 ] : {
284+ " @type" : " Organization" ,
285+ " name" : " ZOO-Project"
286+ },
287+
288+ " additionalProperty" : additionalProps
289+ }
290+
291+ // Inject JSON-LD into <head>
292+ useHead ({
293+ script: [
294+ {
295+ type: " application/ld+json" ,
296+ children: JSON .stringify (jsonLd )
297+ }
298+ ]
299+ })
300+ },
301+ { immediate: true }
302+ )
303+
304+
305+
306+
214307onMounted (() => {
215308 fetchData ()
216309})
@@ -594,6 +687,65 @@ const removeInputField = (inputId: string, index: number) => {
594687 <div class =" text-subtitle1 text-grey-7" >
595688 {{ data.description }}
596689 </div >
690+ <q-card-section class =" q-pa-md bg-grey-1 rounded-borders q-mt-md" >
691+
692+ <!-- Version -->
693+ <div class =" row q-mb-sm" >
694+ <div class =" col-3 text-grey-7 text-weight-bold" >Version</div >
695+ <div class =" col" >
696+ {{ data.version || '—' }}
697+ </div >
698+ </div >
699+
700+ <!-- Keywords -->
701+ <div class =" row q-mb-sm" >
702+ <div class =" col-3 text-grey-7 text-weight-bold" >Keywords</div >
703+ <div class =" col" >
704+ <span v-if =" data.keywords?.length" >
705+ {{ data.keywords.join(', ') }}
706+ </span >
707+ <span v-else >—</span >
708+ </div >
709+ </div >
710+
711+ <!-- Metadata -->
712+ <div class =" row q-mb-sm" >
713+ <div class =" col-12 text-grey-7 text-weight-bold q-mb-xs" >Additional Metadata</div >
714+
715+ <div v-if =" data.metadata?.length" class =" col-12" >
716+
717+ <div v-for =" (md, index) in data.metadata" :key =" index" class =" q-pa-sm bg-white rounded-borders q-mb-sm shadow-1" >
718+
719+ <!-- Detect Person -->
720+ <div v-if =" md.value && typeof md.value === 'object' && md.value['@type'] === 'Person'" >
721+ <div class =" text-weight-bold text-primary" >👤 {{ md.title || 'Person' }}</div >
722+ <div class =" q-mt-xs" >
723+ <div ><strong >Name:</strong > {{ md.value.name }}</div >
724+ <div v-if =" md.value.role" ><strong >Role:</strong > {{ md.value.role }}</div >
725+ <div v-if =" md.value.email" ><strong >Email:</strong > {{ md.value.email }}</div >
726+ <div v-if =" md.value.affiliation" ><strong >Affiliation:</strong > {{ md.value.affiliation }}</div >
727+ </div >
728+ </div >
729+
730+ <!-- Default metadata -->
731+ <div v-else >
732+ <div class =" text-weight-bold" >{{ md.title || md.role }}</div >
733+ <div class =" text-grey-8" >{{ md.value }}</div >
734+ </div >
735+
736+ </div >
737+
738+ </div >
739+
740+
741+ <div v-else class =" col-12" >
742+ —
743+ </div >
744+ </div >
745+
746+ </q-card-section >
747+
748+
597749 <q-separator class =" q-mt-md" />
598750 </div >
599751
0 commit comments