diff --git a/frontend/src/modules/member/config/filters/activeOn/config.ts b/frontend/src/modules/member/config/filters/activeOn/config.ts index 33f2833e23..5759869824 100644 --- a/frontend/src/modules/member/config/filters/activeOn/config.ts +++ b/frontend/src/modules/member/config/filters/activeOn/config.ts @@ -1,6 +1,8 @@ import { FilterConfigType } from '@/shared/modules/filters/types/FilterConfig'; import { MultiSelectFilterConfig, MultiSelectFilterValue } from '@/shared/modules/filters/types/filterTypes/MultiSelectFilterConfig'; import { CrowdIntegrations } from '@/integrations/integrations-config'; +import { apiFilterRendererByType } from '@/shared/modules/filters/config/apiFilterRendererByType'; +import { itemLabelRendererByType } from '@/shared/modules/filters/config/itemLabelRendererByType'; const activeOn: MultiSelectFilterConfig = { id: 'activeOn', @@ -20,11 +22,10 @@ const activeOn: MultiSelectFilterConfig = { ], }, itemLabelRenderer(value: MultiSelectFilterValue): string { - return `Active On ${value?.value.join(',') || '...'}`; + return itemLabelRendererByType[FilterConfigType.MULTISELECT]('Active on', value); }, - apiFilterRenderer(value): any[] { - console.log(value); - return []; + apiFilterRenderer(value: MultiSelectFilterValue): any[] { + return apiFilterRendererByType[FilterConfigType.MULTISELECT]('activeOn', value); }, }; diff --git a/frontend/src/modules/member/config/filters/activityType/config.ts b/frontend/src/modules/member/config/filters/activityType/config.ts index 63c0c95f2b..93832e0899 100644 --- a/frontend/src/modules/member/config/filters/activityType/config.ts +++ b/frontend/src/modules/member/config/filters/activityType/config.ts @@ -3,6 +3,8 @@ import { CustomFilterConfig } from '@/shared/modules/filters/types/filterTypes/C import ActivityTypeFilter from '@/modules/activity/config/filters/activityType/ActivityTypeFilter.vue'; import { SelectFilterValue } from '@/shared/modules/filters/types/filterTypes/SelectFilterConfig'; import { queryUrlParserByType } from '@/shared/modules/filters/config/queryUrlParserByType'; +import { itemLabelRendererByType } from '@/shared/modules/filters/config/itemLabelRendererByType'; +import { apiFilterRendererByType } from '@/shared/modules/filters/config/apiFilterRendererByType'; const activityType: CustomFilterConfig = { id: 'activityType', @@ -13,11 +15,10 @@ const activityType: CustomFilterConfig = { }, queryUrlParser: queryUrlParserByType[FilterConfigType.SELECT], itemLabelRenderer(value: SelectFilterValue): string { - return `Active on ${value || '...'}`; + return itemLabelRendererByType[FilterConfigType.SELECT]('Activity type', value); }, - apiFilterRenderer(value): any[] { - console.log(value); - return []; + apiFilterRenderer(value: SelectFilterValue): any[] { + return apiFilterRendererByType[FilterConfigType.SELECT]('activityTypes', value); }, }; diff --git a/frontend/src/modules/member/config/filters/avgSentiment/config.ts b/frontend/src/modules/member/config/filters/avgSentiment/config.ts index 96c17d23f5..067cc59a2d 100644 --- a/frontend/src/modules/member/config/filters/avgSentiment/config.ts +++ b/frontend/src/modules/member/config/filters/avgSentiment/config.ts @@ -1,5 +1,7 @@ import { FilterConfigType } from '@/shared/modules/filters/types/FilterConfig'; import { MultiSelectFilterConfig, MultiSelectFilterValue } from '@/shared/modules/filters/types/filterTypes/MultiSelectFilterConfig'; +import { itemLabelRendererByType } from '@/shared/modules/filters/config/itemLabelRendererByType'; +import { apiFilterRendererByType } from '@/shared/modules/filters/config/apiFilterRendererByType'; import options from './options'; const avgSentiment: MultiSelectFilterConfig = { @@ -10,11 +12,10 @@ const avgSentiment: MultiSelectFilterConfig = { options, }, itemLabelRenderer(value: MultiSelectFilterValue): string { - return `Avg. sentiment ${value?.value.join(',') || '...'}`; + return itemLabelRendererByType[FilterConfigType.MULTISELECT]('Avg. sentiment', value); }, - apiFilterRenderer(value): any[] { - console.log(value); - return []; + apiFilterRenderer(value: MultiSelectFilterValue): any[] { + return apiFilterRendererByType[FilterConfigType.MULTISELECT]('averageSentiment', value); }, }; diff --git a/frontend/src/modules/member/config/filters/engagementLevel/config.ts b/frontend/src/modules/member/config/filters/engagementLevel/config.ts index e5ec26b1f9..c8cf3eb421 100644 --- a/frontend/src/modules/member/config/filters/engagementLevel/config.ts +++ b/frontend/src/modules/member/config/filters/engagementLevel/config.ts @@ -1,5 +1,7 @@ import { FilterConfigType } from '@/shared/modules/filters/types/FilterConfig'; import { MultiSelectFilterConfig, MultiSelectFilterValue } from '@/shared/modules/filters/types/filterTypes/MultiSelectFilterConfig'; +import { itemLabelRendererByType } from '@/shared/modules/filters/config/itemLabelRendererByType'; +import { apiFilterRendererByType } from '@/shared/modules/filters/config/apiFilterRendererByType'; import options from './options'; const engagementLevel: MultiSelectFilterConfig = { @@ -10,11 +12,10 @@ const engagementLevel: MultiSelectFilterConfig = { options, }, itemLabelRenderer(value: MultiSelectFilterValue): string { - return `Engagement level ${value?.value.join(',') || '...'}`; + return itemLabelRendererByType[FilterConfigType.MULTISELECT]('Engagement level', value); }, - apiFilterRenderer(value): any[] { - console.log(value); - return []; + apiFilterRenderer(value: MultiSelectFilterValue): any[] { + return apiFilterRendererByType[FilterConfigType.MULTISELECT]('score', value); }, }; diff --git a/frontend/src/modules/member/config/filters/enrichedMember/config.ts b/frontend/src/modules/member/config/filters/enrichedMember/config.ts index a798a0bc19..fec24d298e 100644 --- a/frontend/src/modules/member/config/filters/enrichedMember/config.ts +++ b/frontend/src/modules/member/config/filters/enrichedMember/config.ts @@ -1,17 +1,21 @@ import { FilterConfigType } from '@/shared/modules/filters/types/FilterConfig'; -import { BooleanFilterConfig } from '@/shared/modules/filters/types/filterTypes/BooleanFilterConfig'; +import { + BooleanFilterConfig, + BooleanFilterValue, +} from '@/shared/modules/filters/types/filterTypes/BooleanFilterConfig'; +import { itemLabelRendererByType } from '@/shared/modules/filters/config/itemLabelRendererByType'; +import { apiFilterRendererByType } from '@/shared/modules/filters/config/apiFilterRendererByType'; const enrichedMember: BooleanFilterConfig = { id: 'enrichedMember', label: 'Enriched member', type: FilterConfigType.BOOLEAN, options: {}, - itemLabelRenderer(value): string { - return `Enriched member ${value?.value ? 'True' : 'False'}`; + itemLabelRenderer(value: BooleanFilterValue): string { + return itemLabelRendererByType[FilterConfigType.BOOLEAN]('Enriched member', value); }, - apiFilterRenderer(value): any[] { - console.log(value); - return []; + apiFilterRenderer(value: BooleanFilterValue): any[] { + return apiFilterRendererByType[FilterConfigType.BOOLEAN]('lastEnriched', value); }, }; diff --git a/frontend/src/modules/member/config/filters/identities/config.ts b/frontend/src/modules/member/config/filters/identities/config.ts index cb569d9ee8..9390be952d 100644 --- a/frontend/src/modules/member/config/filters/identities/config.ts +++ b/frontend/src/modules/member/config/filters/identities/config.ts @@ -1,6 +1,8 @@ import { FilterConfigType } from '@/shared/modules/filters/types/FilterConfig'; import { MultiSelectFilterConfig, MultiSelectFilterValue } from '@/shared/modules/filters/types/filterTypes/MultiSelectFilterConfig'; import { CrowdIntegrations } from '@/integrations/integrations-config'; +import { itemLabelRendererByType } from '@/shared/modules/filters/config/itemLabelRendererByType'; +import { apiFilterRendererByType } from '@/shared/modules/filters/config/apiFilterRendererByType'; const identities: MultiSelectFilterConfig = { id: 'identities', @@ -20,11 +22,10 @@ const identities: MultiSelectFilterConfig = { ], }, itemLabelRenderer(value: MultiSelectFilterValue): string { - return `Identities ${value?.value.join(',') || '...'}`; + return itemLabelRendererByType[FilterConfigType.MULTISELECT]('Identities', value); }, - apiFilterRenderer(value): any[] { - console.log(value); - return []; + apiFilterRenderer(value: MultiSelectFilterValue): any[] { + return apiFilterRendererByType[FilterConfigType.MULTISELECT]('identities', value); }, }; diff --git a/frontend/src/modules/member/config/filters/joinedDate/config.ts b/frontend/src/modules/member/config/filters/joinedDate/config.ts index 5b83a2918d..3cd48f4183 100644 --- a/frontend/src/modules/member/config/filters/joinedDate/config.ts +++ b/frontend/src/modules/member/config/filters/joinedDate/config.ts @@ -1,17 +1,18 @@ import { FilterConfigType } from '@/shared/modules/filters/types/FilterConfig'; -import { DateFilterConfig } from '@/shared/modules/filters/types/filterTypes/DateFilterConfig'; +import { DateFilterConfig, DateFilterValue } from '@/shared/modules/filters/types/filterTypes/DateFilterConfig'; +import { itemLabelRendererByType } from '@/shared/modules/filters/config/itemLabelRendererByType'; +import { apiFilterRendererByType } from '@/shared/modules/filters/config/apiFilterRendererByType'; const joinedDate: DateFilterConfig = { id: 'joinedDate', label: 'Joined date', type: FilterConfigType.DATE, options: {}, - itemLabelRenderer(value): string { - return `Joined date ${value.value || '...'}`; + itemLabelRenderer(value: DateFilterValue): string { + return itemLabelRendererByType[FilterConfigType.BOOLEAN]('Joined date', value); }, - apiFilterRenderer(value): any[] { - console.log(value); - return []; + apiFilterRenderer(value: DateFilterValue): any[] { + return apiFilterRendererByType[FilterConfigType.BOOLEAN]('joinedAt', value); }, }; diff --git a/frontend/src/modules/member/config/filters/lastActivityDate/config.ts b/frontend/src/modules/member/config/filters/lastActivityDate/config.ts index 8ea5600954..ba58a2a8ad 100644 --- a/frontend/src/modules/member/config/filters/lastActivityDate/config.ts +++ b/frontend/src/modules/member/config/filters/lastActivityDate/config.ts @@ -1,17 +1,18 @@ import { FilterConfigType } from '@/shared/modules/filters/types/FilterConfig'; -import { DateFilterConfig } from '@/shared/modules/filters/types/filterTypes/DateFilterConfig'; +import { DateFilterConfig, DateFilterValue } from '@/shared/modules/filters/types/filterTypes/DateFilterConfig'; +import { itemLabelRendererByType } from '@/shared/modules/filters/config/itemLabelRendererByType'; +import { apiFilterRendererByType } from '@/shared/modules/filters/config/apiFilterRendererByType'; const lastActivityDate: DateFilterConfig = { id: 'lastActivityDate', label: 'Last activity date', type: FilterConfigType.DATE, options: {}, - itemLabelRenderer(value): string { - return `Last activity date ${value.value || '...'}`; + itemLabelRenderer(value: DateFilterValue): string { + return itemLabelRendererByType[FilterConfigType.BOOLEAN]('Last activity date', value); }, - apiFilterRenderer(value): any[] { - console.log(value); - return []; + apiFilterRenderer(value: DateFilterValue): any[] { + return apiFilterRendererByType[FilterConfigType.BOOLEAN]('lastActive', value); }, }; diff --git a/frontend/src/modules/member/config/filters/noOfActivities/config.ts b/frontend/src/modules/member/config/filters/noOfActivities/config.ts index 4e9715ed57..f1dd90604d 100644 --- a/frontend/src/modules/member/config/filters/noOfActivities/config.ts +++ b/frontend/src/modules/member/config/filters/noOfActivities/config.ts @@ -1,18 +1,18 @@ -import { NumberFilterConfig } from '@/shared/modules/filters/types/filterTypes/NumberFilterConfig'; +import { NumberFilterConfig, NumberFilterValue } from '@/shared/modules/filters/types/filterTypes/NumberFilterConfig'; import { FilterConfigType } from '@/shared/modules/filters/types/FilterConfig'; +import { itemLabelRendererByType } from '@/shared/modules/filters/config/itemLabelRendererByType'; +import { apiFilterRendererByType } from '@/shared/modules/filters/config/apiFilterRendererByType'; const noOfActivities: NumberFilterConfig = { id: 'noOfActivities', label: '# of activities', type: FilterConfigType.NUMBER, options: {}, - itemLabelRenderer(value): string { - return `# of activities ${value?.value || '...'}`; + itemLabelRenderer(value: NumberFilterValue): string { + return itemLabelRendererByType[FilterConfigType.NUMBER]('# of activities', value); }, - apiFilterRenderer({ value }): any[] { - return [ - { activityCount: { eq: value } }, - ]; + apiFilterRenderer(value: NumberFilterValue): any[] { + return apiFilterRendererByType[FilterConfigType.NUMBER]('activityCount', value); }, }; diff --git a/frontend/src/modules/member/config/filters/noOfOSSContributions/config.ts b/frontend/src/modules/member/config/filters/noOfOSSContributions/config.ts index e2c1adb685..8c4aecc322 100644 --- a/frontend/src/modules/member/config/filters/noOfOSSContributions/config.ts +++ b/frontend/src/modules/member/config/filters/noOfOSSContributions/config.ts @@ -1,19 +1,18 @@ -import { NumberFilterConfig } from '@/shared/modules/filters/types/filterTypes/NumberFilterConfig'; +import { NumberFilterConfig, NumberFilterValue } from '@/shared/modules/filters/types/filterTypes/NumberFilterConfig'; import { FilterConfigType } from '@/shared/modules/filters/types/FilterConfig'; +import { itemLabelRendererByType } from '@/shared/modules/filters/config/itemLabelRendererByType'; +import { apiFilterRendererByType } from '@/shared/modules/filters/config/apiFilterRendererByType'; const noOfOSSContributions: NumberFilterConfig = { id: 'noOfOSSContributions', label: '# of open source contributions', type: FilterConfigType.NUMBER, options: {}, - itemLabelRenderer(value): string { - return `# of open source contributions ${value?.value || '...'}`; + itemLabelRenderer(value: NumberFilterValue): string { + return itemLabelRendererByType[FilterConfigType.NUMBER]('# of OSS contributions', value); }, - apiFilterRenderer({ value }): any[] { - console.log(value); - return [ - { numberOfOpenSourceContributions: { eq: value } }, - ]; + apiFilterRenderer(value: NumberFilterValue): any[] { + return apiFilterRendererByType[FilterConfigType.NUMBER]('numberOfOpenSourceContributions', value); }, }; diff --git a/frontend/src/modules/member/config/filters/reach/config.ts b/frontend/src/modules/member/config/filters/reach/config.ts index 5965612c18..4a549ed245 100644 --- a/frontend/src/modules/member/config/filters/reach/config.ts +++ b/frontend/src/modules/member/config/filters/reach/config.ts @@ -1,17 +1,18 @@ -import { NumberFilterConfig } from '@/shared/modules/filters/types/filterTypes/NumberFilterConfig'; +import { NumberFilterConfig, NumberFilterValue } from '@/shared/modules/filters/types/filterTypes/NumberFilterConfig'; import { FilterConfigType } from '@/shared/modules/filters/types/FilterConfig'; +import { itemLabelRendererByType } from '@/shared/modules/filters/config/itemLabelRendererByType'; +import { apiFilterRendererByType } from '@/shared/modules/filters/config/apiFilterRendererByType'; const reach: NumberFilterConfig = { id: 'reach', label: 'Reach', type: FilterConfigType.NUMBER, options: {}, - itemLabelRenderer(value): string { - return `Reach ${value?.value || '...'}`; + itemLabelRenderer(value: NumberFilterValue): string { + return itemLabelRendererByType[FilterConfigType.NUMBER]('Reach', value); }, - apiFilterRenderer(value): any[] { - console.log(value); - return []; + apiFilterRenderer(value: NumberFilterValue): any[] { + return apiFilterRendererByType[FilterConfigType.NUMBER]('reach', value); }, }; diff --git a/frontend/src/modules/member/config/filters/tags/config.ts b/frontend/src/modules/member/config/filters/tags/config.ts index 39054483cc..57f65d3f31 100644 --- a/frontend/src/modules/member/config/filters/tags/config.ts +++ b/frontend/src/modules/member/config/filters/tags/config.ts @@ -1,5 +1,7 @@ import { FilterConfigType } from '@/shared/modules/filters/types/FilterConfig'; import { MultiSelectFilterConfig, MultiSelectFilterValue } from '@/shared/modules/filters/types/filterTypes/MultiSelectFilterConfig'; +import { itemLabelRendererByType } from '@/shared/modules/filters/config/itemLabelRendererByType'; +import { apiFilterRendererByType } from '@/shared/modules/filters/config/apiFilterRendererByType'; const tags: MultiSelectFilterConfig = { id: 'tags', @@ -10,11 +12,10 @@ const tags: MultiSelectFilterConfig = { options: [], }, itemLabelRenderer(value: MultiSelectFilterValue): string { - return `Tags ${value?.value.join(',') || '...'}`; + return itemLabelRendererByType[FilterConfigType.MULTISELECT]('Active on', value); }, - apiFilterRenderer(value): any[] { - console.log(value); - return []; + apiFilterRenderer(value: MultiSelectFilterValue): any[] { + return apiFilterRendererByType[FilterConfigType.MULTISELECT]('tags', value); }, }; diff --git a/frontend/src/modules/member/config/saved-views/main.ts b/frontend/src/modules/member/config/saved-views/main.ts new file mode 100644 index 0000000000..609acc63c3 --- /dev/null +++ b/frontend/src/modules/member/config/saved-views/main.ts @@ -0,0 +1,29 @@ +import { SavedView, SavedViewsConfig } from '@/shared/modules/saved-views/types/SavedViewsConfig'; +import allMembers from './views/all-members'; +import newAndActive from './views/new-and-active'; +import slippingAway from './views/slipping-away'; +import mostEngaged from './views/most-engaged'; +import influential from './views/influential'; +import teamMembers from './views/team-members'; + +import bot from './settings/bot'; +import teamMember from './settings/teamMember'; +import organization from './settings/organization'; + +export const memberSavedViews: SavedViewsConfig = { + defaultView: allMembers, + settings: { + bot, + teamMember, + organization, + }, +}; + +// Hardcoded views until we have backend done for it +export const memberViews: SavedView[] = [ + newAndActive, + slippingAway, + mostEngaged, + influential, + teamMembers, +]; diff --git a/frontend/src/modules/member/config/saved-views/settings/bot.ts b/frontend/src/modules/member/config/saved-views/settings/bot.ts new file mode 100644 index 0000000000..0e735a1eba --- /dev/null +++ b/frontend/src/modules/member/config/saved-views/settings/bot.ts @@ -0,0 +1,15 @@ +import { SavedViewsSetting } from '@/shared/modules/saved-views/types/SavedViewsConfig'; +import { IncludeEnum } from '@/modules/member/config/saved-views/settings/types/IncludeEnum'; +import { includeFilterRenderer } from '@/modules/member/config/saved-views/settings/common/includeFilterRenderer'; + +const bot: SavedViewsSetting = { + defaultValue: IncludeEnum.EXCLUDE, + queryUrlParser(value: string): IncludeEnum { + return value as IncludeEnum; + }, + apiFilterRenderer(value: IncludeEnum): any[] { + return includeFilterRenderer('isBot', value); + }, +}; + +export default bot; diff --git a/frontend/src/modules/member/config/saved-views/settings/common/includeFilterRenderer.ts b/frontend/src/modules/member/config/saved-views/settings/common/includeFilterRenderer.ts new file mode 100644 index 0000000000..ab54350309 --- /dev/null +++ b/frontend/src/modules/member/config/saved-views/settings/common/includeFilterRenderer.ts @@ -0,0 +1,17 @@ +import { IncludeEnum } from '@/modules/member/config/saved-views/settings/types/IncludeEnum'; + +export const includeFilterRenderer = (property: string, value: IncludeEnum) => { + if (value === IncludeEnum.FILTER) { + return [ + { + [property]: { eq: true }, + }, + ]; + } + if (value === IncludeEnum.EXCLUDE) { + return [ + { [property]: { not: true } }, + ]; + } + return []; +}; diff --git a/frontend/src/modules/member/config/saved-views/settings/organization.ts b/frontend/src/modules/member/config/saved-views/settings/organization.ts new file mode 100644 index 0000000000..005a7e2517 --- /dev/null +++ b/frontend/src/modules/member/config/saved-views/settings/organization.ts @@ -0,0 +1,15 @@ +import { SavedViewsSetting } from '@/shared/modules/saved-views/types/SavedViewsConfig'; +import { includeFilterRenderer } from '@/modules/member/config/saved-views/settings/common/includeFilterRenderer'; +import { IncludeEnum } from '@/modules/member/config/saved-views/settings/types/IncludeEnum'; + +const organization: SavedViewsSetting = { + defaultValue: IncludeEnum.EXCLUDE, + queryUrlParser(value: string): IncludeEnum { + return value as IncludeEnum; + }, + apiFilterRenderer(value: IncludeEnum): any[] { + return includeFilterRenderer('isOrganization', value); + }, +}; + +export default organization; diff --git a/frontend/src/modules/member/config/saved-views/settings/teamMember.ts b/frontend/src/modules/member/config/saved-views/settings/teamMember.ts new file mode 100644 index 0000000000..217fda7a05 --- /dev/null +++ b/frontend/src/modules/member/config/saved-views/settings/teamMember.ts @@ -0,0 +1,15 @@ +import { SavedViewsSetting } from '@/shared/modules/saved-views/types/SavedViewsConfig'; +import { includeFilterRenderer } from '@/modules/member/config/saved-views/settings/common/includeFilterRenderer'; +import { IncludeEnum } from '@/modules/member/config/saved-views/settings/types/IncludeEnum'; + +const teamMember: SavedViewsSetting = { + defaultValue: IncludeEnum.EXCLUDE, + queryUrlParser(value: string): IncludeEnum { + return value as IncludeEnum; + }, + apiFilterRenderer(value: IncludeEnum): any[] { + return includeFilterRenderer('isTeamMember', value); + }, +}; + +export default teamMember; diff --git a/frontend/src/modules/member/config/saved-views/settings/types/IncludeEnum.ts b/frontend/src/modules/member/config/saved-views/settings/types/IncludeEnum.ts new file mode 100644 index 0000000000..f3b3e6e3d2 --- /dev/null +++ b/frontend/src/modules/member/config/saved-views/settings/types/IncludeEnum.ts @@ -0,0 +1,5 @@ +export enum IncludeEnum { + INCLUDE = 'include', + EXCLUDE = 'exclude', + FILTER = 'filter' +} diff --git a/frontend/src/modules/member/config/saved-views/views/all-members.ts b/frontend/src/modules/member/config/saved-views/views/all-members.ts new file mode 100644 index 0000000000..9fff32ed44 --- /dev/null +++ b/frontend/src/modules/member/config/saved-views/views/all-members.ts @@ -0,0 +1,21 @@ +import { SavedView } from '@/shared/modules/saved-views/types/SavedViewsConfig'; + +const allMembers: SavedView = { + id: 'all-members', + label: 'All members', + filter: { + search: '', + relation: 'and', + order: { + prop: 'createdBy', + order: 'descending', + }, + settings: { + bot: 'exclude', + teamMember: 'exclude', + organization: 'exclude', + }, + }, +}; + +export default allMembers; diff --git a/frontend/src/modules/member/config/saved-views/views/influential.ts b/frontend/src/modules/member/config/saved-views/views/influential.ts new file mode 100644 index 0000000000..52708baf97 --- /dev/null +++ b/frontend/src/modules/member/config/saved-views/views/influential.ts @@ -0,0 +1,27 @@ +import { SavedView } from '@/shared/modules/saved-views/types/SavedViewsConfig'; + +const influential: SavedView = { + id: 'influential', + label: 'Influential', + filter: { + search: '', + relation: 'and', + order: { + prop: 'lastActivity', + order: 'descending', + }, + settings: { + bot: 'exclude', + teamMember: 'exclude', + organization: 'exclude', + }, + + reach: { + operator: '>=', + value: 500, + exclude: false, + }, + }, +}; + +export default influential; diff --git a/frontend/src/modules/member/config/saved-views/views/most-engaged.ts b/frontend/src/modules/member/config/saved-views/views/most-engaged.ts new file mode 100644 index 0000000000..57bf382ba6 --- /dev/null +++ b/frontend/src/modules/member/config/saved-views/views/most-engaged.ts @@ -0,0 +1,26 @@ +import { SavedView } from '@/shared/modules/saved-views/types/SavedViewsConfig'; + +const mostEngaged: SavedView = { + id: 'most-engaged', + label: 'Most engaged', + filter: { + search: '', + relation: 'and', + order: { + prop: 'lastActivity', + order: 'descending', + }, + settings: { + bot: 'exclude', + teamMember: 'exclude', + organization: 'exclude', + }, + + engagementLevel: { + value: ['fan', 'ultra'], + exclude: false, + }, + }, +}; + +export default mostEngaged; diff --git a/frontend/src/modules/member/config/saved-views/views/new-and-active.ts b/frontend/src/modules/member/config/saved-views/views/new-and-active.ts new file mode 100644 index 0000000000..a4eff4d00f --- /dev/null +++ b/frontend/src/modules/member/config/saved-views/views/new-and-active.ts @@ -0,0 +1,34 @@ +import { SavedView } from '@/shared/modules/saved-views/types/SavedViewsConfig'; +import moment from 'moment'; + +const newAndActive: SavedView = { + id: 'new-and-active', + label: 'New and active', + filter: { + search: '', + relation: 'and', + order: { + prop: 'createdBy', + order: 'descending', + }, + settings: { + bot: 'exclude', + teamMember: 'exclude', + organization: 'exclude', + }, + + joinedDate: { + operator: 'after', + value: moment().subtract(1, 'month').format('YYYY-MM-DD'), + exclude: false, + }, + + lastActivityDate: { + operator: 'after', + value: moment().subtract(1, 'month').format('YYYY-MM-DD'), + exclude: false, + }, + }, +}; + +export default newAndActive; diff --git a/frontend/src/modules/member/config/saved-views/views/slipping-away.ts b/frontend/src/modules/member/config/saved-views/views/slipping-away.ts new file mode 100644 index 0000000000..3368e152b4 --- /dev/null +++ b/frontend/src/modules/member/config/saved-views/views/slipping-away.ts @@ -0,0 +1,33 @@ +import { SavedView } from '@/shared/modules/saved-views/types/SavedViewsConfig'; +import moment from 'moment'; + +const slippingAway: SavedView = { + id: 'slipping-away', + label: 'Slipping away', + filter: { + search: '', + relation: 'and', + order: { + prop: 'lastActivity', + order: 'descending', + }, + settings: { + bot: 'exclude', + teamMember: 'exclude', + organization: 'exclude', + }, + + engagementLevel: { + value: ['fan', 'ultra'], + exclude: false, + }, + + lastActivityDate: { + operator: 'after', + value: moment().subtract(1, 'month').format('YYYY-MM-DD'), + exclude: false, + }, + }, +}; + +export default slippingAway; diff --git a/frontend/src/modules/member/config/saved-views/views/team-members.ts b/frontend/src/modules/member/config/saved-views/views/team-members.ts new file mode 100644 index 0000000000..d7e8e092e9 --- /dev/null +++ b/frontend/src/modules/member/config/saved-views/views/team-members.ts @@ -0,0 +1,21 @@ +import { SavedView } from '@/shared/modules/saved-views/types/SavedViewsConfig'; + +const teamMembers: SavedView = { + id: 'team-members', + label: 'Team members', + filter: { + search: '', + relation: 'and', + order: { + prop: 'lastActivity', + order: 'descending', + }, + settings: { + bot: 'exclude', + teamMember: 'filter', + organization: 'exclude', + }, + }, +}; + +export default teamMembers; diff --git a/frontend/src/modules/member/pages/member-list-page.vue b/frontend/src/modules/member/pages/member-list-page.vue index b46bfec76d..33124510cf 100644 --- a/frontend/src/modules/member/pages/member-list-page.vue +++ b/frontend/src/modules/member/pages/member-list-page.vue @@ -51,11 +51,12 @@ - + @@ -77,7 +78,9 @@ import { MemberService } from '@/modules/member/member-service'; import { MemberPermissions } from '@/modules/member/member-permissions'; import { mapGetters } from '@/shared/vuex/vuex.helpers'; import { FilterQuery } from '@/shared/modules/filters/types/FilterQuery'; +import CrSavedViews from '@/shared/modules/saved-views/components/SavedViews.vue'; import { memberFilters, memberSearchFilter } from '../config/filters/main'; +import { memberSavedViews, memberViews } from '../config/saved-views/main'; // import MemberListFilter from '@/modules/member/components/list/member-list-filter.vue'; // import MemberListTable from '@/modules/member/components/list/member-list-table.vue'; // import MemberListTabs from '@/modules/member/components/list/member-list-tabs.vue'; diff --git a/frontend/src/shared/modules/filters/components/Filter.vue b/frontend/src/shared/modules/filters/components/Filter.vue index ac5876b81b..213b81a47d 100644 --- a/frontend/src/shared/modules/filters/components/Filter.vue +++ b/frontend/src/shared/modules/filters/components/Filter.vue @@ -36,12 +36,14 @@ import { SearchFilterConfig } from '@/shared/modules/filters/types/filterTypes/S import { useRoute, useRouter } from 'vue-router'; import { filterApiService } from '@/shared/modules/filters/services/filter-api.service'; import { FilterQuery } from '@/shared/modules/filters/types/FilterQuery'; +import { SavedViewsConfig } from '@/shared/modules/saved-views/types/SavedViewsConfig'; const props = defineProps<{ modelValue: Filter, config: Record, customConfig?: Record, searchConfig: SearchFilterConfig, + savedViewsConfig?: SavedViewsConfig, }>(); const emit = defineEmits<{(e: 'update:modelValue', value: Filter), (e: 'fetch', value: FilterQuery),}>(); @@ -57,7 +59,7 @@ const filters = computed({ }, set(value: Filter) { const { - config, search, relation, order, pagination, ...filterValues + settings, search, relation, order, pagination, ...filterValues } = value; filterList.value = Object.keys(filterValues); emit('update:modelValue', value); @@ -73,20 +75,23 @@ const switchOperator = () => { const removeFilter = (key) => { open.value = ''; filterList.value = filterList.value.filter((el) => el !== key); - filters.value[key] = undefined; + delete filters.value[key]; }; const { setQuery, parseQuery } = filterQueryService(); const { buildApiFilter } = filterApiService(); const fetch = (value: Filter) => { - const data = buildApiFilter(value, { ...props.config, ...props.customConfig }, props.searchConfig); + const data = buildApiFilter(value, { ...props.config, ...props.customConfig }, props.searchConfig, props.savedViewsConfig); emit('fetch', data); - console.log('fetch', data); }; watch(() => filters.value, (value: Filter) => { fetch(value); + const { + settings, search, relation, order, pagination, ...filterValues + } = value; + filterList.value = Object.keys(filterValues); const query = setQuery(value); router.push({ query }); }, { deep: true }); @@ -96,7 +101,7 @@ watch(() => route.query, (query) => { const parsed = parseQuery(query, { ...props.config, ...props.customConfig, - }); + }, props.savedViewsConfig); if (!parsed || Object.keys(parsed).length === 0) { const query = setQuery(props.modelValue); router.push({ query }); diff --git a/frontend/src/shared/modules/filters/components/FilterItem.vue b/frontend/src/shared/modules/filters/components/FilterItem.vue index 207f9dbab8..83e84cd312 100644 --- a/frontend/src/shared/modules/filters/components/FilterItem.vue +++ b/frontend/src/shared/modules/filters/components/FilterItem.vue @@ -11,7 +11,7 @@ > diff --git a/frontend/src/shared/modules/filters/config/apiFilterRenderer/boolean.filter.renderer.ts b/frontend/src/shared/modules/filters/config/apiFilterRenderer/boolean.filter.renderer.ts new file mode 100644 index 0000000000..1317b82551 --- /dev/null +++ b/frontend/src/shared/modules/filters/config/apiFilterRenderer/boolean.filter.renderer.ts @@ -0,0 +1,7 @@ +import { BooleanFilterValue } from '@/shared/modules/filters/types/filterTypes/BooleanFilterConfig'; + +export const booleanApiFilterRenderer = (property: string, { value }: BooleanFilterValue): any[] => [ + { + [property]: value, + }, +]; diff --git a/frontend/src/shared/modules/filters/config/apiFilterRenderer/date.filter.renderer.ts b/frontend/src/shared/modules/filters/config/apiFilterRenderer/date.filter.renderer.ts new file mode 100644 index 0000000000..222327007c --- /dev/null +++ b/frontend/src/shared/modules/filters/config/apiFilterRenderer/date.filter.renderer.ts @@ -0,0 +1,7 @@ +import { DateFilterValue } from '@/shared/modules/filters/types/filterTypes/DateFilterConfig'; + +export const dateApiFilterRenderer = (property: string, { value }: DateFilterValue): any[] => [ + { + [property]: value, + }, +]; diff --git a/frontend/src/shared/modules/filters/config/apiFilterRenderer/multiselect.filter.renderer.ts b/frontend/src/shared/modules/filters/config/apiFilterRenderer/multiselect.filter.renderer.ts new file mode 100644 index 0000000000..7f61d4a6c0 --- /dev/null +++ b/frontend/src/shared/modules/filters/config/apiFilterRenderer/multiselect.filter.renderer.ts @@ -0,0 +1,7 @@ +import { MultiSelectFilterValue } from '@/shared/modules/filters/types/filterTypes/MultiSelectFilterConfig'; + +export const multiSelectApiFilterRenderer = (property: string, { value }: MultiSelectFilterValue): any[] => [ + { + [property]: value, + }, +]; diff --git a/frontend/src/shared/modules/filters/config/apiFilterRenderer/number.filter.renderer.ts b/frontend/src/shared/modules/filters/config/apiFilterRenderer/number.filter.renderer.ts new file mode 100644 index 0000000000..0590e0c5ad --- /dev/null +++ b/frontend/src/shared/modules/filters/config/apiFilterRenderer/number.filter.renderer.ts @@ -0,0 +1,7 @@ +import { NumberFilterValue } from '@/shared/modules/filters/types/filterTypes/NumberFilterConfig'; + +export const numberApiFilterRenderer = (property: string, { value }: NumberFilterValue): any[] => [ + { + [property]: value, + }, +]; diff --git a/frontend/src/shared/modules/filters/config/apiFilterRenderer/select.filter.renderer.ts b/frontend/src/shared/modules/filters/config/apiFilterRenderer/select.filter.renderer.ts new file mode 100644 index 0000000000..fb487b9ec1 --- /dev/null +++ b/frontend/src/shared/modules/filters/config/apiFilterRenderer/select.filter.renderer.ts @@ -0,0 +1,7 @@ +import { SelectFilterValue } from '@/shared/modules/filters/types/filterTypes/SelectFilterConfig'; + +export const selectApiFilterRenderer = (property: string, { value }: SelectFilterValue): any[] => [ + { + [property]: value, + }, +]; diff --git a/frontend/src/shared/modules/filters/config/apiFilterRendererByType.ts b/frontend/src/shared/modules/filters/config/apiFilterRendererByType.ts new file mode 100644 index 0000000000..2ad5e9548d --- /dev/null +++ b/frontend/src/shared/modules/filters/config/apiFilterRendererByType.ts @@ -0,0 +1,17 @@ +import { FilterConfigType } from '@/shared/modules/filters/types/FilterConfig'; +import { booleanApiFilterRenderer } from './apiFilterRenderer/boolean.filter.renderer'; +import { numberApiFilterRenderer } from './apiFilterRenderer/number.filter.renderer'; +import { dateApiFilterRenderer } from './apiFilterRenderer/date.filter.renderer'; +import { selectApiFilterRenderer } from './apiFilterRenderer/select.filter.renderer'; +import { + multiSelectApiFilterRenderer, +} from './apiFilterRenderer/multiselect.filter.renderer'; + +export const apiFilterRendererByType: Record any[]> = { + [FilterConfigType.BOOLEAN]: booleanApiFilterRenderer, + [FilterConfigType.NUMBER]: numberApiFilterRenderer, + [FilterConfigType.DATE]: dateApiFilterRenderer, + [FilterConfigType.SELECT]: selectApiFilterRenderer, + [FilterConfigType.MULTISELECT]: multiSelectApiFilterRenderer, + [FilterConfigType.CUSTOM]: () => [], +}; diff --git a/frontend/src/shared/modules/filters/config/itemLabelRenderer/boolean.label.renderer.ts b/frontend/src/shared/modules/filters/config/itemLabelRenderer/boolean.label.renderer.ts new file mode 100644 index 0000000000..433f90aef8 --- /dev/null +++ b/frontend/src/shared/modules/filters/config/itemLabelRenderer/boolean.label.renderer.ts @@ -0,0 +1,6 @@ +import { BooleanFilterValue } from '@/shared/modules/filters/types/filterTypes/BooleanFilterConfig'; + +export const booleanItemLabelRenderer = (property: string, { value }: BooleanFilterValue): string => { + console.log(value); + return `${property}: ${value ? 'True' : 'False'}`; +}; diff --git a/frontend/src/shared/modules/filters/config/itemLabelRenderer/date.label.renderer.ts b/frontend/src/shared/modules/filters/config/itemLabelRenderer/date.label.renderer.ts new file mode 100644 index 0000000000..f26cc6c87c --- /dev/null +++ b/frontend/src/shared/modules/filters/config/itemLabelRenderer/date.label.renderer.ts @@ -0,0 +1,3 @@ +import { DateFilterValue } from '@/shared/modules/filters/types/filterTypes/DateFilterConfig'; + +export const dateItemLabelRenderer = (property: string, { value }: DateFilterValue): string => `${property}: ${value}`; diff --git a/frontend/src/shared/modules/filters/config/itemLabelRenderer/multiselect.label.renderer.ts b/frontend/src/shared/modules/filters/config/itemLabelRenderer/multiselect.label.renderer.ts new file mode 100644 index 0000000000..eb0d049e8b --- /dev/null +++ b/frontend/src/shared/modules/filters/config/itemLabelRenderer/multiselect.label.renderer.ts @@ -0,0 +1,3 @@ +import { MultiSelectFilterValue } from '@/shared/modules/filters/types/filterTypes/MultiSelectFilterConfig'; + +export const multiSelectItemLabelRenderer = (property: string, { value }: MultiSelectFilterValue): string => `${property}: ${value.join(',')}`; diff --git a/frontend/src/shared/modules/filters/config/itemLabelRenderer/number.label.renderer.ts b/frontend/src/shared/modules/filters/config/itemLabelRenderer/number.label.renderer.ts new file mode 100644 index 0000000000..87ab374773 --- /dev/null +++ b/frontend/src/shared/modules/filters/config/itemLabelRenderer/number.label.renderer.ts @@ -0,0 +1,3 @@ +import { NumberFilterValue } from '@/shared/modules/filters/types/filterTypes/NumberFilterConfig'; + +export const numberItemLabelRenderer = (property: string, { value }: NumberFilterValue): string => `${property}: ${value}`; diff --git a/frontend/src/shared/modules/filters/config/itemLabelRenderer/select.label.renderer.ts b/frontend/src/shared/modules/filters/config/itemLabelRenderer/select.label.renderer.ts new file mode 100644 index 0000000000..1372fbfeaf --- /dev/null +++ b/frontend/src/shared/modules/filters/config/itemLabelRenderer/select.label.renderer.ts @@ -0,0 +1,3 @@ +import { SelectFilterValue } from '@/shared/modules/filters/types/filterTypes/SelectFilterConfig'; + +export const selectItemLabelRenderer = (property: string, { value }: SelectFilterValue): string => `${property}: ${value}`; diff --git a/frontend/src/shared/modules/filters/config/itemLabelRendererByType.ts b/frontend/src/shared/modules/filters/config/itemLabelRendererByType.ts new file mode 100644 index 0000000000..f5d279eefd --- /dev/null +++ b/frontend/src/shared/modules/filters/config/itemLabelRendererByType.ts @@ -0,0 +1,17 @@ +import { FilterConfigType } from '@/shared/modules/filters/types/FilterConfig'; +import { booleanItemLabelRenderer } from './itemLabelRenderer/boolean.label.renderer'; +import { numberItemLabelRenderer } from './itemLabelRenderer/number.label.renderer'; +import { dateItemLabelRenderer } from './itemLabelRenderer/date.label.renderer'; +import { selectItemLabelRenderer } from './itemLabelRenderer/select.label.renderer'; +import { + multiSelectItemLabelRenderer, +} from './itemLabelRenderer/multiselect.label.renderer'; + +export const itemLabelRendererByType: Record string> = { + [FilterConfigType.BOOLEAN]: booleanItemLabelRenderer, + [FilterConfigType.NUMBER]: numberItemLabelRenderer, + [FilterConfigType.DATE]: dateItemLabelRenderer, + [FilterConfigType.SELECT]: selectItemLabelRenderer, + [FilterConfigType.MULTISELECT]: multiSelectItemLabelRenderer, + [FilterConfigType.CUSTOM]: () => '', +}; diff --git a/frontend/src/shared/modules/filters/config/queryUrlParserByType.ts b/frontend/src/shared/modules/filters/config/queryUrlParserByType.ts index b9446611dd..991ef4dac3 100644 --- a/frontend/src/shared/modules/filters/config/queryUrlParserByType.ts +++ b/frontend/src/shared/modules/filters/config/queryUrlParserByType.ts @@ -5,11 +5,11 @@ import { dateQueryUrlParser } from '@/shared/modules/filters/config/queryUrlPars import { selectQueryUrlParser } from '@/shared/modules/filters/config/queryUrlParser/select.parser'; import { multiSelectQueryUrlParser } from '@/shared/modules/filters/config/queryUrlParser/multiselect.parser'; -export const queryUrlParserByType: Record any) | null> = { +export const queryUrlParserByType: Record any> = { [FilterConfigType.BOOLEAN]: booleanQueryUrlParser, [FilterConfigType.NUMBER]: numberQueryUrlParser, [FilterConfigType.DATE]: dateQueryUrlParser, [FilterConfigType.SELECT]: selectQueryUrlParser, [FilterConfigType.MULTISELECT]: multiSelectQueryUrlParser, - [FilterConfigType.CUSTOM]: null, + [FilterConfigType.CUSTOM]: () => null, }; diff --git a/frontend/src/shared/modules/filters/services/filter-api.service.ts b/frontend/src/shared/modules/filters/services/filter-api.service.ts index 49e3df897d..59d7ea52e0 100644 --- a/frontend/src/shared/modules/filters/services/filter-api.service.ts +++ b/frontend/src/shared/modules/filters/services/filter-api.service.ts @@ -1,24 +1,28 @@ import { Filter, FilterConfig } from '@/shared/modules/filters/types/FilterConfig'; import { SearchFilterConfig } from '@/shared/modules/filters/types/filterTypes/SearchFilterConfig'; import { FilterQuery } from '@/shared/modules/filters/types/FilterQuery'; +import { SavedViewsConfig } from '@/shared/modules/saved-views/types/SavedViewsConfig'; export const filterApiService = () => { - function buildApiFilter(values: Filter, configuration: Record, searchConfig: SearchFilterConfig): FilterQuery { + function buildApiFilter( + values: Filter, + configuration: Record, + searchConfig: SearchFilterConfig, + savedViewsConfig?: SavedViewsConfig, + ): FilterQuery { const { search, relation, order, pagination, - config, + settings, ...filterValues } = values; - // Remove when saved views done - console.log(config); - let baseFilters: any[] = []; let filters: any[] = []; + // Search if (search.length > 0) { baseFilters = [ ...baseFilters, @@ -26,8 +30,20 @@ export const filterApiService = () => { ]; } - // TODO: config filter parsing + // Settings + if (savedViewsConfig) { + Object.entries(settings).forEach(([setting, value]) => { + const filter = savedViewsConfig.settings[setting]?.apiFilterRenderer(value); + if (filter) { + baseFilters = [ + ...baseFilters, + ...filter, + ]; + } + }); + } + // Filter values Object.entries(filterValues).forEach(([key, values]) => { const filter = configuration[key]?.apiFilterRenderer(values); if (filter && filter.length > 0) { @@ -38,6 +54,7 @@ export const filterApiService = () => { } }); + // build object const filter = { and: [ ...baseFilters, diff --git a/frontend/src/shared/modules/filters/services/filter-query.service.ts b/frontend/src/shared/modules/filters/services/filter-query.service.ts index 162c09dfa5..69f41f4c1d 100644 --- a/frontend/src/shared/modules/filters/services/filter-query.service.ts +++ b/frontend/src/shared/modules/filters/services/filter-query.service.ts @@ -1,10 +1,11 @@ import { Filter, FilterConfig } from '@/shared/modules/filters/types/FilterConfig'; import { queryUrlParserByType } from '@/shared/modules/filters/config/queryUrlParserByType'; import { CustomFilterConfig } from '@/shared/modules/filters/types/filterTypes/CustomFilterConfig'; +import { SavedViewsConfig } from '@/shared/modules/saved-views/types/SavedViewsConfig'; export const filterQueryService = () => { // Parses url query params and puts them in nested object format - function parseQuery(query: Record, config: Record) { + function parseQuery(query: Record, config: Record, savedViewsConfig?: SavedViewsConfig) { const object: Record = {}; Object.entries(query).forEach(([key, value]) => { const [mainKey, subKey] = key.split('.'); @@ -21,7 +22,15 @@ export const filterQueryService = () => { }); // Url params come out as strings so we need to transform them to boolean, number or array Object.keys(object).forEach((key) => { - if (key in config) { + if (key === 'settings' && savedViewsConfig) { + Object.keys(object[key]).forEach((setting) => { + object[key][setting] = savedViewsConfig.settings[setting].queryUrlParser(object[key][setting]); + }); + } else if (key === 'pagination') { + Object.keys(object[key]).forEach((paginationProperty) => { + object[key][paginationProperty] = +object[key][paginationProperty]; + }); + } else if (key in config) { const { type } = config[key]; const queryUrlParser = queryUrlParserByType[type] ?? (config[key] as CustomFilterConfig).queryUrlParser; if (queryUrlParser) { diff --git a/frontend/src/shared/modules/filters/types/FilterConfig.ts b/frontend/src/shared/modules/filters/types/FilterConfig.ts index 39034ee465..7d2dec1eab 100644 --- a/frontend/src/shared/modules/filters/types/FilterConfig.ts +++ b/frontend/src/shared/modules/filters/types/FilterConfig.ts @@ -26,18 +26,21 @@ export type FilterConfig = NumberFilterConfig | DateFilterConfig | CustomFilterConfig -interface FilterObject { +export interface FilterStatic { search: string; relation: 'and' | 'or', order: { prop: string, order: 'descending' | 'ascending' }, + settings: Record +} + +export type FilterObject = FilterStatic & Record + +export interface Filter extends FilterObject { pagination: { page: number, perPage: number }, - config: Record } - -export type Filter = FilterObject & Record diff --git a/frontend/src/shared/modules/saved-views/components/SavedViews.vue b/frontend/src/shared/modules/saved-views/components/SavedViews.vue new file mode 100644 index 0000000000..eb34c5ed96 --- /dev/null +++ b/frontend/src/shared/modules/saved-views/components/SavedViews.vue @@ -0,0 +1,130 @@ + + + + + + + diff --git a/frontend/src/shared/modules/saved-views/types/SavedViewsConfig.ts b/frontend/src/shared/modules/saved-views/types/SavedViewsConfig.ts new file mode 100644 index 0000000000..07e6f8f0ba --- /dev/null +++ b/frontend/src/shared/modules/saved-views/types/SavedViewsConfig.ts @@ -0,0 +1,19 @@ +import { FilterObject } from '@/shared/modules/filters/types/FilterConfig'; + +export interface SavedView { + id: string; + label: string; + filter: FilterObject & Record +} + +export interface SavedViewsSetting { + component?: any; + defaultValue: T, + queryUrlParser: (value: string) => T; + apiFilterRenderer: (value: T) => any[]; +} + +export interface SavedViewsConfig { + defaultView: SavedView; + settings: Record> +}