From b08710b23e88cdb4419d0d764045e17db43d1b19 Mon Sep 17 00:00:00 2001 From: Tobi Abiodun Date: Sun, 4 Feb 2024 21:21:11 -0500 Subject: [PATCH] feat(lists): Add List filter to worksites --- src/components/work/WorksiteActions.vue | 30 --- src/components/work/WorksiteFilters.vue | 221 +++++++++++++++--- .../work/WorksiteSearchAndFilters.vue | 84 +++++++ src/components/work/WorksiteSearchInput.vue | 22 +- src/pages/Work.vue | 64 +++-- src/pages/lists/NewListForm.vue | 2 +- src/utils/data_filters/FormDataFilter.ts | 4 +- src/utils/data_filters/SurvivorFilter.ts | 4 +- src/utils/data_filters/UserInvitedByFilter.ts | 4 +- src/utils/data_filters/WorksiteDatesFilter.ts | 10 +- .../data_filters/WorksiteFieldsFilter.ts | 4 +- src/utils/data_filters/WorksiteFlagsFilter.ts | 4 +- src/utils/data_filters/WorksiteListsFilter.ts | 49 ++++ .../data_filters/WorksiteLocationsFilter.ts | 8 +- .../WorksiteMissingWorkTypeFilter.ts | 4 +- .../data_filters/WorksiteMyTeamFilter.ts | 4 +- .../data_filters/WorksiteStatusFilter.ts | 4 +- .../data_filters/WorksiteStatusGroupFilter.ts | 4 +- src/utils/data_filters/WorksiteTeamsFilter.ts | 4 +- 19 files changed, 418 insertions(+), 112 deletions(-) create mode 100644 src/components/work/WorksiteSearchAndFilters.vue create mode 100644 src/utils/data_filters/WorksiteListsFilter.ts diff --git a/src/components/work/WorksiteActions.vue b/src/components/work/WorksiteActions.vue index 9e5e810f6..dc0d5b59a 100644 --- a/src/components/work/WorksiteActions.vue +++ b/src/components/work/WorksiteActions.vue @@ -500,36 +500,6 @@ - - {{ $t('casesVue.filters') }} - {{ filtersCount }} - - {{ datesCount }} +
+ {{ $t('~~Lists') }} + {{ listsCount }} +
+
+
+
+ {{ $t('~~Lists') }} +
+
+ +
+
+
@@ -580,24 +663,26 @@ diff --git a/src/components/work/WorksiteSearchInput.vue b/src/components/work/WorksiteSearchInput.vue index c06a0fb28..8f035ae4d 100644 --- a/src/components/work/WorksiteSearchInput.vue +++ b/src/components/work/WorksiteSearchInput.vue @@ -8,6 +8,9 @@ :required="required" :tooltip="tooltip" data-testid="testWorksiteSearchInputSearch" + :input-style="{ + 'border-right': icon || tooltip ? '0' : '1px solid #dadada', + }" @update:model-value="debouncedSearch" @input.stop="" @focus="onFocus" @@ -69,14 +72,21 @@
+ {{ iconBadge }}
@@ -94,6 +104,7 @@ import Worksite from '@/models/Worksite'; import { getWorkTypeImage } from '@/filters/index'; import { useRecentWorksites } from '@/hooks/useRecentWorksites'; import type WorkType from '@/models/WorkType'; +import Badge from '@/components/Badge.vue'; type GeocoderResult = Awaited< ReturnType @@ -106,7 +117,7 @@ interface WorksiteSearchResult { export default defineComponent({ name: 'WorksiteSearchInput', - components: { BaseInput }, + components: { Badge, BaseInput }, props: { value: { type: String, @@ -152,6 +163,10 @@ export default defineComponent({ type: Boolean, default: false, }, + iconBadge: { + type: Number, + default: 0, + }, }, emits: [ 'input', @@ -160,6 +175,7 @@ export default defineComponent({ 'selectedGeocode', 'selectedExisting', 'clearSuggestions', + 'iconClicked', ], setup(props, { emit }) { const { currentUser } = useCurrentUser(); diff --git a/src/pages/Work.vue b/src/pages/Work.vue index 663c035e4..7f77cf0a9 100644 --- a/src/pages/Work.vue +++ b/src/pages/Work.vue @@ -403,22 +403,14 @@ {{ numeral(allWorksiteCount) }} - +
+
+ {{ $t('~~Current Filters') }} + + {{ $t('~~Clear All') }} + +
+
+ +
+
+ ({ id: 2 }); const filterQuery = ref({}); const filters = ref({}); + const filterLabels = ref([]); const mostRecentlySavedWorksite = ref(null); const selectedTableItems = ref>(new Set()); const availableWorkTypes = ref({}); @@ -1864,6 +1892,10 @@ export default defineComponent({ }); } + function updateFilterLabels(labels: any) { + filterLabels.value = labels; + } + watch( () => worksiteQuery.value, (value, previousValue) => { @@ -2130,6 +2162,8 @@ export default defineComponent({ portal, currentUserLocation, can: $can, + updateFilterLabels, + filterLabels, }; }, }); diff --git a/src/pages/lists/NewListForm.vue b/src/pages/lists/NewListForm.vue index 6f7dddad3..1f3a4768a 100644 --- a/src/pages/lists/NewListForm.vue +++ b/src/pages/lists/NewListForm.vue @@ -66,7 +66,7 @@ const props = defineProps({ const newList = ref({ name: '', description: '', - model: '', + model: props.model, shared: '', permissions: '', incident: undefined, diff --git a/src/utils/data_filters/FormDataFilter.ts b/src/utils/data_filters/FormDataFilter.ts index 85a7c3ab8..ce58be4c0 100644 --- a/src/utils/data_filters/FormDataFilter.ts +++ b/src/utils/data_filters/FormDataFilter.ts @@ -1,4 +1,4 @@ -import { useI18n } from 'vue-i18n'; +import { i18n } from '@/modules/i18n'; import Filter from './Filter'; export default class FormDataFilter extends Filter { @@ -30,7 +30,7 @@ export default class FormDataFilter extends Filter { for (const [key] of Object.entries(this.data).filter(([, value]) => { return Boolean(value); })) { - const localeMessages = useI18n().t(`formLabels.${key}`); + const localeMessages = i18n.global.t(`formLabels.${key}`); labels[key] = `${localeMessages}`; } diff --git a/src/utils/data_filters/SurvivorFilter.ts b/src/utils/data_filters/SurvivorFilter.ts index 91fd5f14c..355bde896 100644 --- a/src/utils/data_filters/SurvivorFilter.ts +++ b/src/utils/data_filters/SurvivorFilter.ts @@ -1,4 +1,4 @@ -import { useI18n } from 'vue-i18n'; +import { i18n } from '@/modules/i18n'; import Filter from './Filter'; export default class SurvivorFilter extends Filter { @@ -25,7 +25,7 @@ export default class SurvivorFilter extends Filter { } return { - my_team: useI18n().t('actions.member_of_my_org'), + my_team: i18n.global.t('actions.member_of_my_org'), }; } diff --git a/src/utils/data_filters/UserInvitedByFilter.ts b/src/utils/data_filters/UserInvitedByFilter.ts index cc967ffd8..e627f22f3 100644 --- a/src/utils/data_filters/UserInvitedByFilter.ts +++ b/src/utils/data_filters/UserInvitedByFilter.ts @@ -1,4 +1,4 @@ -import { useI18n } from 'vue-i18n'; +import { i18n } from '@/modules/i18n'; import User from '../../models/User'; import Filter from './Filter'; @@ -21,7 +21,7 @@ export default class UserInvitedByFilter extends Filter { const labels: Record = {}; [...this.data].forEach((user: any) => { const { id, full_name } = user; - labels[id] = useI18n().t('userInvitedBy.invited_by', { + labels[id] = i18n.global.t('userInvitedBy.invited_by', { full_name, }); }); diff --git a/src/utils/data_filters/WorksiteDatesFilter.ts b/src/utils/data_filters/WorksiteDatesFilter.ts index ce778e648..b7cc0c24a 100644 --- a/src/utils/data_filters/WorksiteDatesFilter.ts +++ b/src/utils/data_filters/WorksiteDatesFilter.ts @@ -1,6 +1,6 @@ import moment from 'moment'; import { omitBy, isNull } from 'lodash'; -import { useI18n } from 'vue-i18n'; +import { i18n } from '@/modules/i18n'; import Filter from './Filter'; interface WorksiteDatesFilterPacked { @@ -44,25 +44,25 @@ export default class WorksiteDatesFilter extends Filter { { created_start: this.data.created && moment(this.data.created[0]).isValid() - ? `${useI18n().t('worksiteFilters.from')}: ${moment( + ? `${i18n.global.t('worksiteFilters.from')}: ${moment( this.data.created[0], ).format('YYYY-MM-DD')}` : null, created_end: this.data.created && moment(this.data.created[1]).isValid() - ? `${useI18n().t('worksiteFilters.to')}: ${moment( + ? `${i18n.global.t('worksiteFilters.to')}: ${moment( this.data.created[1], ).format('YYYY-MM-DD')}` : null, updated_start: this.data.updated && moment(this.data.updated[0]).isValid() - ? `${useI18n().t('worksiteFilters.from')}: ${moment( + ? `${i18n.global.t('worksiteFilters.from')}: ${moment( this.data.updated[0], ).format('YYYY-MM-DD')}` : null, updated_end: this.data.updated && moment(this.data.updated[1]).isValid() - ? `${useI18n().t('worksiteFilters.to')}: ${moment( + ? `${i18n.global.t('worksiteFilters.to')}: ${moment( this.data.updated[1], ).format('YYYY-MM-DD')}` : null, diff --git a/src/utils/data_filters/WorksiteFieldsFilter.ts b/src/utils/data_filters/WorksiteFieldsFilter.ts index 0e1c94628..aa0fe30e5 100644 --- a/src/utils/data_filters/WorksiteFieldsFilter.ts +++ b/src/utils/data_filters/WorksiteFieldsFilter.ts @@ -1,4 +1,4 @@ -import { useI18n } from 'vue-i18n'; +import { i18n } from '@/modules/i18n'; import { getWorkTypeName } from '../../filters'; import Filter from './Filter'; @@ -33,7 +33,7 @@ export default class WorksiteFieldsFilter extends Filter { for (const [key] of Object.entries(this.data).filter(([, value]) => { return Boolean(value); })) { - labels[key] = `${useI18n().t( + labels[key] = `${i18n.global.t( 'worksiteFilters.work_type', )}: ${getWorkTypeName(key)}`; } diff --git a/src/utils/data_filters/WorksiteFlagsFilter.ts b/src/utils/data_filters/WorksiteFlagsFilter.ts index 0c6f32754..583994a9b 100644 --- a/src/utils/data_filters/WorksiteFlagsFilter.ts +++ b/src/utils/data_filters/WorksiteFlagsFilter.ts @@ -1,4 +1,4 @@ -import { useI18n } from 'vue-i18n'; +import { i18n } from '@/modules/i18n'; import Filter from './Filter'; export default class WorksiteFlagsFilter extends Filter { @@ -30,7 +30,7 @@ export default class WorksiteFlagsFilter extends Filter { for (const [key] of Object.entries(this.data).filter(([, value]) => { return Boolean(value); })) { - labels[key] = `${useI18n().t('worksiteFilters.flag')}: ${useI18n().t( + labels[key] = `${i18n.global.t('worksiteFilters.flag')}: ${i18n.global.t( key, )}`; } diff --git a/src/utils/data_filters/WorksiteListsFilter.ts b/src/utils/data_filters/WorksiteListsFilter.ts new file mode 100644 index 000000000..20c9e90cd --- /dev/null +++ b/src/utils/data_filters/WorksiteListsFilter.ts @@ -0,0 +1,49 @@ +import { i18n } from '@/modules/i18n'; +import Filter from './Filter'; + +export default class WorksiteListsFilter extends Filter { + packFunction() { + const packed = {} as Record; + + if (this.data.include_lists.length > 0) { + packed.include_lists = this.data.include_lists.map((l) => l.id).join(','); + } + + if (this.data.exclude_lists.length > 0) { + packed.exclude_lists = this.data.exclude_lists.map((l) => l.id).join(','); + } + + return packed; + } + + getCount() { + if (!this.data) { + return 0; + } + + return this.data.include_lists.length + this.data.exclude_lists.length; + } + + getFilterLabels() { + const labels = {} as Record; + for (const list of this.data.include_lists) { + labels[list.id] = `${i18n.global.t('~~List')}: ${list.name}`; + } + + for (const list of this.data.exclude_lists) { + labels[list.id] = `${i18n.global.t('~~-List')}: ${list.name}`; + } + + return labels; + } + + removeField(identifier: string) { + this.data.include_lists = this.data.include_lists.filter( + (list) => String(list.id) !== identifier, + ); + this.data.exclude_lists = this.data.exclude_lists.filter( + (list) => String(list.id) !== identifier, + ); + this.data = { ...this.data }; + } +} diff --git a/src/utils/data_filters/WorksiteLocationsFilter.ts b/src/utils/data_filters/WorksiteLocationsFilter.ts index 0713c061c..c15538b01 100644 --- a/src/utils/data_filters/WorksiteLocationsFilter.ts +++ b/src/utils/data_filters/WorksiteLocationsFilter.ts @@ -1,4 +1,4 @@ -import { useI18n } from 'vue-i18n'; +import { i18n } from '@/modules/i18n'; import User from '../../models/User'; import { store } from '../../store'; import Filter from './Filter'; @@ -47,11 +47,13 @@ export default class UserLocationsFilter extends Filter { })) { labels[key] = `Location: ${key}`; if (key === 'organization_primary_location') { - labels[key] = useI18n().t('worksiteFilters.in_primary_response_area'); + labels[key] = i18n.global.t('worksiteFilters.in_primary_response_area'); } if (key === 'organization_secondary_location') { - labels[key] = useI18n().t('worksiteFilters.in_secondary_response_area'); + labels[key] = i18n.global.t( + 'worksiteFilters.in_secondary_response_area', + ); } } diff --git a/src/utils/data_filters/WorksiteMissingWorkTypeFilter.ts b/src/utils/data_filters/WorksiteMissingWorkTypeFilter.ts index a6693bcd2..eed6d9078 100644 --- a/src/utils/data_filters/WorksiteMissingWorkTypeFilter.ts +++ b/src/utils/data_filters/WorksiteMissingWorkTypeFilter.ts @@ -1,4 +1,4 @@ -import { useI18n } from 'vue-i18n'; +import { i18n } from '@/modules/i18n'; import { snakeToTitleCase } from '../../filters'; import Filter from './Filter'; @@ -28,7 +28,7 @@ export default class WorksiteMissingWorkTypeFilter extends Filter { for (const [key] of Object.entries(this.data).filter(([, value]) => { return Boolean(value); })) { - labels[key] = `${useI18n().t( + labels[key] = `${i18n.global.t( 'worksiteFilters.status', )}: ${snakeToTitleCase(key)}`; } diff --git a/src/utils/data_filters/WorksiteMyTeamFilter.ts b/src/utils/data_filters/WorksiteMyTeamFilter.ts index 70558e18f..db52b4de0 100644 --- a/src/utils/data_filters/WorksiteMyTeamFilter.ts +++ b/src/utils/data_filters/WorksiteMyTeamFilter.ts @@ -1,4 +1,4 @@ -import { useI18n } from 'vue-i18n'; +import { i18n } from '@/modules/i18n'; import Filter from './Filter'; export default class WorksiteMyTeamFilter extends Filter { @@ -25,7 +25,7 @@ export default class WorksiteMyTeamFilter extends Filter { } return { - my_team: useI18n().t('worksiteFilters.my_team'), + my_team: i18n.global.t('worksiteFilters.my_team'), }; } diff --git a/src/utils/data_filters/WorksiteStatusFilter.ts b/src/utils/data_filters/WorksiteStatusFilter.ts index 0e4c903f6..e5cc9aa52 100644 --- a/src/utils/data_filters/WorksiteStatusFilter.ts +++ b/src/utils/data_filters/WorksiteStatusFilter.ts @@ -1,4 +1,4 @@ -import { useI18n } from 'vue-i18n'; +import { i18n } from '@/modules/i18n'; import { snakeToTitleCase } from '../../filters'; import Filter from './Filter'; @@ -33,7 +33,7 @@ export default class WorksiteStatusFilter extends Filter { for (const [key] of Object.entries(this.data).filter(([, value]) => { return Boolean(value); })) { - labels[key] = `${useI18n().t( + labels[key] = `${i18n.global.t( 'worksiteFilters.status', )}: ${snakeToTitleCase(key)}`; } diff --git a/src/utils/data_filters/WorksiteStatusGroupFilter.ts b/src/utils/data_filters/WorksiteStatusGroupFilter.ts index 46bba6233..a0116685a 100644 --- a/src/utils/data_filters/WorksiteStatusGroupFilter.ts +++ b/src/utils/data_filters/WorksiteStatusGroupFilter.ts @@ -1,4 +1,4 @@ -import { useI18n } from 'vue-i18n'; +import { i18n } from '@/modules/i18n'; import { snakeToTitleCase } from '../../filters'; import enums from '../../store/modules/enums'; import User from '../../models/User'; @@ -61,7 +61,7 @@ export default class WorksiteStatusGroupFilter extends Filter { for (const [key] of Object.entries(this.data).filter(([, value]) => { return Boolean(value); })) { - labels[key] = `${useI18n().t( + labels[key] = `${i18n.global.t( 'worksiteFilters.status', )}: ${snakeToTitleCase(key)}`; } diff --git a/src/utils/data_filters/WorksiteTeamsFilter.ts b/src/utils/data_filters/WorksiteTeamsFilter.ts index 311510423..a8f820489 100644 --- a/src/utils/data_filters/WorksiteTeamsFilter.ts +++ b/src/utils/data_filters/WorksiteTeamsFilter.ts @@ -1,4 +1,4 @@ -import { useI18n } from 'vue-i18n'; +import { i18n } from '@/modules/i18n'; import Team from '../../models/Team'; import Filter from './Filter'; @@ -31,7 +31,7 @@ export default class WorksiteTeamsFilter extends Filter { for (const [key] of Object.entries(this.data).filter(([, value]) => { return Boolean(value); })) { - labels[key] = `${useI18n().t('worksiteFilters.teams')}: ${Team.find(key) + labels[key] = `${i18n.global.t('worksiteFilters.teams')}: ${Team.find(key) ?.name}`; }