Skip to content

Commit 0576184

Browse files
committed
Added dev profile pagination #39
* improved loading msg in devs count * added pagination to dev list query vars * added pagination component to dev list * added devs pagination component * added pagination props to pinia
1 parent 7d1cff6 commit 0576184

File tree

5 files changed

+127
-32
lines changed

5 files changed

+127
-32
lines changed

stm_vue_ui/src/components/MatchingDevsCount.vue

Lines changed: 24 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,22 @@
11
<script setup lang="ts">
22
import { devCountForStack } from "@/graphql/queries";
33
import { useQuery } from "@vue/apollo-composable";
4-
import { ref, watch } from "vue";
4+
import { watch } from "vue";
55
import { useQueryStore } from "@/stores/QueryStore";
66
import { numFmt } from "@/formatters";
77
import { debounce } from "throttle-debounce";
88
99
const store = useQueryStore();
1010
11-
/** The number of matched profiles */
12-
const count = ref(0);
13-
14-
/** Refetches GQL data via useQuery.
11+
/** Re-fetches GQL data via useQuery.
1512
* Needed because Apollo's built-in debounce and throttle are not working.
1613
*/
1714
const debounceRefetch = debounce(2000, () => {
1815
// console.log("dev-count debounceRefetch");
1916
refetch(store.stackVar);
2017
});
2118
22-
/** Removes a pkg from the search */
23-
const removePkg = (t: string) => {
24-
store.pkg.delete(t);
25-
};
26-
27-
/** Removes a tech from the search */
28-
const removeTech = (t: string) => {
29-
store.tech.delete(t);
30-
};
31-
19+
// request server data
3220
const { result, loading, error, refetch } = useQuery(
3321
devCountForStack,
3422
store.stackVar,
@@ -39,21 +27,25 @@ const { result, loading, error, refetch } = useQuery(
3927
watch(result, (value) => {
4028
// console.log(value);
4129
if (value) {
42-
count.value = value.devCountForStack;
30+
store.searchProfileCount = value.devCountForStack;
4331
}
4432
});
4533
4634
watch(store.tech, () => {
4735
// console.log(`watch for store.tech: ${value} `);
4836
// tell the template we are waiting for more input
49-
count.value = -1;
37+
store.searchProfileCount = -1;
38+
// reset the current page on search filter change
39+
store.currentPageProfiles = 0;
5040
debounceRefetch();
5141
});
5242
5343
watch(store.pkg, () => {
5444
// console.log(`watch for store.pkg: ${value} `);
5545
// tell the template we are waiting for more input
56-
count.value = -1;
46+
store.searchProfileCount = -1;
47+
// reset the current page on search filter change
48+
store.currentPageProfiles = 0;
5749
debounceRefetch();
5850
});
5951
@@ -63,6 +55,16 @@ watch(store.pkg, () => {
6355
// console.log(tNew);
6456
// console.log(tOld);
6557
// });
58+
59+
/** Removes a pkg from the search */
60+
const removePkg = (t: string) => {
61+
store.pkg.delete(t);
62+
};
63+
64+
/** Removes a tech from the search */
65+
const removeTech = (t: string) => {
66+
store.tech.delete(t);
67+
};
6668
</script>
6769

6870
<template>
@@ -71,8 +73,10 @@ watch(store.pkg, () => {
7173
<span v-else> Matching profiles: </span>
7274

7375
<span v-if="loading"> counting ...</span>
74-
<span v-else-if="!loading && count < 0"> waiting ...</span>
75-
<span v-else>{{ numFmt(count) }}</span>
76+
<span v-else-if="!loading && store.searchProfileCount < 0">
77+
waiting ...</span
78+
>
79+
<span v-else>{{ numFmt(store.searchProfileCount) }}</span>
7680
</h6>
7781
<ul class="text-muted list-inline">
7882
<li class="list-inline-item">Stack:</li>

stm_vue_ui/src/components/MatchingDevsList.vue

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,39 @@
11
<script setup lang="ts">
22
import { devListForStack } from "@/graphql/queries";
3+
import type { devListForStackVars } from "@/graphql/queries";
34
import { useQuery } from "@vue/apollo-composable";
4-
import { useQueryStore } from "@/stores/QueryStore";
5+
import { computed } from "vue";
6+
import { useQueryStore, PROFILES_PER_PAGE } from "@/stores/QueryStore";
57
import DevCard from "./DevCard.vue";
8+
import MatchingDevsPaginationVue from "./MatchingDevsPagination.vue";
9+
10+
/** Adds local data to the stack from store to make a complete set of vars needed for the query. */
11+
const useQueryVars = computed(() => {
12+
let retValue: devListForStackVars = {
13+
stack: [],
14+
pkgs: [],
15+
resultsFrom: store.currentPageProfiles * PROFILES_PER_PAGE,
16+
};
17+
18+
Object.assign(retValue, store.stackVar);
19+
20+
console.log(retValue);
21+
22+
return retValue;
23+
});
624
725
const store = useQueryStore();
826
927
const { result, loading, error } = useQuery(
1028
devListForStack,
11-
store.stackVar,
29+
useQueryVars,
1230
store.defaultApolloOptions
1331
);
1432
</script>
1533

1634
<template>
17-
<h6>
18-
<span v-if="loading"> Loading ...</span>
19-
<span v-else>List of Devs</span>
20-
</h6>
35+
<MatchingDevsPaginationVue v-if="!loading && result && !error" />
36+
<h6 v-if="loading">Loading ...</h6>
2137

2238
<h2
2339
class="pe-md-5 text-muted"
@@ -32,4 +48,5 @@ const { result, loading, error } = useQuery(
3248
<p v-if="error" class="text-danger">
3349
<small>{{ error }}</small>
3450
</p>
51+
<MatchingDevsPaginationVue v-if="!loading && result && !error" />
3552
</template>
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
<script setup lang="ts">
2+
import { computed } from "vue";
3+
import { useQueryStore, PROFILES_PER_PAGE } from "@/stores/QueryStore";
4+
5+
const store = useQueryStore();
6+
7+
const pageNumList = computed(() =>
8+
[
9+
...Array(
10+
Math.min(20, Math.ceil(store.searchProfileCount / PROFILES_PER_PAGE))
11+
).keys(),
12+
].map((x) => ++x)
13+
);
14+
15+
/** store.currentPageProfiles is zero-based and needs to be incremented before use in UI. */
16+
const currentPageProfiles = computed(() => store.currentPageProfiles + 1);
17+
18+
const lastPage = computed(() => {
19+
console.log(
20+
`pages: ${Math.ceil(store.searchProfileCount / PROFILES_PER_PAGE)}`
21+
);
22+
console.log(`current: ${store.currentPageProfiles}`);
23+
24+
return Math.ceil(store.searchProfileCount / PROFILES_PER_PAGE);
25+
});
26+
27+
/** Forces an update with data for the specified page. */
28+
const navigateToPage = (p: number) => {
29+
console.log(`navigating to: ${p}`);
30+
store.currentPageProfiles = Math.max(0, p - 1);
31+
};
32+
</script>
33+
34+
<template>
35+
<div class="row justify-content-center mt-4" v-if="lastPage > 1">
36+
<div class="col-lg-10 col-xxl-8">
37+
<ul class="list-inline text-center">
38+
<li
39+
class="list-inline-item"
40+
v-for="pageNum in pageNumList"
41+
:key="pageNum"
42+
>
43+
<span
44+
@click="navigateToPage(pageNum)"
45+
:class="{
46+
clickable: pageNum != currentPageProfiles,
47+
'text-decoration-underline': pageNum != currentPageProfiles,
48+
}"
49+
>{{ pageNum }}</span
50+
>
51+
</li>
52+
</ul>
53+
</div>
54+
</div>
55+
</template>

stm_vue_ui/src/graphql/queries.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import gql from "graphql-tag";
22

33
// Corresponds to inpTechExperience input block
4-
export interface inpTechExperienceInterface {
4+
export interface inpTechExperience {
55
tech: string;
66
locBand: number;
77
}
@@ -79,9 +79,15 @@ export interface DevListForStack {
7979
};
8080
}
8181

82+
export interface devListForStackVars {
83+
stack: inpTechExperience[];
84+
pkgs: string[];
85+
resultsFrom: number;
86+
}
87+
8288
export const devListForStack = gql`
83-
query ($stack: [TechExperience!]!, $pkgs: [String!]!) {
84-
devListForStack(stack: $stack, pkgs: $pkgs) {
89+
query ($stack: [TechExperience!]!, $pkgs: [String!]!, $resultsFrom: Int!) {
90+
devListForStack(stack: $stack, pkgs: $pkgs, resultsFrom: $resultsFrom) {
8591
login
8692
name
8793
email

stm_vue_ui/src/stores/QueryStore.ts

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { defineStore } from "pinia";
2-
import type { inpTechExperienceInterface } from "@/graphql/queries";
2+
import type { inpTechExperience } from "@/graphql/queries";
33
import type { UseQueryOptions } from "@vue/apollo-composable";
44

55
/** Amount of experience required for a particular technology. */
@@ -29,6 +29,10 @@ const defaultApolloOptions: UseQueryOptions = {
2929
notifyOnNetworkStatusChange: true,
3030
};
3131

32+
/** The number of profiles per search page.
33+
* This value must synchronized with the server to correctly split the results into pages */
34+
export const PROFILES_PER_PAGE = 5;
35+
3236
/** A shared app store based on Pinia. */
3337
export const useQueryStore = defineStore({
3438
id: "query",
@@ -48,8 +52,17 @@ export const useQueryStore = defineStore({
4852
/** A search string typed into the search box by the user. */
4953
searchFilter: "",
5054

55+
/** How many profiles match the current search filter. -1 for no data, 0 - none, >0 - profiles found */
56+
searchProfileCount: -1,
57+
5158
/** Name of the currently active tab. Defaults to Search. */
5259
activeSearchTab: SearchTabNames.Search,
60+
61+
/** Zero-based, used to calculate query results offset for Profiles tab */
62+
currentPageProfiles: 0,
63+
64+
/** Zero-based, used to calculate query results offset for Shortlist tab */
65+
currentPageShortlist: 0,
5366
}),
5467

5568
getters: {
@@ -58,10 +71,10 @@ export const useQueryStore = defineStore({
5871

5972
/** GQL variables for useQuery: converts the current search criteria to GQL format */
6073
stackVar: (state) => {
61-
const stack = new Array<inpTechExperienceInterface>();
74+
const stack = new Array<inpTechExperience>();
6275

6376
state.tech.forEach((v, k) => {
64-
stack.push({ tech: k, locBand: v.loc } as inpTechExperienceInterface);
77+
stack.push({ tech: k, locBand: v.loc } as inpTechExperience);
6578
});
6679

6780
const x = {

0 commit comments

Comments
 (0)