Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
fde8385
wip
cannarocks Nov 6, 2023
bcf9663
feat(filters-prototype): add fake filters
cannarocks Nov 8, 2023
ae74a2c
feat(filters-prototype): ux changes during design critique
cannarocks Nov 9, 2023
3fe32e2
feat(schema): update api
marcbon Nov 17, 2023
6b95c14
feat(ux-filters): setup base filter state for ux data
cannarocks Nov 18, 2023
bdb1fa0
feat(filters): add basic filter flow
cannarocks Nov 19, 2023
7f64690
fix: fixed disabled on selected useCases
iDome89 Nov 20, 2023
406dfe4
fix: fixed disabled issue when severity is selected
iDome89 Nov 20, 2023
e36e348
Merge pull request #693 from AppQuality/fix-selected-filter
marcbon Nov 20, 2023
0645cc8
feat: refactored filters component to be sticky
iDome89 Nov 20, 2023
861f462
🌍 feat(translation.json): add translations for empty insights section…
marcbon Nov 20, 2023
ea16448
Merge pull request #694 from AppQuality/insights-empty-state
marcbon Nov 20, 2023
7b68a76
Merge pull request #695 from AppQuality/fix-selected-filter
marcbon Nov 20, 2023
519f081
🔧 chore(StickyContainer.tsx): adjust top spacing in StickyContainer c…
marcbon Nov 20, 2023
946d3a0
🌍 feat(translation.json): add new translation keys for insights section
marcbon Nov 20, 2023
e9672f0
🐛 fix(Insights/index.tsx): fix import paths for filters components
marcbon Nov 20, 2023
527e8f8
🔥 refactor(Navigation.tsx): remove unused import of StickyContainer f…
marcbon Nov 20, 2023
378b870
🔥 refactor(widgets.tsx): remove console.log statements in useEffect
marcbon Nov 20, 2023
7becb90
🔀 refactor(FiltersCounter.tsx): extract filters count logic into a se…
marcbon Nov 20, 2023
5d52bfe
🐛 fix(Insights/index.tsx): fix conditional rendering of Empty component
marcbon Nov 20, 2023
ded150c
🔥 refactor(Insights/index.tsx): remove unused import and simplify con…
marcbon Nov 20, 2023
afcde14
🌍 chore(translation.json): update severity filter item label in Engli…
marcbon Nov 20, 2023
463c55b
🔧 fix(FiltersDropdowns.tsx): fix padding value in FlexWrapper component
marcbon Nov 20, 2023
2b2b821
🔧 chore(Navigation.tsx): adjust top position of StickyContainer in In…
marcbon Nov 20, 2023
0002999
🔥 refactor(fakeData.ts): remove unused fake data and clusters array
marcbon Nov 20, 2023
e66bf72
🐛 fix(SeverityFilter.tsx): handle cases where counters[item.id] is un…
marcbon Nov 20, 2023
33fbadc
🔧 fix(FiltersTags.tsx): import useCallback from react
marcbon Nov 21, 2023
130ff5c
🔥 refactor(FiltersTags.tsx): remove unused import of useCallback from…
marcbon Nov 21, 2023
c426cd4
🔧 chore(SeverityFilter.tsx): refactor SeverityFilter component
marcbon Nov 21, 2023
9a2eeb6
feat: added loading state and fixed dropdown label spacing
iDome89 Nov 21, 2023
0fb6225
Merge pull request #696 from AppQuality/fix-ux-filters-tags-counters
marcbon Nov 21, 2023
d8566d4
Merge pull request #697 from AppQuality/loading-spacings-fixes
marcbon Nov 21, 2023
d823ca0
🐛 fix(FiltersCounter.tsx): fix issue with missing campaign id in useG…
marcbon Nov 22, 2023
91c862f
🎨 style(FiltersDropdowns.tsx): add padding to FlexWrapper component
marcbon Nov 22, 2023
d7227a4
🔥 refactor(FiltersHeader.tsx): remove marginBottom style property
marcbon Nov 22, 2023
c6dc24e
🐛 fix(Content.tsx): add missing import statement for FiltersCounter c…
marcbon Nov 22, 2023
a7a8f21
🌍 chore(translation.json): update translation for insight severity la…
marcbon Nov 22, 2023
81f04e9
🐛 fix(useFilterData.ts): fix counter key for 'all' cluster in useFilt…
marcbon Nov 22, 2023
8ee0f6a
🔀 refactor(useUxData.tsx): rename severities variable to severitiesData
marcbon Nov 22, 2023
4cae188
🔧 fix(FiltersTags.tsx): remove unused styles and components
marcbon Nov 22, 2023
0c3dec4
fix: added correct text to selected typology in dropdown
iDome89 Nov 23, 2023
67dfc95
Merge pull request #700 from AppQuality/typology-dropdown-fix
iDome89 Nov 23, 2023
ca151e3
🔧 chore(index.tsx): reformat ReactDOM.render to use React.StrictMode
marcbon Nov 23, 2023
bed1ffb
🔧 fix(formModel.ts): update validation error messages to use translat…
marcbon Nov 23, 2023
35de5ef
🌐 chore(translation.json): update translation for "UseCase" key in En…
marcbon Nov 23, 2023
249a8b8
🐛 fix(translation.json): remove unnecessary translations
marcbon Nov 23, 2023
ef34d93
🐛 fix(Content.tsx): handle case when data or data.findings is null or…
marcbon Nov 23, 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
2 changes: 2 additions & 0 deletions src/app/store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import filterReducer from '../features/campaignsFilter/campaignsFilterSlice';
import expressReducer from '../features/express/expressSlice';
import { strapiSlice } from '../features/backoffice/strapi';
import bugsPageReducer from '../features/bugsPage/bugsPageSlice';
import uxFilterReducer from '../features/uxFilters';

