Skip to content

Commit

Permalink
Added pagination, updated profiles data structure, search updates
Browse files Browse the repository at this point in the history
  • Loading branch information
jamiefeiss committed Apr 20, 2023
1 parent 0892479 commit 0a93cfd
Show file tree
Hide file tree
Showing 9 changed files with 216 additions and 56 deletions.
22 changes: 13 additions & 9 deletions src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -63,17 +63,21 @@ onMounted(() => {
profParseIntoStore(profData.value);
// get list of profiles
let profileUris: string[] = [];
let profileUris: {[uri: string]: {
token: string;
link: string;
}} = {};
profStore.value.forSubjects(subject => {
profStore.value.forEach(q => {
if (q.predicate.value === profQname("prez:link")) {
profileUris.push(`${apiBaseUrl}${q.object.value}`);
profileUris[q.subject.value] = {
token: q.object.value.replace("/profiles/", ""),
link: `${apiBaseUrl}${q.object.value}`
}
}, subject, null, null, null);
}, subject, namedNode(profQname("prez:link")), null, null);
}, namedNode(profQname("a")), namedNode(profQname("prof:Profile")), null);
// promise.all request for each profile in parallel
Promise.all(profileUris.map(uri => fetch(uri).then(r => r.text()))).then(values => {
Promise.all(Object.values(profileUris).map(p => fetch(p.link).then(r => r.text()))).then(values => {
// parse all results into store
values.forEach(value => {
combinedParseIntoStore(value)
Expand All @@ -84,7 +88,7 @@ onMounted(() => {
combinedStore.value.forSubjects(subject => {
let p: Profile = {
namespace: subject.id,
token: "",
token: profileUris[subject.id].token,
title: "",
description: "",
mediatypes: [],
Expand All @@ -95,8 +99,8 @@ onMounted(() => {
p.title = q.object.value;
} else if (q.predicate.value === combinedQname("dcterms:description")) {
p.description = q.object.value;
} else if (q.predicate.value === combinedQname("dcterms:identifier")) {
p.token = q.object.value;
// } else if (q.predicate.value === combinedQname("dcterms:identifier")) {
// p.token = q.object.value;
} else if (q.predicate.value === combinedQname("altr-ext:hasResourceFormat")) {
p.mediatypes.push(q.object.value);
} else if (q.predicate.value === combinedQname("altr-ext:hasDefaultResourceFormat")) {
Expand All @@ -111,7 +115,7 @@ onMounted(() => {
profs.push(p);
}, namedNode(combinedQname("a")), namedNode(combinedQname("prof:Profile")), null);
ui.profiles = profs.reduce<{[token: string]: Profile}>((obj, prof) => (obj[prof.token] = prof, obj), {}); // {"dcat": {...}, "vocpub": {...}, ...}
ui.profiles = profs.reduce<{[namespace: string]: Profile}>((obj, prof) => (obj[prof.namespace] = prof, obj), {}); // {uri: {...}, ...}
});
});
}
Expand Down
1 change: 1 addition & 0 deletions src/components/ItemList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ const props = defineProps<{
display: flex;
flex-direction: column;
gap: 12px;
margin-bottom: 12px;;
a.list-item {
display: flex;
Expand Down
83 changes: 83 additions & 0 deletions src/components/PaginationComponent.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
<script lang="ts" setup>
import { ref, computed, watch } from "vue";
import { RouterLink } from "vue-router";
import router from "@/router";
const PER_PAGE = 10;
const props = defineProps<{
totalCount: number;
perPage?: number;
currentPage: number;
url: string;
}>();
const pageDropdown = ref(props.currentPage);
const totalPages = computed(() => {
const pageCount = props.perPage || PER_PAGE;
return Math.ceil(props.totalCount / pageCount);
});
watch(pageDropdown, (newValue, oldValue) => {
if (newValue !== oldValue) {
router.push(`${props.url}?page=${newValue}${props.perPage ? `&per_page=${props.perPage}` : ''}`);
}
});
</script>

<template>
<div class="pagination">
<RouterLink
v-if="props.currentPage > 2"
:to="`${props.url}?page=1${props.perPage ? `&per_page=${props.perPage}` : ''}`"
class="btn outline pagination-btn"
>
1
</RouterLink>
<div v-if="props.currentPage > 3"><i class="fa-regular fa-ellipsis"></i></div>
<RouterLink
v-if="props.currentPage > 1"
:to="`${props.url}?page=${props.currentPage - 1}${props.perPage ? `&per_page=${props.perPage}` : ''}`"
class="btn outline pagination-btn"
>
{{ props.currentPage - 1 }}
</RouterLink>
<select class="pagination-btn" name="" id="" title="Select page" v-model="pageDropdown">
<option v-for="pageNo in [...Array(totalPages)].map((_, index) => index + 1)" :value="pageNo">{{ pageNo }}</option>
</select>
<RouterLink
v-if="props.currentPage < totalPages"
:to="`${props.url}?page=${props.currentPage + 1}${props.perPage ? `&per_page=${props.perPage}` : ''}`"
class="btn outline pagination-btn"
>
{{ props.currentPage + 1 }}
</RouterLink>
<div v-if="props.currentPage < totalPages - 2"><i class="fa-regular fa-ellipsis"></i></div>
<RouterLink
v-if="props.currentPage < totalPages - 1"
:to="`${props.url}?page=${totalPages}${props.perPage ? `&per_page=${props.perPage}` : ''}`"
class="btn outline pagination-btn"
>
{{ totalPages }}
</RouterLink>
</div>
</template>

<style lang="scss" scoped>
.pagination {
display: flex;
flex-direction: row;
gap: 8px;
margin-top: auto;
align-items: center;
justify-content: center;
select.pagination-btn {
color: var(--primary);
border: 1px solid var(--primary);
font-size: 16px;
padding: 6px 8px;
}
}
</style>
27 changes: 16 additions & 11 deletions src/components/search/AdvancedSearch.vue
Original file line number Diff line number Diff line change
Expand Up @@ -73,22 +73,26 @@ function camelToTitleCase(s: string): string {
<button v-if="!expanded" type="submit" class="btn submit-btn"><i class="fa-regular fa-magnifying-glass"></i></button>
</div>
<div class="search-below">
<select v-if="!props.flavour" name="prez" id="" class="search-type" v-model="prez">
<option value="all">All</option>
<option v-for="prezFlavour in enabledPrezs" :value="prezFlavour">{{ prezFlavour }}</option>
</select>
<div v-if="!props.flavour">
<label for="subsystem">Subsystem</label>
<br/>
<select name="prez" id="" class="subsystem" v-model="prez">
<option value="all">All</option>
<option v-for="prezFlavour in enabledPrezs" :value="prezFlavour">{{ prezFlavour }}</option>
</select>
</div>
<div v-if="prez !== 'all'">
<label for="search-method">Search Method</label>
<br/>
<select name="search-method" id="search-method" v-model="searchMethod">
<option v-for="method in ui.searchMethods[prez]" :value="method">{{ camelToTitleCase(method) }}</option>
</select>
</div>
<button v-if="prez !== 'all'" type="button" class="collapse-btn" @click.prevent="expanded = !expanded">
<template v-if="expanded">Collapse <i class="fa-regular fa-chevron-up"></i></template>
<template v-else>Expand <i class="fa-regular fa-chevron-down"></i></template>
</button>
</div>
<div v-if="prez !== 'all' && expanded">
<label for="search-method">Search Method</label>
<br/>
<select name="search-method" id="search-method" v-model="searchMethod">
<option v-for="method in ui.searchMethods[prez]" :value="method">{{ camelToTitleCase(method) }}</option>
</select>
</div>
<template v-if="!props.flavour || (props.flavour === 'CatPrez')">
<div v-show="prez === 'CatPrez' && expanded">
<CatPrezSearch @updateOptions="searchOptions = $event" :defaultSelected="props.query?.catalog" />
Expand Down Expand Up @@ -188,6 +192,7 @@ function camelToTitleCase(s: string): string {
display: flex;
flex-direction: row;
align-items: flex-start;
gap: 12px;
.collapse-btn {
margin-left: auto;
Expand Down
4 changes: 1 addition & 3 deletions src/router/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,6 @@ const router = createRouter({
props: {
title: "Datasets",
class: "dcat:Dataset",
itemPred: "rdfs:member",
childButton: { name: "Collections", url: "/collections" },
enableSearch: true,
content: `A list of <a href="http://www.w3.org/ns/dcat#Dataset" target="_blank" rel="noopener noreferrer">dcat:Datasets</a>.`
Expand All @@ -142,7 +141,6 @@ const router = createRouter({
props: {
title: "Feature Collections",
parentType: "dcat:Dataset",
itemPred: "rdfs:member",
childButton: { name: "Features", url: "/items" },
enableSearch: true,
content: `A list of <a href="http://www.opengis.net/ont/geosparql#FeatureCollection" target="_blank" rel="noopener noreferrer">geo:FeatureCollections</a>.`
Expand All @@ -166,7 +164,7 @@ const router = createRouter({
props: {
title: "Features",
parentType: "geo:FeatureCollection",
itemPred: "rdfs:member",
enableSearch: true,
content: `A list of <a href="http://www.opengis.net/ont/geosparql#Feature" target="_blank" rel="noopener noreferrer">geo:Features</a>.`
}
},
Expand Down
6 changes: 3 additions & 3 deletions src/stores/ui.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,13 @@ export const useUiStore = defineStore("ui", () => {
// actions

// get profiles from local storage
if (localStorage.getItem("profiles")) {
profiles.value = JSON.parse(localStorage.getItem("profiles") || "");
if (sessionStorage.getItem("profiles")) {
profiles.value = JSON.parse(sessionStorage.getItem("profiles") || "");
}

// watch & save profiles to local storage
watch(profiles, (state) => {
localStorage.setItem("profiles", JSON.stringify(state));
sessionStorage.setItem("profiles", JSON.stringify(state));
}, { deep: true });

return {
Expand Down
76 changes: 60 additions & 16 deletions src/views/ItemListView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import ItemList from "@/components/ItemList.vue";
import AdvancedSearch from "@/components/search/AdvancedSearch.vue";
import ProfilesTable from "@/components/ProfilesTable.vue";
import ErrorMessage from "@/components/ErrorMessage.vue";
import PaginationComponent from "@/components/PaginationComponent.vue";
const { namedNode } = DataFactory;
Expand All @@ -22,14 +23,14 @@ const { data, profiles, loading, error, doRequest } = useGetRequest();
const props = defineProps<{
title: string;
parentType?: string;
class: string;
class?: string;
childButton?: { name: string, url: string }; // undefined or link to children (/collections or /items)
enableSearch?: boolean;
content?: string;
}>();
const items = ref<ListItem[]>([]);
const count = ref(0);
const isAltView = ref(false);
const flavour = computed<PrezFlavour | undefined>(() => {
Expand All @@ -44,6 +45,14 @@ const flavour = computed<PrezFlavour | undefined>(() => {
}
});
const currentPageNumber = computed(() => {
if (route.query && route.query.page) {
return parseInt(route.query.page as string);
} else {
return 1;
}
});
function getBreadcrumbs(): Breadcrumb[] {
let breadcrumbs: Breadcrumb[] = [];
if (flavour.value) {
Expand Down Expand Up @@ -73,7 +82,25 @@ function getSearchDefaults(): {[key: string]: string} { // need IRI of parent to
}
onMounted(() => {
doRequest(`${apiBaseUrl}${route.path}`, () => {
let paginationParams = "";
if (route.query) {
if (route.query.page) {
paginationParams += `?page=${route.query.page}`;
}
if (route.query.per_page) {
if (route.query.page) {
paginationParams += "&";
} else {
paginationParams += "?";
}
paginationParams += `per_page=${route.query.per_page}`;
}
}
doRequest(`${apiBaseUrl}${route.path}${paginationParams}`, () => {
const defaultProfile = profiles.value.find(p => p.default)!;
// check profile, potentially show Alt profiles page or redirect to API endpoint
Expand All @@ -97,8 +124,8 @@ onMounted(() => {
let labelPred = qname("rdfs:label");
let descPred = qname("dcterms:description");
if (Object.keys(ui.profiles).includes(defaultProfile.token)) {
const currentProfile = ui.profiles[defaultProfile.token];
if (Object.keys(ui.profiles).includes(defaultProfile.uri)) {
const currentProfile = ui.profiles[defaultProfile.uri];
// get profile-specific label & description predicates if available
if (currentProfile.labelPredicate) {
Expand All @@ -112,9 +139,12 @@ onMounted(() => {
let nodeList: Quad_Subject[] | Quad_Object[] = [];
if (props.class) {
count.value = parseInt(store.value.getObjects(namedNode(qname(props.class)), namedNode(qname("prez:count")), null)[0].value);
nodeList = store.value.getSubjects(namedNode(qname("a")), namedNode(qname(props.class)), null);
} else {
nodeList = store.value.getObjects(null, namedNode(qname("rdfs:member")), null);
const quad = store.value.getQuads(null, namedNode(qname("prez:count")), null, null)[0];
count.value = parseInt(quad.object.value);
nodeList = store.value.getObjects(quad.subject, namedNode(qname("rdfs:member")), null);
}
nodeList.forEach(member => {
Expand All @@ -133,6 +163,18 @@ onMounted(() => {
items.value.push(c);
});
items.value.sort((a, b) => {
if (a.title && b.title) {
return a.title.localeCompare(b.title);
} else if (a.title) {
return -1;
} else if (b.title) {
return 1;
} else {
return a.iri.localeCompare(b.iri);
}
});
if (isAltView.value) {
ui.rightNavConfig = { enabled: false };
} else {
Expand All @@ -159,16 +201,18 @@ onMounted(() => {
<template v-else>
<h1 class="page-title">{{ props.title }}</h1>
<p v-if="props.content" v-html="props.content"></p>
<div>
<template v-if="error">
<ErrorMessage :message="error" />
</template>
<template v-else-if="loading">
<i class="fa-regular fa-spinner-third fa-spin"></i> Loading...
</template>
<ItemList v-else-if="items.length > 0" :items="items" :childName="props.childButton?.name" :childLink="props.childButton?.url" />
<template v-else>No {{ props.title }} found.</template>
</div>
<p v-if="items.length > 0">Showing {{ items.length }} of {{ count }} items.</p>
<template v-if="error">
<ErrorMessage :message="error" />
</template>
<template v-else-if="loading">
<i class="fa-regular fa-spinner-third fa-spin"></i> Loading...
</template>
<template v-else-if="items.length > 0">
<ItemList :items="items" :childName="props.childButton?.name" :childLink="props.childButton?.url" />
<PaginationComponent :url="route.path" :totalCount="count" :currentPage="currentPageNumber" />
</template>
<template v-else>No {{ props.title }} found.</template>
<Teleport v-if="props.enableSearch" to="#right-bar-content">
<AdvancedSearch :flavour="flavour" :query="getSearchDefaults()" />
</Teleport>
Expand Down
Loading

0 comments on commit 0a93cfd

Please sign in to comment.