Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Persist dashboard filters in query params #3406

Merged
merged 26 commits into from
Aug 15, 2023
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
30477fa
persist dashboard filters in query params
DeNic0la Apr 9, 2023
f46a437
show only id in url params and add period
DeNic0la Apr 9, 2023
1f8a558
add Dashboard test
DeNic0la Apr 9, 2023
5d35136
add Unittests for implemented functionality
DeNic0la Apr 9, 2023
1e4a2d0
Merge remote-tracking branch 'original/devel' into dashboard-filters-…
DeNic0la Jun 4, 2023
a40967f
move formatting utilities to helper
DeNic0la Jun 5, 2023
5acb42d
fix jsdoc
DeNic0la Jun 5, 2023
d7f9eed
inline variable
DeNic0la Jun 5, 2023
0daa6b7
Merge pull request #1 from DeNic0la/devel
DeNic0la Jun 5, 2023
cc76582
Merge pull request #2 from DeNic0la/devel
DeNic0la Jun 11, 2023
e57d0cc
fix: dont redirect after unload
DeNic0la Jun 11, 2023
501d962
remove debug info and wrap all promises into one
DeNic0la Jun 11, 2023
17ecc42
update unittests
DeNic0la Jun 11, 2023
27857a9
minor fix
DeNic0la Jun 11, 2023
1ed96ba
minor fix
DeNic0la Jun 11, 2023
aecb048
simplify and lint
DeNic0la Jun 11, 2023
14b7788
Merge branch 'devel' into dashboard-filters-in-url-#3155
DeNic0la Aug 1, 2023
0536fdd
use window history to persist query params
DeNic0la Aug 1, 2023
0663adf
improve querySyncHelper and add unit tests
DeNic0la Aug 2, 2023
ede3e68
refactor and add tests
DeNic0la Aug 3, 2023
9719413
refactor and add tests
DeNic0la Aug 3, 2023
fbc0250
minor fix
DeNic0la Aug 3, 2023
46d4a10
Add Cypress-Tests and refactor
DeNic0la Aug 6, 2023
80d1eb3
Lint and fix
DeNic0la Aug 6, 2023
00938d1
Merge branch 'devel' into dashboard-filters-in-url-#3155
DeNic0la Aug 6, 2023
8aee5d2
fix
DeNic0la Aug 6, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions frontend/src/helpers/formatHalHelper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/**
* The Type of Object in qualified hal format for api
* @typedef {'categories'|'camp_collaborations'|'periods'} HalType
*/
/**
* Format of the Hal Uri
* @typedef {`/${HalType}/${string}`} HalUri
*/
/**
* Formats the Hal-Object-URI to the id
* @param uri {HalUri} the ID (from the hal object)
* @return {string} the ID of the uri
*/
export const halUriToId = (uri) => {
return uri.substring(uri.lastIndexOf('/') + 1)
}
/**
* Formats an id and a Datatype to a hal-object-id
* @param dataType {HalType} the datatype of the object
* @param id {string} the id of the object
* @return {HalUri}
*/
export const idToHalUri = (dataType, id) => {
return `/${dataType}/${id}`
}
10 changes: 0 additions & 10 deletions frontend/src/mixins/formatHalHelperMixin.js

This file was deleted.

81 changes: 67 additions & 14 deletions frontend/src/views/camp/Dashboard.vue
Original file line number Diff line number Diff line change
Expand Up @@ -180,15 +180,30 @@ import BooleanFilter from '@/components/dashboard/BooleanFilter.vue'
import SelectFilter from '@/components/dashboard/SelectFilter.vue'
import ActivityRow from '@/components/dashboard/ActivityRow.vue'
import FilterDivider from '@/components/dashboard/FilterDivider.vue'
import { keyBy, groupBy, mapValues } from 'lodash'
import { groupBy, keyBy, mapValues } from 'lodash'
import campCollaborationDisplayName from '../../common/helpers/campCollaborationDisplayName.js'
import { dateHelperUTCFormatted } from '@/mixins/dateHelperUTCFormatted.js'
import TextAlignBaseline from '@/components/layout/TextAlignBaseline.vue'
import { FormatHalHelperMixin } from '@/mixins/formatHalHelperMixin'
import { halUriToId, idToHalUri } from '@/helpers/formatHalHelper.js'
import { mapGetters } from 'vuex'

function filterEquals(arr1, arr2) {
return JSON.stringify(arr1) === JSON.stringify(arr2)
}
/**
* Allowed Url param keys
* @typedef {'period'|'responsible'|'category'} UrlParamKey
*/
/**
* The Allowed Url parameter keys
* @type {UrlParamKey[]} UrlParamKeys
*/
const urlParamKeys = ['period', 'responsible', 'category']