export const store = configureStore({
reducer: {
Expand All @@ -17,6 +18,7 @@ export const store = configureStore({
filters: filterReducer,
express: expressReducer,
bugsPage: bugsPageReducer,
uxFilters: uxFilterReducer,
[unguessApiSlice.reducerPath]: unguessApiSlice.reducer,
[strapiSlice.reducerPath]: strapiSlice.reducer,
},
Expand Down
5 changes: 3 additions & 2 deletions src/common/components/CustomStatusDrawer/formModel.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import * as Yup from 'yup';
import { BugCustomStatus } from 'src/features/api';
import { t } from 'i18next';

export interface CustomStatusFormProps {
custom_statuses: BugCustomStatus[];
Expand All @@ -10,8 +11,8 @@ export const validationSchema = Yup.object().shape({
Yup.object().shape({
id: Yup.number(),
name: Yup.string()
.max(17, '__BUGS_PAGE_CUSTOM_STATUS_DRAWER_CUSTOM_STATUS_MAX')
.required('__BUGS_PAGE_CUSTOM_STATUS_DRAWER_CUSTOM_STATUS_REQUIRED'),
.max(17, t('__BUGS_PAGE_CUSTOM_STATUS_DRAWER_CUSTOM_STATUS_MAX'))
.required(t('__BUGS_PAGE_CUSTOM_STATUS_DRAWER_CUSTOM_STATUS_REQUIRED')),
color: Yup.string(),
phase: Yup.object().shape({
id: Yup.number().required(),
Expand Down
35 changes: 35 additions & 0 deletions src/common/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,14 @@ export interface paths {
};
};
};
'/campaigns/{cid}/clusters': {
get: operations['get-campaigns-cid-clusters'];
parameters: {
path: {
cid: string;
};
};
};
'/campaigns/{cid}/custom_statuses': {
get: operations['get-campaigns-cid-custom-statuses'];
delete: operations['delete-campaigns-cid-custom_statuses'];
Expand Down Expand Up @@ -521,6 +529,11 @@ export interface components {
CampaignWithOutput: components['schemas']['Campaign'] & {
outputs?: components['schemas']['Output'][];
};
/** Cluster */
Cluster: {
id?: number;
name?: string;
};
/**
* Coin
* @description A coin package is a set of coins (free or paid).
Expand Down Expand Up @@ -1247,6 +1260,26 @@ export interface operations {
500: components['responses']['Error'];
};
};
'get-campaigns-cid-clusters': {
parameters: {
path: {
cid: string;
};
};
responses: {
/** OK */
200: {
content: {
'application/json': {
items?: components['schemas']['Cluster'][];
};
};
};
400: components['responses']['Error'];
403: components['responses']['Error'];
500: components['responses']['Error'];
};
};
'get-campaigns-cid-custom-statuses': {
parameters: {
path: {
Expand Down Expand Up @@ -1636,6 +1669,8 @@ export interface operations {
};
query: {
showAsCustomer?: boolean;
/** filterBy[<fieldName>]=<fieldValue> */
filterBy?: components['parameters']['filterBy'];
};
};
responses: {
Expand Down
32 changes: 31 additions & 1 deletion src/features/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,12 @@ const injectedRtkApi = api.injectEndpoints({
>({
query: (queryArg) => ({ url: `/campaigns/${queryArg.cid}/bugTypes` }),
}),
getCampaignsByCidClusters: build.query<
GetCampaignsByCidClustersApiResponse,
GetCampaignsByCidClustersApiArg
>({
query: (queryArg) => ({ url: `/campaigns/${queryArg.cid}/clusters` }),
}),
getCampaignsByCidCustomStatuses: build.query<
GetCampaignsByCidCustomStatusesApiResponse,
GetCampaignsByCidCustomStatusesApiArg
Expand Down Expand Up @@ -231,7 +237,10 @@ const injectedRtkApi = api.injectEndpoints({
>({
query: (queryArg) => ({
url: `/campaigns/${queryArg.cid}/ux`,
params: { showAsCustomer: queryArg.showAsCustomer },
params: {
showAsCustomer: queryArg.showAsCustomer,
filterBy: queryArg.filterBy,
},
}),
}),
getCampaignsByCidWidgets: build.query<
Expand Down Expand Up @@ -438,6 +447,9 @@ const injectedRtkApi = api.injectEndpoints({
body: queryArg.body,
}),
}),
getMediaById: build.query<GetMediaByIdApiResponse, GetMediaByIdApiArg>({
query: (queryArg) => ({ url: `/media/${queryArg.id}` }),
}),
}),
overrideExisting: false,
});
Expand Down Expand Up @@ -611,6 +623,12 @@ export type GetCampaignsByCidBugTypesApiArg = {
/** Campaign id */
cid: string;
};
export type GetCampaignsByCidClustersApiResponse = /** status 200 OK */ {
items?: Cluster[];
};
export type GetCampaignsByCidClustersApiArg = {
cid: string;
};
export type GetCampaignsByCidCustomStatusesApiResponse =
/** status 200 OK */ BugCustomStatus[];
export type GetCampaignsByCidCustomStatusesApiArg = {
Expand Down Expand Up @@ -814,6 +832,8 @@ export type GetCampaignsByCidUxApiArg = {
/** Campaign id */
cid: string;
showAsCustomer?: boolean;
/** filterBy[<fieldName>]=<fieldValue> */
filterBy?: any;
};
export type GetCampaignsByCidWidgetsApiResponse =
/** status 200 OK */
Expand Down Expand Up @@ -1092,6 +1112,10 @@ export type DeleteWorkspacesByWidUsersApiArg = {
include_shared?: boolean;
};
};
export type GetMediaByIdApiResponse = unknown;
export type GetMediaByIdApiArg = {
id: string;
};
export type Error = {
message: string;
code: number;
Expand Down Expand Up @@ -1293,6 +1317,10 @@ export type BugAdditionalField = {
name: string;
value: string;
} & (BugAdditionalFieldRegex | BugAdditionalFieldSelect);
export type Cluster = {
id: number;
name: string;
};
export type ReportExtensions =
| 'pdf'
| 'doc'
Expand Down Expand Up @@ -1439,6 +1467,7 @@ export const {
usePatchCampaignsByCidBugsAndBidMutation,
useGetCampaignsByCidBugsAndBidSiblingsQuery,
useGetCampaignsByCidBugTypesQuery,
useGetCampaignsByCidClustersQuery,
useGetCampaignsByCidCustomStatusesQuery,
usePatchCampaignsByCidCustomStatusesMutation,
useDeleteCampaignsByCidCustomStatusesMutation,
Expand Down Expand Up @@ -1477,4 +1506,5 @@ export const {
useGetWorkspacesByWidUsersQuery,
usePostWorkspacesByWidUsersMutation,
useDeleteWorkspacesByWidUsersMutation,
useGetMediaByIdQuery,
} = injectedRtkApi;
58 changes: 58 additions & 0 deletions src/features/uxFilters/clusterFilter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { useAppSelector } from 'src/app/hooks';

export type ClusterFilterType = {
clusters: {
available: { id: number; name: string }[];
selected: { id: number; name: string }[];
};
};

export const ClusterFilter = {
reset: (state: ClusterFilterType) => ({
clusters: {
...ClusterFilter.getCurrent(state),
selected: [],
},
}),
getCurrent: (state?: ClusterFilterType) => ({
available: state?.clusters?.available ? state.clusters.available : [],
selected: state?.clusters?.selected ? state.clusters.selected : [],
}),
setAvailable: (
state: ClusterFilterType,
clusters?: { id: number; name: string }[]
) => ({
clusters: {
...ClusterFilter.getCurrent(state),
...(clusters ? { available: clusters } : {}),
},
}),
filter: (
state: ClusterFilterType,
clusters?: { id: number; name: string }[]
) => ({
clusters: {
...ClusterFilter.getCurrent(state),
...(clusters ? { selected: clusters } : {}),
},
}),
getValues: () => {
const uxDataSlice = useAppSelector((state) => state.uxFilters);

if (!uxDataSlice.currentCampaign) return undefined;

if (!uxDataSlice.campaigns[uxDataSlice.currentCampaign].clusters)
return undefined;

const campaign: ClusterFilterType =
uxDataSlice.campaigns[uxDataSlice.currentCampaign];

if (!campaign.clusters.selected) return undefined;
return campaign.clusters.selected;
},
getIds: () => {
const values = ClusterFilter.getValues();
if (!values) return undefined;
return values.map((t) => t.id);
},
};
96 changes: 96 additions & 0 deletions src/features/uxFilters/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
import { createSlice } from '@reduxjs/toolkit';
import { useAppSelector } from 'src/app/hooks';
import { ClusterFilter, ClusterFilterType } from './clusterFilter';
import { SeverityFilter, SeverityFilterType } from './severityFilter';
import { InsightState, InsightStateType } from './insights';

export interface FilterState {
currentCampaign?: number;
campaigns: {
[campaign_id: string]: ClusterFilterType &
SeverityFilterType &
InsightStateType;
};
}

const initialState: FilterState = {
campaigns: {},
};

const filtersSlice = createSlice({
name: 'uxfilters',
initialState,
reducers: {
selectUxCampaign: (state, action) => {
const { campaignId, filters } = action.payload;
state.campaigns[campaignId as number] = {
...(state.campaigns[campaignId as number]
? state.campaigns[campaignId as number]
: {}),
...ClusterFilter.setAvailable(
state.campaigns[campaignId as number],
filters.clusters
),
...SeverityFilter.setAvailable(
state.campaigns[campaignId as number],
filters.severities
),
...InsightState.setAvailable(
state.campaigns[campaignId as number],
filters.insights
),
};
state.currentCampaign = campaignId;
},
updateFilters: (state, action) => {
const { filters } = action.payload;
if (!state.currentCampaign) return;

state.campaigns[state.currentCampaign] = {
...state.campaigns[state.currentCampaign],
...ClusterFilter.filter(
state.campaigns[state.currentCampaign],
filters.clusters
),
...SeverityFilter.filter(
state.campaigns[state.currentCampaign],
filters.severities
),
};
},
resetFilters(state) {
if (!state.currentCampaign) return;
state.campaigns[state.currentCampaign] = {
...state.campaigns[state.currentCampaign],
...ClusterFilter.reset(state.campaigns[state.currentCampaign]),
...SeverityFilter.reset(state.campaigns[state.currentCampaign]),
};
},
},
});

export const getSelectedUxFiltersIds = () => ({
clusters: ClusterFilter.getIds(),
severities: SeverityFilter.getIds(),
});

export const getSelectedUxFilters = () => ({
clusters: ClusterFilter.getValues(),
severities: SeverityFilter.getValues(),
});

export const getCurrentUxData = () => {
const { currentCampaign, campaigns } = useAppSelector(
(state) => state.uxFilters
);
if (!currentCampaign || !campaigns[currentCampaign as number]) return null;

const campaign = campaigns[currentCampaign as number];
if (!campaign) return false;
return campaign;
};

export const { selectUxCampaign, updateFilters, resetFilters } =
filtersSlice.actions;

export default filtersSlice.reducer;
41 changes: 41 additions & 0 deletions src/features/uxFilters/insights.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { useAppSelector } from 'src/app/hooks';

export type InsightStateType = {
insights: {
available: { id: number; name: string }[];
};
};

export const InsightState = {
getCurrent: (state?: InsightStateType) => ({
available: state?.insights?.available ? state.insights.available : [],
}),
setAvailable: (
state: InsightStateType,
insights?: { id: number; name: string }[]
) => ({
insights: {
...InsightState.getCurrent(state),
...(insights ? { available: insights } : {}),
},
}),
getValues: () => {
const uxDataSlice = useAppSelector((state) => state.uxFilters);

if (!uxDataSlice.currentCampaign) return undefined;

if (!uxDataSlice.campaigns[uxDataSlice.currentCampaign].insights)
return undefined;

const campaign: InsightStateType =
uxDataSlice.campaigns[uxDataSlice.currentCampaign];

if (!campaign.insights.available) return undefined;
return campaign.insights.available;
},
getIndex: (id: number) => {
const values = InsightState.getValues();
if (!values) return undefined;
return values.findIndex((t) => t.id === id);
},
};
Loading