From b4d155f9ae61bbaff1ef2903891b48ba3dfcee7f Mon Sep 17 00:00:00 2001 From: "Petter H. Juliussen" Date: Fri, 18 Aug 2023 14:23:56 +0200 Subject: [PATCH 001/102] Add pane layout components --- src/components/layout/PaneLayout.vue | 75 +++++++++++++++++++++++ src/components/panes/PaneWrapper.vue | 91 ++++++++++++++++++++++++++++ 2 files changed, 166 insertions(+) create mode 100644 src/components/layout/PaneLayout.vue create mode 100644 src/components/panes/PaneWrapper.vue diff --git a/src/components/layout/PaneLayout.vue b/src/components/layout/PaneLayout.vue new file mode 100644 index 000000000..e04a843a6 --- /dev/null +++ b/src/components/layout/PaneLayout.vue @@ -0,0 +1,75 @@ + + + + + diff --git a/src/components/panes/PaneWrapper.vue b/src/components/panes/PaneWrapper.vue new file mode 100644 index 000000000..abfd25162 --- /dev/null +++ b/src/components/panes/PaneWrapper.vue @@ -0,0 +1,91 @@ + + + + + From 16205558c973984371984ef2cc1a57604bbbd17e Mon Sep 17 00:00:00 2001 From: "Petter H. Juliussen" Date: Thu, 24 Aug 2023 11:42:11 +0200 Subject: [PATCH 002/102] Organize new OKR related state in separate store module --- src/store/index.js | 50 ++--------------- src/store/modules/okrs.js | 113 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 117 insertions(+), 46 deletions(-) create mode 100644 src/store/modules/okrs.js diff --git a/src/store/index.js b/src/store/index.js index 0e80f0273..20fc7d4cf 100644 --- a/src/store/index.js +++ b/src/store/index.js @@ -6,6 +6,7 @@ import { sortByLocale } from '@/store/actions/actionUtils'; import defaultPreferences from '@/db/User/defaultPreferences'; import moduleActions from './actions'; +import okrsModule from './modules/okrs'; Vue.use(Vuex); @@ -139,20 +140,6 @@ export const storeGetters = { id: o.id, })); }, - - /** - * Return selected objectives for `state.activeItem`. - */ - selectedObjectives: (state, getters) => { - if (!state.activeItem || !state.selectedObjectives) { - return []; - } - const objectiveIds = state.selectedObjectives[state.activeItem.id] || []; - if (!objectiveIds.length) { - return []; - } - return getters.objectivesWithID.filter((o) => objectiveIds.includes(o.id)); - }, }; export const actions = { @@ -186,15 +173,6 @@ export const actions = { return true; }, - setSelectedObjective: async ({ commit, state }, objective) => { - if (state.activeItem) { - commit('SET_SELECTED_OBJECTIVE', { - itemId: state.activeItem.id, - objectiveId: objective?.id, - }); - } - }, - setActiveOrganization: async ({ commit, dispatch, state }, orgId) => { const orgSlug = orgId ? state.organizations.find((org) => org.id === orgId)?.slug || null @@ -239,26 +217,6 @@ export const mutations = { state.selectedPeriod = payload; }, - SET_SELECTED_OBJECTIVE(state, { itemId, objectiveId }) { - if (Object.hasOwnProperty.call(state.selectedObjectives, itemId)) { - const objectives = state.selectedObjectives[itemId]; - - if (objectiveId && objectives.includes(objectiveId)) { - Vue.set( - state.selectedObjectives, - itemId, - objectives.filter((id) => id !== objectiveId) - ); - } else if (objectiveId) { - objectives.push(objectiveId); - } else { - Vue.delete(state.selectedObjectives, itemId); - } - } else { - Vue.set(state.selectedObjectives, itemId, [objectiveId]); - } - }, - SET_HOME_ORGANIZATION(state, orgSlug) { if (!state.user.preferences) { state.user.preferences = defaultPreferences; @@ -268,6 +226,9 @@ export const mutations = { }; export default new Vuex.Store({ + modules: { + okrs: okrsModule, + }, state: { user: null, users: [], @@ -276,9 +237,7 @@ export default new Vuex.Store({ products: [], activeItem: null, activeItemRef: null, - activeKeyResult: null, activePeriod: null, - activeObjective: null, periods: [], objectives: [], objectiveContributors: [], @@ -290,7 +249,6 @@ export default new Vuex.Store({ loginLoading: false, dataLoading: false, selectedPeriod: null, - selectedObjectives: {}, organizationsUnsubscribe: () => {}, departmentsUnsubscribe: () => {}, productsUnsubscribe: () => {}, diff --git a/src/store/modules/okrs.js b/src/store/modules/okrs.js new file mode 100644 index 000000000..5587121c3 --- /dev/null +++ b/src/store/modules/okrs.js @@ -0,0 +1,113 @@ +import Vue from 'vue'; +import { firestoreAction } from 'vuexfire'; +import { db } from '@/config/firebaseConfig'; + +export default { + namespaced: true, + + state: () => ({ + activeObjective: null, + activeKeyResult: null, + workbenchObjectives: {}, + }), + + mutations: { + ADD_WORKBENCH_OBJECTIVE(state, { itemId, objectiveId }) { + if (Object.hasOwnProperty.call(state.workbenchObjectives, itemId)) { + const objectives = state.workbenchObjectives[itemId]; + + if (objectiveId && !objectives.includes(objectiveId)) { + objectives.push(objectiveId); + } + } else { + Vue.set(state.workbenchObjectives, itemId, [objectiveId]); + } + }, + + REMOVE_WORKBENCH_OBJECTIVE(state, { itemId, objectiveId }) { + if (Object.hasOwnProperty.call(state.workbenchObjectives, itemId)) { + const objectives = state.workbenchObjectives[itemId]; + + if (objectiveId && objectives.includes(objectiveId)) { + Vue.set( + state.workbenchObjectives, + itemId, + objectives.filter((id) => id !== objectiveId) + ); + } + } + }, + + CLEAR_WORKBENCH_OBJECTIVES(state, { itemId }) { + Vue.delete(state.workbenchObjectives, itemId); + }, + }, + + actions: { + setActiveObjective: firestoreAction( + ({ bindFirestoreRef, unbindFirestoreRef, rootGetters }, objectiveId) => { + if (!objectiveId) { + return unbindFirestoreRef('activeObjective'); + } + + if (!rootGetters.objectivesWithID.find((o) => o.id === objectiveId)) { + return unbindFirestoreRef('activeObjective'); + } + + return bindFirestoreRef( + 'activeObjective', + db.collection('objectives').doc(objectiveId), + { maxRefDepth: 1, wait: true } + ); + } + ), + + setActiveKeyResult: firestoreAction( + ({ bindFirestoreRef, unbindFirestoreRef }, keyResultId) => { + if (!keyResultId) { + return unbindFirestoreRef('activeKeyResult'); + } + + return bindFirestoreRef( + 'activeKeyResult', + db.collection('keyResults').doc(keyResultId), + { maxRefDepth: 1, wait: true } + ); + } + ), + + addWorkbenchObjective: async ({ commit, rootState }, objectiveId) => { + commit('ADD_WORKBENCH_OBJECTIVE', { + itemId: rootState.activeItem.id, + objectiveId, + }); + }, + + removeWorkbenchObjective: async ({ commit, rootState }, objectiveId) => { + commit('REMOVE_WORKBENCH_OBJECTIVE', { + itemId: rootState.activeItem.id, + objectiveId, + }); + }, + + clearWorkbenchObjectives: async ({ commit, rootState }) => { + commit('CLEAR_WORKBENCH_OBJECTIVES', { itemId: rootState.activeItem.id }); + }, + }, + + getters: { + /** + * Return objectives currently in the workbench for `rootState.activeItem`. + */ + workbenchObjectives: (state, getters, rootState, rootGetters) => { + if (!rootState.activeItem || !state.workbenchObjectives) { + return []; + } + const objectiveIds = state.workbenchObjectives[rootState.activeItem.id] || []; + if (!objectiveIds.length) { + return []; + } + return rootGetters.objectivesWithId.filter((o) => objectiveIds.includes(o.id)); + }, + }, +}; From e5c010188e9c7655b1fc0ae8ef50ac34c4a47cf6 Mon Sep 17 00:00:00 2001 From: "Petter H. Juliussen" Date: Thu, 24 Aug 2023 12:25:02 +0200 Subject: [PATCH 003/102] Add panes for OKR timeline, objectives and key results --- src/components/GanttChart.vue | 134 +++++--- src/components/OkrLinkCard.vue | 98 ++++++ src/components/ProgressBar.vue | 114 +++++-- src/components/panes/KeyResultPane.vue | 289 ++++++++++++++++++ src/components/panes/ObjectivePane.vue | 188 ++++++++++++ src/components/panes/TimelinePane.vue | 64 ++++ src/components/panes/WorkbenchPane.vue | 115 +++++++ .../WidgetProgressHistory.vue | 8 +- src/locale/locales/en-US.json | 12 +- src/locale/locales/nb-NO.json | 12 +- 10 files changed, 966 insertions(+), 68 deletions(-) create mode 100644 src/components/OkrLinkCard.vue create mode 100644 src/components/panes/KeyResultPane.vue create mode 100644 src/components/panes/ObjectivePane.vue create mode 100644 src/components/panes/TimelinePane.vue create mode 100644 src/components/panes/WorkbenchPane.vue diff --git a/src/components/GanttChart.vue b/src/components/GanttChart.vue index 22c2d522a..ec6987b35 100644 --- a/src/components/GanttChart.vue +++ b/src/components/GanttChart.vue @@ -49,28 +49,30 @@ { 'sep__period--clickable': periodObjectives.length }, ]" :style="periodStyle()" - @click="selectPeriodObjectives" + @click="periodObjectivesToWorkbench" > @@ -80,7 +82,7 @@ + + diff --git a/src/components/ProgressBar.vue b/src/components/ProgressBar.vue index 791969b65..5d772500e 100644 --- a/src/components/ProgressBar.vue +++ b/src/components/ProgressBar.vue @@ -1,51 +1,127 @@ diff --git a/src/components/panes/KeyResultPane.vue b/src/components/panes/KeyResultPane.vue new file mode 100644 index 000000000..7b5612a6e --- /dev/null +++ b/src/components/panes/KeyResultPane.vue @@ -0,0 +1,289 @@ + + + + + diff --git a/src/components/panes/ObjectivePane.vue b/src/components/panes/ObjectivePane.vue new file mode 100644 index 000000000..0340f0955 --- /dev/null +++ b/src/components/panes/ObjectivePane.vue @@ -0,0 +1,188 @@ + + + + + diff --git a/src/components/panes/TimelinePane.vue b/src/components/panes/TimelinePane.vue new file mode 100644 index 000000000..9808a3348 --- /dev/null +++ b/src/components/panes/TimelinePane.vue @@ -0,0 +1,64 @@ + + + + + diff --git a/src/components/panes/WorkbenchPane.vue b/src/components/panes/WorkbenchPane.vue new file mode 100644 index 000000000..1d410b8c6 --- /dev/null +++ b/src/components/panes/WorkbenchPane.vue @@ -0,0 +1,115 @@ + + + + + diff --git a/src/components/widgets/WidgetProgressHistory/WidgetProgressHistory.vue b/src/components/widgets/WidgetProgressHistory/WidgetProgressHistory.vue index c7b518886..804096789 100644 --- a/src/components/widgets/WidgetProgressHistory/WidgetProgressHistory.vue +++ b/src/components/widgets/WidgetProgressHistory/WidgetProgressHistory.vue @@ -1,5 +1,6 @@