/**
* Map for url param keys to hal types
* @type {{(typeof urlParamKeys[number]):HalType }}
*/
const URL_PARAM_TO_HAL_TYPE = {
category: 'categories',
responsible: 'camp_collaborations',
Expand All @@ -206,14 +221,16 @@ export default {
ContentCard,
UserAvatar,
},
mixins: [dateHelperUTCFormatted, FormatHalHelperMixin],
mixins: [dateHelperUTCFormatted],
props: {
camp: { type: Function, required: true },
},
data() {
return {
loggedInUser: null,
loading: true,
/**
* Contains the Values declared initially. In the unverified values from the Url params will be written in the filter.url
* @type {{period:null|HalUri,responsible:HalUri[],category: HalUri[],url?:{period?:null|HalUri,responsible?:HalUri[],category?: HalUri[]}}} Filter*/
filter: {
period: null,
responsible: [],
Expand Down Expand Up @@ -317,10 +334,13 @@ export default {
.filter(([_, value]) => !!value)
.map(([key, value]) => [
key,
Array.isArray(value) ? value.map((e) => this.toId(e)) : this.toId(value),
Array.isArray(value) ? value.map((e) => halUriToId(e)) : halUriToId(value),
])
)
},
...mapGetters({
loggedInUser: 'getLoggedInUser',
}),
},
watch: {
'filter.category': 'persistRouterState',
Expand All @@ -330,36 +350,69 @@ export default {
async mounted() {
this.api.reload(this.camp())

const [loggedInUser] = await Promise.all([
this.$auth.loadUser(),
this.camp().periods()._meta.load,
const loadingDataPromise = Promise.all([
this.loggedInUser._meta.load,
this.camp().activities()._meta.load,
this.camp().categories()._meta.load,
])

this.loggedInUser = loggedInUser
// Once camp data is loaded validate and map values from url param to filter
await Promise.all([
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a specific reason we need 2 separate calls for Promise.all? Or could we put all the loading promises into one Promise.all and await this one?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nope, that's leftover code from when I tried some performance optimisation.

this.camp().categories()._meta.load,
this.camp().periods()._meta.load,
this.camp().campCollaborations()._meta.load,
loadingDataPromise,
]).then(([categories, periods, collaborators]) => {
const availableCategoryIds = categories.allItems.map((value) => value._meta.self)
const availablePeriodsIds = periods.allItems.map((value) => value._meta.self)
const availableCollaboratorIds = collaborators.allItems.map(
(value) => value._meta.self
)

const category = (this.filter.url?.category ?? []).filter((value) =>
availableCategoryIds.includes(value)
)
const responsible = (this.filter.url?.responsible ?? []).filter((value) =>
availableCollaboratorIds.includes(value)
)
const period = availablePeriodsIds.includes(this.filter.url?.period)
? this.filter.url.period
: null

this.filter = {
category,
responsible,
period,
}
})

this.loading = false
},
beforeMount() {
let fromUrl = Object.fromEntries(
this.filter.url = Object.fromEntries(
/**
* @type {[UrlParamKey,(HalUri[])|HalUri]}
*/
Object.entries(this.$route.query)
.filter(([key, value]) => urlParamKeys.includes(key) && !!value)
.map(([key, value]) => [key, value, URL_PARAM_TO_HAL_TYPE[key]])
.map(([key, value, type]) => {
if (typeof value === 'string') {
let halUriValue =
key === 'period' ? this.toUri(type, value) : [this.toUri(type, value)]
key === 'period' ? idToHalUri(type, value) : [idToHalUri(type, value)]
return [key, halUriValue]
}
if (Array.isArray(value)) {
let uriValues = value
.filter((entry) => !!entry)
.map((entry) => this.toUri(type, entry))
.map((entry) => idToHalUri(type, entry))
return [key, uriValues]
}
return [key, null]
})
.filter(([_, value]) => {
return !!value
})
)
this.filter = { ...this.filter, ...fromUrl }
},
methods: {
campCollaborationDisplayName(campCollaboration) {
Expand Down
5 changes: 3 additions & 2 deletions frontend/src/views/camp/__tests__/Dashboard.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ describe('Dashboard view', () => {
}
await flushPromises()

expect(vueWrapper.vm.$data.filter).toMatchObject(expectedFilterValues)
expect(vueWrapper.vm.$data.filter.url).toMatchObject(expectedFilterValues)
})
it('Parses filter value into URL', async () => {
const filter = {
Expand All @@ -40,9 +40,10 @@ describe('Dashboard view', () => {
filter,
})
const options = { ...DEFAULT_DASHBOARD_OPTIONS(), data }
shallowMount(Dashboard, options)
const wrapper = shallowMount(Dashboard, options)

await flushPromises()
wrapper.vm.persistRouterState()

const expectedURLParams = {
responsible: ['fe6557a4b89f'],
Expand Down