-
-
+
diff --git a/src/assets/banner_suggestions/testing_automation.svg b/src/assets/banner_suggestions/testing_automation.svg
deleted file mode 100644
index eb628809d..000000000
--- a/src/assets/banner_suggestions/testing_automation.svg
+++ /dev/null
@@ -1,331 +0,0 @@
-
diff --git a/src/assets/icons/accessibility-task-icon.svg b/src/assets/icons/accessibility-task-icon.svg
new file mode 100644
index 000000000..a1a3193ff
--- /dev/null
+++ b/src/assets/icons/accessibility-task-icon.svg
@@ -0,0 +1,3 @@
+
diff --git a/src/common/components/inviteUsers/campaignSettings.tsx b/src/common/components/inviteUsers/campaignSettings.tsx
index bcce76764..b8be7573d 100644
--- a/src/common/components/inviteUsers/campaignSettings.tsx
+++ b/src/common/components/inviteUsers/campaignSettings.tsx
@@ -1,12 +1,13 @@
import {
+ AccordionNew,
Button,
+ getColor,
Label,
MD,
Modal,
ModalClose,
Notification,
Span,
- getColor,
useToast,
} from '@appquality/unguess-design-system';
import { FormikHelpers } from 'formik';
@@ -35,8 +36,6 @@ import {
FixedBody,
FlexContainer,
SettingsDivider,
- StyledAccordion,
- StyledAccordionPanel,
UsersContainer,
UsersLabel,
} from './styled';
@@ -361,42 +360,36 @@ export const CampaignSettings = () => {
)}
{projectCount > 0 && (
-
-
-
-
-
-
-
- {t('__PERMISSION_SETTINGS_PROJECT_USERS')}{' '}
-
- ({projectCount})
-
-
-
-
-
-
+
+
+ }
+ >
+
+
+
{projectUsers?.items.map((user) => (
))}
-
-
-
+
+
+
)}
{workspaceUsersError && (
@@ -412,42 +405,36 @@ export const CampaignSettings = () => {
)}
{workspaceCount > 0 && (
-
-
-
-
-
-
-
- {t('__PERMISSION_SETTINGS_WORKSPACE_USERS')}{' '}
-
- ({workspaceCount})
-
-
-
-
-
-
+
+
+ }
+ >
+
+
+
{workspaceUsers?.items.map((user) => (
))}
-
-
-
+
+
+
)}
diff --git a/src/common/components/inviteUsers/projectSettings.tsx b/src/common/components/inviteUsers/projectSettings.tsx
index 14e8b710e..ba689c224 100644
--- a/src/common/components/inviteUsers/projectSettings.tsx
+++ b/src/common/components/inviteUsers/projectSettings.tsx
@@ -1,12 +1,13 @@
import {
+ AccordionNew,
Button,
+ getColor,
Label,
MD,
Modal,
ModalClose,
Notification,
Span,
- getColor,
useToast,
} from '@appquality/unguess-design-system';
import { FormikHelpers } from 'formik';
@@ -32,8 +33,6 @@ import {
FixedBody,
FlexContainer,
SettingsDivider,
- StyledAccordion,
- StyledAccordionPanel,
UsersContainer,
UsersLabel,
} from './styled';
@@ -335,42 +334,36 @@ export const ProjectSettings = () => {
)}
{workspaceCount > 0 && (
-
-
-
-
-
-
-
- {t('__PERMISSION_SETTINGS_WORKSPACE_USERS')}{' '}
-
- ({workspaceCount})
-
-
-
-
-
-
+
+
+ }
+ >
+
+
+
{workspaceUsers?.items.map((user) => (
))}
-
-
-
+
+
+
)}
diff --git a/src/common/components/inviteUsers/styled.tsx b/src/common/components/inviteUsers/styled.tsx
index 7cb958430..7646f9c94 100644
--- a/src/common/components/inviteUsers/styled.tsx
+++ b/src/common/components/inviteUsers/styled.tsx
@@ -1,9 +1,4 @@
-import {
- Accordion,
- MD,
- Modal,
- getColor,
-} from '@appquality/unguess-design-system';
+import { getColor, MD, Modal } from '@appquality/unguess-design-system';
import styled from 'styled-components';
export const FlexContainer = styled.div<{ isLoading?: boolean }>`
@@ -25,22 +20,6 @@ export const SettingsDivider = styled.div`
padding-top: ${({ theme }) => theme.space.base * 6}px;
`;
-export const StyledAccordion = styled(Accordion)<{
- isDisabled?: boolean;
-}>`
- ${({ isDisabled }) =>
- isDisabled &&
- `
- opacity: 0.5;
- pointer-events: none;
- `}
-`;
-
-export const StyledAccordionPanel = styled(Accordion.Panel)`
- padding: 0;
- padding-left: ${({ theme }) => theme.space.xs};
-`;
-
export const UsersLabel = styled(MD)`
display: flex;
align-items: center;
diff --git a/src/common/components/inviteUsers/userItem.tsx b/src/common/components/inviteUsers/userItem.tsx
index f5501e75d..254c0a77f 100644
--- a/src/common/components/inviteUsers/userItem.tsx
+++ b/src/common/components/inviteUsers/userItem.tsx
@@ -2,10 +2,10 @@ import {
Avatar,
ButtonMenu,
Ellipsis,
+ getColor,
MD,
SM,
Span,
- getColor,
} from '@appquality/unguess-design-system';
import { useState } from 'react';
import { useTranslation } from 'react-i18next';
@@ -56,27 +56,25 @@ export const UserItem = ({
{getInitials(user.name.length ? user.name : user.email)}
-
-
+
+
{user.name.length ? user.name : user.email}{' '}
{isMe && t('__WORKSPACE_SETTINGS_CURRENT_MEMBER_YOU_LABEL')}
-
-
- {user.name.length > 0 && (
-
-
- {user.email}
-
+
+ {user.name.length > 0 && (
+
+ {user.email}
+
)}
{onResendInvite && onRemoveUser ? (
@@ -109,7 +107,7 @@ export const UserItem = ({
}}
>
{user.invitationPending && (
-
+
{t('__WORKSPACE_SETTINGS_MEMBER_RESEND_INVITE_ACTION')}
)}
diff --git a/src/common/schema.ts b/src/common/schema.ts
index 2cc0ea7d9..75eee33f2 100644
--- a/src/common/schema.ts
+++ b/src/common/schema.ts
@@ -343,6 +343,7 @@ export interface paths {
'/projects/{pid}': {
/** Retrieve projects details from an ID. */
get: operations['get-projects-projectId'];
+ delete: operations['delete-projects-projectId'];
/** Update fields of a specific project. Currently only the project name is editable. */
patch: operations['patch-projects-pid'];
parameters: {
@@ -446,26 +447,6 @@ export interface paths {
};
};
};
- '/videos/{vid}/sentiment': {
- /**
- * This endpoint generates a new sentiment for the provided video if it does not already exist.
- *
- * **Security**: Requires Bearer Authentication. Provide your bearer token in the Authorization header when making requests to protected resources. Example: Authorization: Bearer 123.
- *
- * **Path Parameters**:
- *
- * vid (string, required): The ID of the video for which the translation is to be generated.
- * Request Body (application/json):
- *
- * language (string, required): The language code for the desired translation.
- */
- post: operations['post-videos-vid-sentiment'];
- parameters: {
- path: {
- vid: string;
- };
- };
- };
'/workspaces': {
get: operations['get-workspaces'];
/** This endpoint is useful to add a new workspace. Only admin can use this. */
@@ -895,6 +876,16 @@ export interface components {
usecaseTitle: string;
})[];
};
+ MediaSentiment: {
+ value: number;
+ reason: string;
+ paragraphs: {
+ start: number;
+ end: number;
+ value: number;
+ reason: string;
+ }[];
+ };
Module:
| components['schemas']['ModuleTitle']
| components['schemas']['ModuleDate']
@@ -1236,6 +1227,7 @@ export interface components {
};
};
transcript?: components['schemas']['Transcript'];
+ sentiment?: components['schemas']['MediaSentiment'];
};
/** VideoTag */
VideoTag: {
@@ -1466,7 +1458,10 @@ export interface components {
* BannerType
* @enum {string}
*/
- BannerType: 'banner_testing_automation' | 'banner_user_experience';
+ BannerType:
+ | 'banner_testing_automation'
+ | 'banner_user_experience'
+ | 'banner_cyber_security';
/** CpReqTemplate */
CpReqTemplate: {
id: number;
@@ -1528,13 +1523,23 @@ export interface components {
/** Format: uri */
url?: string;
};
+ /** OutputModuleTaskAccessibility */
+ OutputModuleTaskAccessibility: {
+ /** @enum {string} */
+ kind: 'accessibility';
+ title: string;
+ description?: string;
+ /** Format: uri */
+ url?: string;
+ };
/** SubcomponentTask */
OutputModuleTask:
| components['schemas']['OutputModuleTaskVideo']
| components['schemas']['OutputModuleTaskBug']
| components['schemas']['OutputModuleTaskSurvey']
| components['schemas']['OutputModuleTaskModerateVideo']
- | components['schemas']['OutputModuleTaskExplorativeBug'];
+ | components['schemas']['OutputModuleTaskExplorativeBug']
+ | components['schemas']['OutputModuleTaskAccessibility'];
/** SubcomponentTouchpoints */
OutputModuleTouchpoints:
| components['schemas']['OutputModuleTouchpointsAppDesktop']
@@ -1956,6 +1961,11 @@ export interface operations {
}[];
siblings: number;
comments: number;
+ additional_fields?: {
+ slug: string;
+ value: string;
+ name: string;
+ }[];
})[];
start?: number;
limit?: number;
@@ -3132,6 +3142,23 @@ export interface operations {
500: components['responses']['Error'];
};
};
+ 'delete-projects-projectId': {
+ parameters: {
+ path: {
+ /** Project id */
+ pid: components['parameters']['pid'];
+ };
+ };
+ responses: {
+ /** OK */
+ 200: unknown;
+ 400: components['responses']['Error'];
+ 401: components['responses']['Error'];
+ 403: components['responses']['Error'];
+ 405: components['responses']['Error'];
+ 500: components['responses']['Error'];
+ };
+ };
/** Update fields of a specific project. Currently only the project name is editable. */
'patch-projects-pid': {
parameters: {
@@ -3547,41 +3574,6 @@ export interface operations {
};
};
};
- /**
- * This endpoint generates a new sentiment for the provided video if it does not already exist.
- *
- * **Security**: Requires Bearer Authentication. Provide your bearer token in the Authorization header when making requests to protected resources. Example: Authorization: Bearer 123.
- *
- * **Path Parameters**:
- *
- * vid (string, required): The ID of the video for which the translation is to be generated.
- * Request Body (application/json):
- *
- * language (string, required): The language code for the desired translation.
- */
- 'post-videos-vid-sentiment': {
- parameters: {
- path: {
- vid: string;
- };
- };
- responses: {
- /** OK */
- 200: {
- content: {
- 'application/json': { [key: string]: unknown };
- };
- };
- 400: components['responses']['Error'];
- 403: components['responses']['Error'];
- 500: components['responses']['Error'];
- };
- requestBody: {
- content: {
- 'application/json': { [key: string]: unknown };
- };
- };
- };
'get-workspaces': {
parameters: {
query: {
@@ -3973,7 +3965,7 @@ export interface operations {
/** Start pagination parameter */
start?: components['parameters']['start'];
/** Orders results */
- orderBy?: 'updated_at' | 'id';
+ orderBy?: 'updated_at' | 'id' | 'order';
/** Order value (ASC, DESC) */
order?: components['parameters']['order'];
/** filterBy[]= */
diff --git a/src/features/api/apiTags.ts b/src/features/api/apiTags.ts
index 8e697a2e3..578b553c5 100644
--- a/src/features/api/apiTags.ts
+++ b/src/features/api/apiTags.ts
@@ -280,6 +280,9 @@ unguessApi.enhanceEndpoints({
patchPlansByPidStatus: {
invalidatesTags: ['Plans', 'Projects'],
},
+ deleteProjectsByPid: {
+ invalidatesTags: ['Projects'],
+ },
},
});
diff --git a/src/features/api/index.ts b/src/features/api/index.ts
index 20ac6e677..a95c87b53 100644
--- a/src/features/api/index.ts
+++ b/src/features/api/index.ts
@@ -435,6 +435,15 @@ const injectedRtkApi = api.injectEndpoints({
body: queryArg.body,
}),
}),
+ deleteProjectsByPid: build.mutation<
+ DeleteProjectsByPidApiResponse,
+ DeleteProjectsByPidApiArg
+ >({
+ query: (queryArg) => ({
+ url: `/projects/${queryArg.pid}`,
+ method: 'DELETE',
+ }),
+ }),
getProjectsByPidCampaigns: build.query<
GetProjectsByPidCampaignsApiResponse,
GetProjectsByPidCampaignsApiArg
@@ -572,16 +581,6 @@ const injectedRtkApi = api.injectEndpoints({
body: queryArg.body,
}),
}),
- postVideosByVidSentiment: build.mutation<
- PostVideosByVidSentimentApiResponse,
- PostVideosByVidSentimentApiArg
- >({
- query: (queryArg) => ({
- url: `/videos/${queryArg.vid}/sentiment`,
- method: 'POST',
- body: queryArg.body,
- }),
- }),
getWorkspaces: build.query({
query: (queryArg) => ({
url: `/workspaces`,
@@ -882,6 +881,11 @@ export type GetCampaignsByCidBugsApiResponse = /** status 200 OK */ {
}[];
siblings: number;
comments: number;
+ additional_fields?: {
+ slug: string;
+ value: string;
+ name: string;
+ }[];
})[];
start?: number;
limit?: number;
@@ -1482,6 +1486,11 @@ export type PatchProjectsByPidApiArg = {
description: string;
};
};
+export type DeleteProjectsByPidApiResponse = /** status 200 OK */ void;
+export type DeleteProjectsByPidApiArg = {
+ /** Project id */
+ pid: string;
+};
export type GetProjectsByPidCampaignsApiResponse = /** status 200 OK */ {
items?: CampaignWithOutput[];
start?: number;
@@ -1640,11 +1649,6 @@ export type PostVideosByVidTranslationApiArg = {
language: string;
};
};
-export type PostVideosByVidSentimentApiResponse = /** status 200 OK */ object;
-export type PostVideosByVidSentimentApiArg = {
- vid: string;
- body: object;
-};
export type GetWorkspacesApiResponse = /** status 200 OK */ {
items?: Workspace[];
start?: number;
@@ -2247,7 +2251,10 @@ export type Report = {
creation_date?: string;
update_date?: string;
};
-export type BannerType = 'banner_testing_automation' | 'banner_user_experience';
+export type BannerType =
+ | 'banner_testing_automation'
+ | 'banner_user_experience'
+ | 'banner_cyber_security';
export type Tenant = {
/** tryber wp_user_id */
id: number;
@@ -2505,12 +2512,19 @@ export type OutputModuleTaskExplorativeBug = {
description?: string;
url?: string;
};
+export type OutputModuleTaskAccessibility = {
+ kind: 'accessibility';
+ title: string;
+ description?: string;
+ url?: string;
+};
export type SubcomponentTask =
| SubcomponentTaskVideo
| SubcomponentTaskBug
| SubcomponentTaskSurvey
| OutputModuleTaskModerateVideo
- | OutputModuleTaskExplorativeBug;
+ | OutputModuleTaskExplorativeBug
+ | OutputModuleTaskAccessibility;
export type ModuleTask = {
type: 'tasks';
variant: string;
@@ -2773,6 +2787,7 @@ export const {
usePostProjectsMutation,
useGetProjectsByPidQuery,
usePatchProjectsByPidMutation,
+ useDeleteProjectsByPidMutation,
useGetProjectsByPidCampaignsQuery,
useGetProjectsByPidUsersQuery,
usePostProjectsByPidUsersMutation,
@@ -2788,7 +2803,6 @@ export const {
useDeleteVideosByVidObservationsAndOidMutation,
useGetVideosByVidTranslationQuery,
usePostVideosByVidTranslationMutation,
- usePostVideosByVidSentimentMutation,
useGetWorkspacesQuery,
usePostWorkspacesMutation,
useGetWorkspacesByWidQuery,
diff --git a/src/features/navigation/Navigation/NavigationHeader.tsx b/src/features/navigation/Navigation/NavigationHeader.tsx
new file mode 100644
index 000000000..0a72e446b
--- /dev/null
+++ b/src/features/navigation/Navigation/NavigationHeader.tsx
@@ -0,0 +1,14 @@
+import { Header } from 'src/common/components/navigation/header/header';
+import { useGetUsersMePreferencesQuery } from 'src/features/api';
+
+export const NavigationHeader = ({ isMinimal }: { isMinimal?: boolean }) => {
+ const { isFetching: isFetchingPrefs } = useGetUsersMePreferencesQuery();
+
+ return (
+
+ );
+};
diff --git a/src/features/navigation/Navigation.tsx b/src/features/navigation/Navigation/NavigationProfileModal.tsx
similarity index 62%
rename from src/features/navigation/Navigation.tsx
rename to src/features/navigation/Navigation/NavigationProfileModal.tsx
index f695b3f08..8f75a58f9 100644
--- a/src/features/navigation/Navigation.tsx
+++ b/src/features/navigation/Navigation/NavigationProfileModal.tsx
@@ -1,76 +1,45 @@
import {
- Content,
Notification,
ProfileModal,
- Skeleton,
useToast,
} from '@appquality/unguess-design-system';
-import { ComponentProps, useEffect } from 'react';
import { useTranslation } from 'react-i18next';
-import { useParams } from 'react-router-dom';
import { useAppDispatch, useAppSelector } from 'src/app/hooks';
-import { AppSidebar } from 'src/common/components/navigation/sidebar';
import { isDev } from 'src/common/isDevEnvironment';
import { prepareGravatar } from 'src/common/utils';
import WPAPI from 'src/common/wpapi';
-import {
- setProfileModalOpen,
- setSidebarOpen,
- toggleSidebar,
-} from 'src/features/navigation/navigationSlice';
-import { NoActiveWorkSpaceState } from 'src/features/templates/NoActiveWorkspaceState';
-import { useActiveWorkspace } from 'src/hooks/useActiveWorkspace';
-import i18n from 'src/i18n';
-import { styled } from 'styled-components';
-import { Header } from '../../common/components/navigation/header/header';
-import { usePathWithoutLocale } from './usePathWithoutLocale';
import {
useGetUsersMePreferencesQuery,
usePutUsersMePreferencesBySlugMutation,
-} from '../api';
-
-const StyledContent = styled(Content)<
- ComponentProps & {
- isMinimal?: boolean;
- children?: React.ReactNode;
- }
->`
- height: ${({ isMinimal, theme }) =>
- isMinimal
- ? '100%'
- : `calc(100% - ${theme.components.chrome.header.height})`};
-`;
+} from 'src/features/api';
+import { useActiveWorkspace } from 'src/hooks/useActiveWorkspace';
+import { setProfileModalOpen } from '../navigationSlice';
+import { usePathWithoutLocale } from '../usePathWithoutLocale';
-export const Navigation = ({
- children,
- route,
- isMinimal = false,
-}: {
- children: React.ReactNode;
- route: string;
- isMinimal?: boolean;
-}) => {
- const { t } = useTranslation();
- const dispatch = useAppDispatch();
- const pathWithoutLocale = usePathWithoutLocale();
+export const NavigationProfileModal = () => {
+ const isProfileModalOpen = useAppSelector(
+ (state) => state.navigation.isProfileModalOpen
+ );
const { userData: user } = useAppSelector((state) => state.user);
- const { isProfileModalOpen } = useAppSelector((state) => state.navigation);
const { activeWorkspace } = useActiveWorkspace();
+
const { addToast } = useToast();
+ const { t, i18n } = useTranslation();
+ const dispatch = useAppDispatch();
- const {
- data: preferences,
- isLoading: isLoadingPrefs,
- isFetching: isFetchingPrefs,
- isError,
- } = useGetUsersMePreferencesQuery();
+ const { data: preferences } = useGetUsersMePreferencesQuery();
const notificationsPreference = preferences?.items?.find(
(preference) => preference?.name === 'notifications_enabled'
);
+ const pathWithoutLocale = usePathWithoutLocale();
const [updatePreference] = usePutUsersMePreferencesBySlugMutation();
+ const onProfileModalClose = () => {
+ dispatch(setProfileModalOpen(false));
+ };
+
const onSetSettings = async (value: string) => {
await updatePreference({
slug: `${notificationsPreference?.name}`,
@@ -93,39 +62,6 @@ export const Navigation = ({
});
};
- useEffect(() => {
- switch (route) {
- case 'service':
- case 'campaigns':
- case 'bugs':
- case 'bug':
- case 'video':
- case 'videos':
- case 'insights':
- dispatch(setSidebarOpen(false));
- break;
- case 'template':
- dispatch(setSidebarOpen(false));
- break;
- default:
- dispatch(setSidebarOpen(true));
- break;
- }
- }, [route]);
-
- // Set current params
- const params = useParams();
-
- let parameter = '';
-
- if (params) {
- Object.keys(params).forEach((key) => {
- if (key !== 'language') {
- parameter = params[`${key}`] ?? '';
- }
- });
- }
-
const profileModal = {
user: {
name: user.name,
@@ -219,43 +155,7 @@ export const Navigation = ({
disableMenuLanguageSettings: true,
};
- const toggleSidebarState = () => {
- dispatch(toggleSidebar());
- };
-
- const onProfileModalClose = () => {
- dispatch(setProfileModalOpen(false));
- };
+ if (!isProfileModalOpen) return null;
- if (isLoadingPrefs) {
- return ;
- }
- if (isError || !preferences) {
- return null;
- }
- if (!activeWorkspace) return ;
- return (
- <>
-
- {isProfileModalOpen && (
-
- )}
-
-
- {children}
-
- >
- );
+ return ;
};
diff --git a/src/features/navigation/Navigation/index.tsx b/src/features/navigation/Navigation/index.tsx
new file mode 100644
index 000000000..44db1ea32
--- /dev/null
+++ b/src/features/navigation/Navigation/index.tsx
@@ -0,0 +1,95 @@
+import { Content } from '@appquality/unguess-design-system';
+import { ComponentProps, useEffect, useMemo } from 'react';
+import { useParams } from 'react-router-dom';
+import { useAppDispatch } from 'src/app/hooks';
+import { AppSidebar } from 'src/common/components/navigation/sidebar';
+import {
+ setSidebarOpen,
+ toggleSidebar,
+} from 'src/features/navigation/navigationSlice';
+import { NoActiveWorkSpaceState } from 'src/features/templates/NoActiveWorkspaceState';
+import { useActiveWorkspace } from 'src/hooks/useActiveWorkspace';
+import { styled } from 'styled-components';
+import { NavigationHeader } from './NavigationHeader';
+import { NavigationProfileModal } from './NavigationProfileModal';
+
+const StyledContent = styled(Content)<
+ ComponentProps & {
+ $isMinimal?: boolean;
+ children?: React.ReactNode;
+ }
+>`
+ height: ${({ $isMinimal, theme }) =>
+ $isMinimal
+ ? '100%'
+ : `calc(100% - ${theme.components.chrome.header.height})`};
+`;
+
+export const Navigation = ({
+ children,
+ route,
+ isMinimal = false,
+}: {
+ children: React.ReactNode;
+ route: string;
+ isMinimal?: boolean;
+}) => {
+ const dispatch = useAppDispatch();
+ const { activeWorkspace, isLoading } = useActiveWorkspace();
+
+ useEffect(() => {
+ switch (route) {
+ case 'service':
+ case 'campaigns':
+ case 'bugs':
+ case 'bug':
+ case 'video':
+ case 'videos':
+ case 'insights':
+ dispatch(setSidebarOpen(false));
+ break;
+ case 'template':
+ dispatch(setSidebarOpen(false));
+ break;
+ default:
+ dispatch(setSidebarOpen(true));
+ break;
+ }
+ }, [route]);
+
+ // Set current params
+ const params = useParams();
+
+ const parameter = useMemo(() => {
+ if (!params) return '';
+ return Object.keys(params)
+ .filter((key) => key !== 'language')
+ .map((key) => params[`${key}`] ?? '')
+ .join('');
+ }, [params]);
+
+ const toggleSidebarState = () => {
+ dispatch(toggleSidebar());
+ };
+
+ if (!activeWorkspace && !isLoading) return ;
+
+ return (
+ <>
+
+
+
+
+ {children}
+
+ >
+ );
+};
diff --git a/src/hooks/useActiveWorkspace.ts b/src/hooks/useActiveWorkspace.ts
index 27e0b07f2..e9db17977 100644
--- a/src/hooks/useActiveWorkspace.ts
+++ b/src/hooks/useActiveWorkspace.ts
@@ -1,65 +1,41 @@
-import { useEffect, useState } from 'react';
+import { useMemo } from 'react';
import { useAppSelector } from 'src/app/hooks';
-import { Workspace, useGetWorkspacesQuery } from 'src/features/api';
+import { useGetWorkspacesQuery } from 'src/features/api';
import {
getWorkspaceFromLS,
saveWorkspaceToLs,
} from 'src/features/navigation/cachedStorage';
export const useActiveWorkspace = () => {
- const cachedWorkspace = getWorkspaceFromLS();
const { data: workspaces, isLoading } = useGetWorkspacesQuery({
orderBy: 'company',
});
- const [result, setResult] = useState(() => {
- if (
- cachedWorkspace &&
- workspaces &&
- workspaces.items &&
- workspaces.items.map((w) => w.id).includes(cachedWorkspace.id)
- ) {
- return cachedWorkspace;
- }
- return undefined;
- });
- const activeWorkspace = useAppSelector(
+
+ const activeWorkspaceFromRedux = useAppSelector(
(state) => state.navigation.activeWorkspace
);
- useEffect(() => {
- if (activeWorkspace) {
- setResult(activeWorkspace);
- return;
+ const workspace = useMemo(() => {
+ if (activeWorkspaceFromRedux) {
+ return activeWorkspaceFromRedux;
}
- if (result) return;
+ if (!isLoading && workspaces?.items?.length) {
+ const cached = getWorkspaceFromLS();
- if (
- isLoading ||
- !workspaces ||
- !workspaces?.items ||
- workspaces.items.length === 0
- )
- return;
+ const found = cached && workspaces.items.find((w) => w.id === cached.id);
+ if (found) {
+ return found;
+ }
- if (
- cachedWorkspace &&
- workspaces.items.map((w) => w.id).includes(cachedWorkspace.id)
- ) {
- setResult(cachedWorkspace);
- return;
+ // default fallback
+ const first = workspaces.items[0];
+ saveWorkspaceToLs(first);
+ return first;
}
- if (
- !isLoading &&
- workspaces &&
- workspaces.items &&
- workspaces.items.length > 0
- ) {
- saveWorkspaceToLs(workspaces.items[0]);
- setResult(workspaces.items[0]);
- }
- }, [activeWorkspace, workspaces, isLoading]);
+ return undefined;
+ }, [activeWorkspaceFromRedux, workspaces, isLoading]);
- return { activeWorkspace: result };
+ return { activeWorkspace: workspace, isLoading };
};
diff --git a/src/hooks/useActiveWorkspaceProjects.ts b/src/hooks/useActiveWorkspaceProjects.ts
index 2b9d5a870..661fb83a4 100644
--- a/src/hooks/useActiveWorkspaceProjects.ts
+++ b/src/hooks/useActiveWorkspaceProjects.ts
@@ -4,6 +4,11 @@ import { useActiveWorkspace } from './useActiveWorkspace';
const useActiveWorkspaceProjects = () => {
const { activeWorkspace } = useActiveWorkspace();
+ const { data, isLoading, isFetching, isError } =
+ useGetWorkspacesByWidProjectsQuery({
+ wid: activeWorkspace?.id.toString() || '',
+ });
+ // If there is no active workspace, we return an empty object
if (!activeWorkspace)
return {
data: undefined,
@@ -12,11 +17,6 @@ const useActiveWorkspaceProjects = () => {
isError: false,
};
- const { data, isLoading, isFetching, isError } =
- useGetWorkspacesByWidProjectsQuery({
- wid: activeWorkspace?.id.toString() || '',
- });
-
return {
data,
isLoading,
diff --git a/src/hooks/useFeatureFlag.ts b/src/hooks/useFeatureFlag.ts
index b26bd73b2..0cd2cc39f 100644
--- a/src/hooks/useFeatureFlag.ts
+++ b/src/hooks/useFeatureFlag.ts
@@ -1,14 +1,21 @@
+import { shallowEqual } from 'react-redux';
import { useAppSelector } from 'src/app/hooks';
export const useFeatureFlag = () => {
- const { userData: user } = useAppSelector((state) => state.user);
+ const { role, features } = useAppSelector(
+ (state) => ({
+ role: state.user.userData.role,
+ features: state.user.userData.features,
+ }),
+ shallowEqual
+ );
const hasFeatureFlag = (slug?: string) => {
- if (user && user.role === 'administrator') {
+ if (role === 'administrator') {
return true;
}
- if (user && user.features) {
- return user.features.some((feature) => feature.slug === slug);
+ if (features) {
+ return features.some((feature) => feature.slug === slug);
}
return false;
};
diff --git a/src/hooks/useGTMevent.ts b/src/hooks/useGTMevent.ts
index 3428ea434..1115515bf 100644
--- a/src/hooks/useGTMevent.ts
+++ b/src/hooks/useGTMevent.ts
@@ -1,5 +1,6 @@
import { useCallback } from 'react';
import TagManager from 'react-gtm-module';
+import { shallowEqual } from 'react-redux';
import { useAppSelector } from 'src/app/hooks';
import { useActiveWorkspace } from './useActiveWorkspace';
@@ -12,7 +13,16 @@ export interface GTMEventData {
}
export const useSendGTMevent = () => {
- const { userData: user } = useAppSelector((state) => state.user);
+ const user = useAppSelector(
+ (state) => ({
+ role: state.user.userData.role,
+ tryber_wp_user_id: state.user.userData.tryber_wp_user_id,
+ id: state.user.userData.id,
+ name: state.user.userData.name,
+ email: state.user.userData.email,
+ }),
+ shallowEqual
+ );
const { activeWorkspace } = useActiveWorkspace();
const callback = useCallback(
diff --git a/src/locales/en/translation.json b/src/locales/en/translation.json
index e9c5b7428..8c57b5aab 100644
--- a/src/locales/en/translation.json
+++ b/src/locales/en/translation.json
@@ -40,19 +40,12 @@
"__ASIDE_NAVIGATION_MODULE_TARGET_SUBTITLE": "Participant number",
"__ASIDE_NAVIGATION_MODULE_TASKS_SUBTITLE": " ",
"__ASIDE_NAVIGATION_MODULE_TOUCHPOINTS_SUBTITLE": "Interaction points",
- "__BANNER_CROSS_FUNCTIONAL_CTA_AUTOMATION": "Get in touch",
- "__BANNER_CROSS_FUNCTIONAL_CTA_EXPERIENCE": "Get in touch",
- "__BANNER_CROSS_FUNCTIONAL_CTA_LOADING": "sending...",
- "__BANNER_CROSS_FUNCTIONAL_MESSAGE_AUTOMATION": "Try out our testing automation services",
- "__BANNER_CROSS_FUNCTIONAL_MESSAGE_AUTOMATION_ANCHOR": "Discover more",
- "__BANNER_CROSS_FUNCTIONAL_MESSAGE_EXPERIENCE": "Try out User Testing",
+ "__BANNER_CROSS_CTA": "Try out",
+ "__BANNER_CROSS_FUNCTIONAL_MESSAGE_CYBER": "Identify security flaws before hackers do",
+ "__BANNER_CROSS_FUNCTIONAL_MESSAGE_EXPERIENCE": "Experience our starter usability package",
"__BANNER_CROSS_FUNCTIONAL_MESSAGE_EXPERIENCE_ANCHOR": "Discover more",
- "__BANNER_CROSS_FUNCTIONAL_TITLE_AUTOMATION": "Optimize your testing efforts:",
- "__BANNER_CROSS_FUNCTIONAL_TITLE_EXPERIENCE": "Fix usability issues ahead of time:",
- "__BANNER_CROSS_FUNCTIONAL_TOAST_ERROR": "Something went wrong, please try again later.",
- "__BANNER_CROSS_FUNCTIONAL_TOAST_SUCCESS": "Email sent, our team will contact you soon",
- "__BANNER_CROSS_TOAST_ERROR": "Something went wrong, please try again later.",
- "__BANNER_CROSS_TOAST_SUCCESS": "Email sent, our team will contact you soon",
+ "__BANNER_CROSS_FUNCTIONAL_TITLE_CYBER": "Make your product bulletproof:",
+ "__BANNER_CROSS_FUNCTIONAL_TITLE_EXPERIENCE": "Validate your UX:",
"__BREADCRUMB_ITEM_SERVICES": "Services",
"__BROWSER_ERROR_REQUIRED": "No browser selected: Choose at least one browser to continue",
"__BUG_COMMENTS_CHAT_BOLD": "Bold",
@@ -345,6 +338,8 @@
"__CAMPAIGN_EXP_WIDGET_SENTIMENT_LIST_SENTIMENT_LABEL": "Sentiment",
"__CAMPAIGN_EXP_WIDGET_SENTIMENT_LIST_USECASE_LABEL_one": "Use Case (tot. {{count}})",
"__CAMPAIGN_EXP_WIDGET_SENTIMENT_LIST_USECASE_LABEL_other": "Use Cases (tot. {{count}})",
+ "__CAMPAIGN_PAGE_ADDITIONAL_SECTION_SUBTITLE": "Advanced metrics for more precise and impactful analysis",
+ "__CAMPAIGN_PAGE_ADDITIONAL_SECTION_TITLE": "Focus on Details",
"__CAMPAIGN_PAGE_ARCHIVE_CAMPAIGN_MODAL_BUTTON_CANCEL": "Cancel",
"__CAMPAIGN_PAGE_ARCHIVE_CAMPAIGN_MODAL_BUTTON_CONFIRM": "Confirm",
"__CAMPAIGN_PAGE_ARCHIVE_CAMPAIGN_MODAL_DESCRIPTION_1": "If you archive the activity, it will no longer appear in the active project but will remain accessible in the archive.",
@@ -419,6 +414,7 @@
"__CAMPAIGN_PAGE_NAVIGATION_BUG_EXTERNAL_LINK_LABEL": "Go to bug list",
"__CAMPAIGN_PAGE_NAVIGATION_BUG_GROUP_DETAILS_LABEL": "INSIGHTS",
"__CAMPAIGN_PAGE_NAVIGATION_BUG_GROUP_OTHER_LABEL": "DOWNLOAD",
+ "__CAMPAIGN_PAGE_NAVIGATION_BUG_ITEM_ADDITIONALS_LABEL": "Other details",
"__CAMPAIGN_PAGE_NAVIGATION_BUG_ITEM_DETAILS_DEVICES_LABEL": "Devices and types",
"__CAMPAIGN_PAGE_NAVIGATION_BUG_ITEM_DETAILS_UNIQUE_BUGS_LABEL": "Unique bugs distribution",
"__CAMPAIGN_PAGE_NAVIGATION_BUG_ITEM_OTHER_REPORTS_LABEL": "Reports & attachments",
@@ -446,11 +442,11 @@
"__CAMPAIGN_PAGE_REPORTS_GENERATE_REPORT_CARD_BUTTON_LABEL": "Download",
"__CAMPAIGN_PAGE_REPORTS_GENERATE_REPORT_CARD_META": "Bugs Report",
"__CAMPAIGN_PAGE_REPORTS_TITLE": "Reports & attachments",
- "__CAMPAIGN_PAGE_SUGGESTIONS_AUTOMATION_CONTENT": "Optimize testing efforts",
- "__CAMPAIGN_PAGE_SUGGESTIONS_AUTOMATION_HEADER": "Explore automation services",
- "__CAMPAIGN_PAGE_SUGGESTIONS_AUTOMATION_TAG": "New In!",
- "__CAMPAIGN_PAGE_SUGGESTIONS_CTA": "Get in touch",
+ "__CAMPAIGN_PAGE_SUGGESTIONS_CTA": "Try Out",
"__CAMPAIGN_PAGE_SUGGESTIONS_CTA_LOADING": "sending...",
+ "__CAMPAIGN_PAGE_SUGGESTIONS_CYBER_CONTENT": "Strengthen your digital defense",
+ "__CAMPAIGN_PAGE_SUGGESTIONS_CYBER_HEADER": "Explore security services",
+ "__CAMPAIGN_PAGE_SUGGESTIONS_CYBER_TAG": "New in!",
"__CAMPAIGN_PAGE_SUGGESTIONS_EXPERIENCE_CONTENT": "Address Usability, early on",
"__CAMPAIGN_PAGE_SUGGESTIONS_EXPERIENCE_HEADER": "Try out Usability Test",
"__CAMPAIGN_PAGE_SUGGESTIONS_EXPERIENCE_TAG": "Suggested",
@@ -459,6 +455,8 @@
"__CAMPAIGN_PAGE_UNIQUE_BUGS_TITLE": "Unique bugs",
"__CAMPAIGN_PAGE_UPDATE_CAMPAIGN_NAME_ERROR": "Error",
"__CAMPAIGN_PAGE_UX_QUESTION_ACCORDION_TITLE": "The questions that shaped our research",
+ "__CAMPAIGN_PAGE_WIDGET_BUGS_BY_ADDITIONAL_CHART_HEADER": "Tot. bugs",
+ "__CAMPAIGN_PAGE_WIDGET_BUGS_BY_ADDITIONAL_TOOLTIP_UNIQUE_BUGS_LABEL": "Unique bugs:",
"__CAMPAIGN_PAGE_WIDGET_BUGS_BY_DEVICE_CHART_HEADER": "Tot. bugs",
"__CAMPAIGN_PAGE_WIDGET_BUGS_BY_DEVICE_CHART_TOOLTIP_DRILLDOWN": "👉 Drill down to:",
"__CAMPAIGN_PAGE_WIDGET_BUGS_BY_DEVICE_CHART_TOOLTIP_VALUE": "Bugs: {{value}}",
@@ -491,6 +489,10 @@
"__CAMPAIGN_PAGE_WIDGET_UNIQUE_BUGS_COUNT_LABEL_one": "{{count}} unique bug",
"__CAMPAIGN_PAGE_WIDGET_UNIQUE_BUGS_COUNT_LABEL_many": "{{count}} unique bugs",
"__CAMPAIGN_PAGE_WIDGET_UNIQUE_BUGS_COUNT_LABEL_other": "{{count}} unique bugs",
+ "__CAMPAIGN_PAGE_WIDGET_UNIQUE_BUGS_ONLY_COUNT_LABEL_one": "total bug",
+ "__CAMPAIGN_PAGE_WIDGET_UNIQUE_BUGS_ONLY_COUNT_LABEL_other": "total bugs",
+ "__CAMPAIGN_PAGE_WIDGET_UNIQUE_BUGS_ONLY_HEADER": "Total bugs",
+ "__CAMPAIGN_PAGE_WIDGET_UNIQUE_BUGS_ONLY_TOOLTIP": "Monitor the total number of unique bugs identified during the testers' activity, that is, individual issues that were reported as separate problems during the test sessions.",
"__CAMPAIGN_PAGE_WIDGET_UNIQUE_BUGS_REPORTED_BY": "Reported by testers",
"__CAMPAIGN_PAGE_WIDGET_UNIQUE_BUGS_TOOLTIP": "Monitor unique bugs reported only once by a single tester or a series of bugs reported by different testers, which we organize into duplicate bug groups",
"__CAMPAIGN_PAGE_WIDGET_UNIQUE_BUGS_TOTAL_LABEL": "out of {{ total }} total",
@@ -1116,7 +1118,12 @@
"__PLAN_PAGE_MODULE_TARGET_PLACEHOLDER": "Example: 8",
"__PLAN_PAGE_MODULE_TARGET_REMOVE_BUTTON": "Delete",
"__PLAN_PAGE_MODULE_TARGET_TITLE": "Target Size",
+ "__PLAN_PAGE_MODULE_TASKS_ACCESSIBILITY_TASK_ACCESSIBILITY_DESCRIPTION_DEFAULT": "Partecipants will test your product without defined tasks.",
+ "__PLAN_PAGE_MODULE_TASKS_ACCESSIBILITY_TASK_ACCESSIBILITY_TITLE_DEFAULT": "Accessibility",
"__PLAN_PAGE_MODULE_TASKS_ADD_TASK_BUTTON": "Add task",
+ "__PLAN_PAGE_MODULE_TASKS_ADD_TASK_MODAL_ACCESSIBILITY_TAB": "Accessibility",
+ "__PLAN_PAGE_MODULE_TASKS_ADD_TASK_MODAL_ACCESSIBILITY_TASK_ACCESSIBILITY_BUTTON": "Accessibility Task",
+ "__PLAN_PAGE_MODULE_TASKS_ADD_TASK_MODAL_ACCESSIBILITY_TASKS_LABEL": "Accessibility",
"__PLAN_PAGE_MODULE_TASKS_ADD_TASK_MODAL_DEFAULT_TAB": "All",
"__PLAN_PAGE_MODULE_TASKS_ADD_TASK_MODAL_EXPERIENTIAL_TAB": "Experience",
"__PLAN_PAGE_MODULE_TASKS_ADD_TASK_MODAL_EXPERIENTIAL_TASK_THINKING_ALOUD_BUTTON": "Thinking aloud task",
@@ -1285,6 +1292,11 @@
"__PROJECT_FORM_NAME_MAX": "This name is a bit long. We advise you to stay within 64 characters including spaces.",
"__PROJECT_FORM_NAME_REQUIRED": "Choose a name before creating the project",
"__PROJECT_PAGE_ARCHIVE_DESCRIPTION": "The Archive helps you keep your projects organized. You can restore a activity at any time or leave it here for future reference",
+ "__PROJECT_PAGE_DELETE_PROJECT_MODAL_BODY": "Once the project has been deleted, it can no longer be recovered
All current shares will be removed and will not be visible anymore.",
+ "__PROJECT_PAGE_DELETE_PROJECT_MODAL_BUTTON_CANCEL": "Cancel",
+ "__PROJECT_PAGE_DELETE_PROJECT_MODAL_BUTTON_CONFIRM": "Delete",
+ "__PROJECT_PAGE_DELETE_PROJECT_MODAL_ERROR": "The project could not be deleted. Please try again",
+ "__PROJECT_PAGE_DELETE_PROJECT_MODAL_TITLE": "Are you sure you want to delete this project?",
"__PROJECT_PAGE_EMPTY_STATE_NO_FEATURE": "Manage your own projects and test campaigns!",
"__PROJECT_PAGE_UPDATE_PROJECT_DESCRIPTION_PLACEHOLDER": "Write a description",
"__PROJECT_PAGE_UPDATE_PROJECT_NAME_ERROR": "The project name is already taken. Choose another name.",
@@ -1445,7 +1457,7 @@
"_CAMPAIGN_WIDGET_UX_USER_ANALYSIS_HEADER": "Analyzed User Contributions",
"_PLAN_PAGE_MODULE_LANGUAGE_DESCRIPTION": "You’ll receive feedback in the language you’re selecting",
"_PLAN_PAGE_MODULE_LANGUAGE_SUBTITLE": "Select participants' preferred language",
- "_PROJECT_PAGE_PLANS_GROUP_SEE_ALL": "View all",
+ "_PROJECT_PAGE_PLANS_GROUP_SEE_ALL": "View more",
"_PROJECT_PAGE_PLANS_GROUP_SEE_LESS": "View less",
"_PROJECT_PAGE_PLANS_GROUP_SUBTITLE": "Create and configure new activities or review those awaiting approval",
"_PROJECT_PAGE_PLANS_GROUP_TITLE": "Setup activities",
diff --git a/src/locales/it/translation.json b/src/locales/it/translation.json
index 8c1c448bd..5da5fc760 100644
--- a/src/locales/it/translation.json
+++ b/src/locales/it/translation.json
@@ -40,19 +40,12 @@
"__ASIDE_NAVIGATION_MODULE_TARGET_SUBTITLE": "",
"__ASIDE_NAVIGATION_MODULE_TASKS_SUBTITLE": "",
"__ASIDE_NAVIGATION_MODULE_TOUCHPOINTS_SUBTITLE": "",
- "__BANNER_CROSS_FUNCTIONAL_CTA_AUTOMATION": "Contattaci",
- "__BANNER_CROSS_FUNCTIONAL_CTA_EXPERIENCE": "Contattaci",
- "__BANNER_CROSS_FUNCTIONAL_CTA_LOADING": "invio in corso...",
- "__BANNER_CROSS_FUNCTIONAL_MESSAGE_AUTOMATION": "Prova i nostri servizi di test automation",
- "__BANNER_CROSS_FUNCTIONAL_MESSAGE_AUTOMATION_ANCHOR": "Scopri di più",
+ "__BANNER_CROSS_CTA": "",
+ "__BANNER_CROSS_FUNCTIONAL_MESSAGE_CYBER": "",
"__BANNER_CROSS_FUNCTIONAL_MESSAGE_EXPERIENCE": "Prova il Test di Usabilità ",
"__BANNER_CROSS_FUNCTIONAL_MESSAGE_EXPERIENCE_ANCHOR": "Scopri di più",
- "__BANNER_CROSS_FUNCTIONAL_TITLE_AUTOMATION": "Ottimizza i tuoi sforzi di test:",
+ "__BANNER_CROSS_FUNCTIONAL_TITLE_CYBER": "",
"__BANNER_CROSS_FUNCTIONAL_TITLE_EXPERIENCE": "Risolvi i problemi di usabilità prima del tempo:",
- "__BANNER_CROSS_FUNCTIONAL_TOAST_ERROR": "Qualcosa è andato storto, riprova più tardi.",
- "__BANNER_CROSS_FUNCTIONAL_TOAST_SUCCESS": "Email inviata, il nostro team ti contatterà presto",
- "__BANNER_CROSS_TOAST_ERROR": "Qualcosa è andato storto, riprova più tardi.",
- "__BANNER_CROSS_TOAST_SUCCESS": "Email inviata, il nostro team ti contatterà presto",
"__BREADCRUMB_ITEM_SERVICES": "Servizi",
"__BROWSER_ERROR_REQUIRED": "",
"__BUG_COMMENTS_CHAT_BOLD": "Grassetto",
@@ -364,6 +357,8 @@
"__CAMPAIGN_EXP_WIDGET_SENTIMENT_LIST_USECASE_LABEL_one": "Use Case (tot. {{count}})",
"__CAMPAIGN_EXP_WIDGET_SENTIMENT_LIST_USECASE_LABEL_many": "",
"__CAMPAIGN_EXP_WIDGET_SENTIMENT_LIST_USECASE_LABEL_other": "Use Case (tot. {{count}})",
+ "__CAMPAIGN_PAGE_ADDITIONAL_SECTION_SUBTITLE": "",
+ "__CAMPAIGN_PAGE_ADDITIONAL_SECTION_TITLE": "",
"__CAMPAIGN_PAGE_ARCHIVE_CAMPAIGN_MODAL_BUTTON_CANCEL": "",
"__CAMPAIGN_PAGE_ARCHIVE_CAMPAIGN_MODAL_BUTTON_CONFIRM": "",
"__CAMPAIGN_PAGE_ARCHIVE_CAMPAIGN_MODAL_DESCRIPTION_1": "",
@@ -441,6 +436,7 @@
"__CAMPAIGN_PAGE_NAVIGATION_BUG_EXTERNAL_LINK_LABEL": "Vai alla lista bug",
"__CAMPAIGN_PAGE_NAVIGATION_BUG_GROUP_DETAILS_LABEL": "APPROFONDISCI",
"__CAMPAIGN_PAGE_NAVIGATION_BUG_GROUP_OTHER_LABEL": "NEL DETTAGLIO",
+ "__CAMPAIGN_PAGE_NAVIGATION_BUG_ITEM_ADDITIONALS_LABEL": "",
"__CAMPAIGN_PAGE_NAVIGATION_BUG_ITEM_DETAILS_DEVICES_LABEL": "Dispositivi e tipologie bug",
"__CAMPAIGN_PAGE_NAVIGATION_BUG_ITEM_DETAILS_UNIQUE_BUGS_LABEL": "Distribuzione bug unici",
"__CAMPAIGN_PAGE_NAVIGATION_BUG_ITEM_OTHER_REPORTS_LABEL": "Report & documenti",
@@ -468,11 +464,11 @@
"__CAMPAIGN_PAGE_REPORTS_GENERATE_REPORT_CARD_BUTTON_LABEL": "Scarica",
"__CAMPAIGN_PAGE_REPORTS_GENERATE_REPORT_CARD_META": "Report Bug",
"__CAMPAIGN_PAGE_REPORTS_TITLE": "Report & documenti",
- "__CAMPAIGN_PAGE_SUGGESTIONS_AUTOMATION_CONTENT": "Ottimizza gli sforzi di test",
- "__CAMPAIGN_PAGE_SUGGESTIONS_AUTOMATION_HEADER": "Esplora i servizi di automazione",
- "__CAMPAIGN_PAGE_SUGGESTIONS_AUTOMATION_TAG": "Novità !",
"__CAMPAIGN_PAGE_SUGGESTIONS_CTA": "Contattaci",
"__CAMPAIGN_PAGE_SUGGESTIONS_CTA_LOADING": "invio in corso...",
+ "__CAMPAIGN_PAGE_SUGGESTIONS_CYBER_CONTENT": "",
+ "__CAMPAIGN_PAGE_SUGGESTIONS_CYBER_HEADER": "",
+ "__CAMPAIGN_PAGE_SUGGESTIONS_CYBER_TAG": "",
"__CAMPAIGN_PAGE_SUGGESTIONS_EXPERIENCE_CONTENT": "Affronta l'usabilità fin dall'inizio",
"__CAMPAIGN_PAGE_SUGGESTIONS_EXPERIENCE_HEADER": "Prova il test di usabilità ",
"__CAMPAIGN_PAGE_SUGGESTIONS_EXPERIENCE_TAG": "Consigliato",
@@ -481,6 +477,8 @@
"__CAMPAIGN_PAGE_UNIQUE_BUGS_TITLE": "Bug unici",
"__CAMPAIGN_PAGE_UPDATE_CAMPAIGN_NAME_ERROR": "Errore",
"__CAMPAIGN_PAGE_UX_QUESTION_ACCORDION_TITLE": "Le domande che hanno guidato la nostra ricerca",
+ "__CAMPAIGN_PAGE_WIDGET_BUGS_BY_ADDITIONAL_CHART_HEADER": "Tot. bugs",
+ "__CAMPAIGN_PAGE_WIDGET_BUGS_BY_ADDITIONAL_TOOLTIP_UNIQUE_BUGS_LABEL": "",
"__CAMPAIGN_PAGE_WIDGET_BUGS_BY_DEVICE_CHART_HEADER": "Tot. bug",
"__CAMPAIGN_PAGE_WIDGET_BUGS_BY_DEVICE_CHART_TOOLTIP_DRILLDOWN": "👉 Visualizza dettaglio di:",
"__CAMPAIGN_PAGE_WIDGET_BUGS_BY_DEVICE_CHART_TOOLTIP_VALUE": "Bug: {{value}}",
@@ -513,6 +511,11 @@
"__CAMPAIGN_PAGE_WIDGET_UNIQUE_BUGS_COUNT_LABEL_one": "{{count}} bug unico",
"__CAMPAIGN_PAGE_WIDGET_UNIQUE_BUGS_COUNT_LABEL_many": "{{count}} bug unici",
"__CAMPAIGN_PAGE_WIDGET_UNIQUE_BUGS_COUNT_LABEL_other": "{{count}} bug unici",
+ "__CAMPAIGN_PAGE_WIDGET_UNIQUE_BUGS_ONLY_COUNT_LABEL_one": "total bugs",
+ "__CAMPAIGN_PAGE_WIDGET_UNIQUE_BUGS_ONLY_COUNT_LABEL_many": "total bugs",
+ "__CAMPAIGN_PAGE_WIDGET_UNIQUE_BUGS_ONLY_COUNT_LABEL_other": "total bugs",
+ "__CAMPAIGN_PAGE_WIDGET_UNIQUE_BUGS_ONLY_HEADER": "",
+ "__CAMPAIGN_PAGE_WIDGET_UNIQUE_BUGS_ONLY_TOOLTIP": "",
"__CAMPAIGN_PAGE_WIDGET_UNIQUE_BUGS_REPORTED_BY": "I tester hanno segnalato",
"__CAMPAIGN_PAGE_WIDGET_UNIQUE_BUGS_TOOLTIP": "Monitora i bug univoci segnalati una sola volta da un solo tester. Oppure monitora una serie di bug segnalati da tester diversi, che noi organizziamo in gruppi di bug duplicati",
"__CAMPAIGN_PAGE_WIDGET_UNIQUE_BUGS_TOTAL_LABEL": "su {{ total }} totali",
@@ -1145,7 +1148,12 @@
"__PLAN_PAGE_MODULE_TARGET_PLACEHOLDER": "",
"__PLAN_PAGE_MODULE_TARGET_REMOVE_BUTTON": "",
"__PLAN_PAGE_MODULE_TARGET_TITLE": "",
+ "__PLAN_PAGE_MODULE_TASKS_ACCESSIBILITY_TASK_ACCESSIBILITY_DESCRIPTION_DEFAULT": "",
+ "__PLAN_PAGE_MODULE_TASKS_ACCESSIBILITY_TASK_ACCESSIBILITY_TITLE_DEFAULT": "",
"__PLAN_PAGE_MODULE_TASKS_ADD_TASK_BUTTON": "",
+ "__PLAN_PAGE_MODULE_TASKS_ADD_TASK_MODAL_ACCESSIBILITY_TAB": "",
+ "__PLAN_PAGE_MODULE_TASKS_ADD_TASK_MODAL_ACCESSIBILITY_TASK_ACCESSIBILITY_BUTTON": "",
+ "__PLAN_PAGE_MODULE_TASKS_ADD_TASK_MODAL_ACCESSIBILITY_TASKS_LABEL": "",
"__PLAN_PAGE_MODULE_TASKS_ADD_TASK_MODAL_DEFAULT_TAB": "",
"__PLAN_PAGE_MODULE_TASKS_ADD_TASK_MODAL_EXPERIENTIAL_TAB": "",
"__PLAN_PAGE_MODULE_TASKS_ADD_TASK_MODAL_EXPERIENTIAL_TASK_THINKING_ALOUD_BUTTON": "",
@@ -1314,6 +1322,12 @@
"__PROJECT_FORM_NAME_MAX": "",
"__PROJECT_FORM_NAME_REQUIRED": "",
"__PROJECT_PAGE_ARCHIVE_DESCRIPTION": "",
+ "__PROJECT_PAGE_DELETE_PROJECT_MODAL_BODY_1": "",
+ "__PROJECT_PAGE_DELETE_PROJECT_MODAL_BODY_2": "",
+ "__PROJECT_PAGE_DELETE_PROJECT_MODAL_BUTTON_CANCEL": "",
+ "__PROJECT_PAGE_DELETE_PROJECT_MODAL_BUTTON_CONFIRM": "",
+ "__PROJECT_PAGE_DELETE_PROJECT_MODAL_ERROR": "",
+ "__PROJECT_PAGE_DELETE_PROJECT_MODAL_TITLE": "",
"__PROJECT_PAGE_EMPTY_STATE_NO_FEATURE": "",
"__PROJECT_PAGE_UPDATE_PROJECT_DESCRIPTION_PLACEHOLDER": "Aggiungi una descrizione",
"__PROJECT_PAGE_UPDATE_PROJECT_NAME_ERROR": "Il nome del progetto è già in uso. Scegline un altro.",
diff --git a/src/pages/Bug/Content.tsx b/src/pages/Bug/Content.tsx
index fd2b021c6..096f2fe39 100644
--- a/src/pages/Bug/Content.tsx
+++ b/src/pages/Bug/Content.tsx
@@ -26,8 +26,8 @@ export const Content = ({ bug, campaignId }: Props) => (
- {bug.media && bug.media.length ? : null}
+ {bug.media && bug.media.length ? : null}
diff --git a/src/pages/Bugs/Content/BugPreview.tsx b/src/pages/Bugs/Content/BugPreview.tsx
index 359c41834..f992feef2 100644
--- a/src/pages/Bugs/Content/BugPreview.tsx
+++ b/src/pages/Bugs/Content/BugPreview.tsx
@@ -1,5 +1,7 @@
import { Skeleton } from '@appquality/unguess-design-system';
import { useEffect, useMemo, useRef } from 'react';
+import { createSearchParams } from 'react-router-dom';
+import { useAppSelector } from 'src/app/hooks';
import { AnchorButtons } from 'src/common/components/BugDetail/AnchorButtons';
import BugAttachments from 'src/common/components/BugDetail/Attachments';
import { BugDuplicates } from 'src/common/components/BugDetail/BugDuplicates';
@@ -18,8 +20,6 @@ import {
getSelectedBugId,
} from 'src/features/bugsPage/bugsPageSlice';
import styled from 'styled-components';
-import { useAppSelector } from 'src/app/hooks';
-import { createSearchParams } from 'react-router-dom';
import { BugCommentsDetail } from './components/BugCommentsDetails';
import BugHeader from './components/BugHeader';
import { BugPreviewContextProvider } from './context/BugPreviewContext';
@@ -206,6 +206,7 @@ export const BugPreview = ({
+
{media && media.length ? : null}
-
{currentBugId && (
)}
diff --git a/src/pages/Bugs/Content/BugsTable/AllBugs.tsx b/src/pages/Bugs/Content/BugsTable/AllBugs.tsx
index 665336f27..3e7a4b21d 100644
--- a/src/pages/Bugs/Content/BugsTable/AllBugs.tsx
+++ b/src/pages/Bugs/Content/BugsTable/AllBugs.tsx
@@ -1,17 +1,16 @@
+import { AccordionNew } from '@appquality/unguess-design-system';
+import { t } from 'i18next';
import { appTheme } from 'src/app/theme';
+import { getSelectedFiltersIds } from 'src/features/bugsPage/bugsPageSlice';
import useWindowSize from 'src/hooks/useWindowSize';
import { styled } from 'styled-components';
-import { useGetCampaignsByCidSuggestionsQuery } from 'src/features/api';
-import { AccordionNew } from '@appquality/unguess-design-system';
-import { getSelectedFiltersIds } from 'src/features/bugsPage/bugsPageSlice';
-import { t } from 'i18next';
-import { InfoRowMeta } from './components/InfoRowMeta';
-import AllBugsTable from './components/SingleGroupTable';
import BugCards from './components/BugCards';
-import { useBugs } from './hooks/useBugs';
-import { LoadingState } from './components/LoadingState';
import { EmptyState } from './components/EmptyState';
+import { InfoRowMeta } from './components/InfoRowMeta';
+import { LoadingState } from './components/LoadingState';
import { Reccomendation } from './components/Reccomendation';
+import AllBugsTable from './components/SingleGroupTable';
+import { useBugs } from './hooks/useBugs';
const Wrapper = styled.div<{
isFetching?: boolean;
@@ -31,9 +30,6 @@ export const AllBugs = ({ campaignId }: { campaignId: number }) => {
const breakpointMd = parseInt(appTheme.breakpoints.md, 10);
const isMdBreakpoint = width < breakpointMd;
const { data, isLoading, isFetching, isError } = useBugs(campaignId);
- const { data: suggestions } = useGetCampaignsByCidSuggestionsQuery({
- cid: campaignId.toString(),
- });
const { allBugs: bugs } = data;
const filterBy = getSelectedFiltersIds();
@@ -49,7 +45,7 @@ export const AllBugs = ({ campaignId }: { campaignId: number }) => {
return (
-
+
diff --git a/src/pages/Bugs/Content/BugsTable/BugsByState.tsx b/src/pages/Bugs/Content/BugsTable/BugsByState.tsx
index 9a57aae40..56060c072 100644
--- a/src/pages/Bugs/Content/BugsTable/BugsByState.tsx
+++ b/src/pages/Bugs/Content/BugsTable/BugsByState.tsx
@@ -3,13 +3,12 @@ import { useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { getCustomStatusInfo } from 'src/common/components/utils/getCustomStatusInfo';
import { styled } from 'styled-components';
-import { useGetCampaignsByCidSuggestionsQuery } from 'src/features/api';
-import { EmptyState } from './components/EmptyState';
import { EmptyGroup } from './components/EmptyGroup';
-import { useBugsByState } from './hooks/useBugsByState';
+import { EmptyState } from './components/EmptyState';
import { LoadingState } from './components/LoadingState';
-import BugStateAccordion from './components/SingleGroupAccordion';
import { Reccomendation } from './components/Reccomendation';
+import BugStateAccordion from './components/SingleGroupAccordion';
+import { useBugsByState } from './hooks/useBugsByState';
const Wrapper = styled.div<{
isFetching?: boolean;
@@ -34,9 +33,6 @@ export const BugsByState = ({
const { t } = useTranslation();
const { data, isError, isFetching, isLoading } = useBugsByState(campaignId);
const { bugsByStates } = data;
- const { data: suggestions } = useGetCampaignsByCidSuggestionsQuery({
- cid: campaignId.toString(),
- });
const emptyBugStates = useMemo(
() => bugsByStates.filter((item) => item.bugs.length === 0),
@@ -73,11 +69,8 @@ export const BugsByState = ({
} ${`(${item.bugs.length})`}`}
item={item}
/>
- {i === 0 && suggestions && (
-
+ {i === 0 && (
+
)}
>
))}
diff --git a/src/pages/Bugs/Content/BugsTable/BugsByUsecase.tsx b/src/pages/Bugs/Content/BugsTable/BugsByUsecase.tsx
index b94b62213..07db66aa9 100644
--- a/src/pages/Bugs/Content/BugsTable/BugsByUsecase.tsx
+++ b/src/pages/Bugs/Content/BugsTable/BugsByUsecase.tsx
@@ -2,14 +2,13 @@ import { AccordionNew, MD } from '@appquality/unguess-design-system';
import { useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { styled } from 'styled-components';
-import { useGetCampaignsByCidSuggestionsQuery } from 'src/features/api';
-import { EmptyState } from './components/EmptyState';
import { CompletionTooltip } from './components/CompletionTooltip';
import { EmptyGroup } from './components/EmptyGroup';
+import { EmptyState } from './components/EmptyState';
import { LoadingState } from './components/LoadingState';
-import { useBugsByUseCase } from './hooks/useBugsByUseCase';
-import BugsByUseCaseAccordion from './components/SingleGroupAccordion';
import { Reccomendation } from './components/Reccomendation';
+import BugsByUseCaseAccordion from './components/SingleGroupAccordion';
+import { useBugsByUseCase } from './hooks/useBugsByUseCase';
const Wrapper = styled.div<{
isFetching?: boolean;
@@ -33,9 +32,6 @@ export const BugsByUsecase = ({
}) => {
const { t } = useTranslation();
const { data, isError, isFetching, isLoading } = useBugsByUseCase(campaignId);
- const { data: suggestions } = useGetCampaignsByCidSuggestionsQuery({
- cid: campaignId.toString(),
- });
const { bugsByUseCases } = data;
const emptyUseCases = useMemo(
@@ -80,11 +76,8 @@ export const BugsByUsecase = ({
)
}
/>
- {i === 0 && suggestions && (
-
+ {i === 0 && (
+
)}
>
))}
diff --git a/src/pages/Bugs/Content/BugsTable/components/Reccomendation.tsx b/src/pages/Bugs/Content/BugsTable/components/Reccomendation.tsx
index bf9b26447..0c9e491fc 100644
--- a/src/pages/Bugs/Content/BugsTable/components/Reccomendation.tsx
+++ b/src/pages/Bugs/Content/BugsTable/components/Reccomendation.tsx
@@ -1,27 +1,46 @@
-import {
- GlobalAlert,
- Anchor,
- useToast,
- Notification,
-} from '@appquality/unguess-design-system';
-import { t } from 'i18next';
+import { GlobalAlert } from '@appquality/unguess-design-system';
import { useEffect } from 'react';
+import { useTranslation } from 'react-i18next';
+import { useNavigate } from 'react-router-dom';
import { appTheme } from 'src/app/theme';
-import { ReactComponent as IconMail } from '@zendeskgarden/svg-icons/src/16/email-stroke.svg';
-import {
- GetCampaignsByCidSuggestionsApiResponse,
- usePostCampaignsByCidSuggestionsMutation,
-} from 'src/features/api';
+import { useGetCampaignsByCidSuggestionsQuery } from 'src/features/api';
import { useSendGTMevent } from 'src/hooks/useGTMevent';
-import { useParams } from 'react-router-dom';
-export const Reccomendation = ({
- suggestion,
-}: GetCampaignsByCidSuggestionsApiResponse) => {
- const [sendMail, { isLoading }] = usePostCampaignsByCidSuggestionsMutation();
- const { addToast } = useToast();
- const { campaignId } = useParams();
+const useBannerData = (banner_type: string) => {
+ const { t } = useTranslation();
+ switch (banner_type) {
+ case 'banner_cyber_security':
+ return {
+ type: 'primary' as const,
+ title: t('__BANNER_CROSS_FUNCTIONAL_TITLE_CYBER'),
+ message: t('__BANNER_CROSS_FUNCTIONAL_MESSAGE_CYBER'),
+ };
+ case 'banner_user_experience':
+ return {
+ type: 'accent' as const,
+ title: t('__BANNER_CROSS_FUNCTIONAL_TITLE_EXPERIENCE'),
+ message: t('__BANNER_CROSS_FUNCTIONAL_MESSAGE_EXPERIENCE'),
+ };
+ default:
+ return {
+ type: 'accent' as const,
+ title: '',
+ message: '',
+ };
+ }
+};
+
+export const Reccomendation = ({ campaignId }: { campaignId: number }) => {
+ const { t } = useTranslation();
+ const { data: suggestions } = useGetCampaignsByCidSuggestionsQuery({
+ cid: campaignId.toString(),
+ });
+ const { type, title, message } = useBannerData(
+ suggestions?.suggestion?.slug || ''
+ );
+ const navigate = useNavigate();
+ const suggestion = suggestions?.suggestion;
const sendGTMEvent = useSendGTMevent();
useEffect(() => {
if (!suggestion?.slug) {
@@ -35,112 +54,17 @@ export const Reccomendation = ({
});
}, [suggestion?.slug]);
- const handleCtaClick = async () => {
- if (!suggestion || isLoading) {
- return;
- }
-
- sendGTMEvent({
- event: 'reccomendation',
- category: 'bugs',
- action: 'click',
- content: suggestion.slug,
- target: 'get_in_touch',
- });
-
- sendMail({ cid: campaignId || '0', body: { slug: suggestion.slug } })
- .unwrap()
- .then(() =>
- addToast(
- ({ close }) => (
-
- ),
- { placement: 'top' }
- )
- )
- .catch((error) =>
- addToast(
- ({ close }) => (
-
- ),
- { placement: 'top' }
- )
- );
- };
-
- const getCtaLabel = (slug: string) => {
- if (isLoading) return t('__BANNER_CROSS_FUNCTIONAL_CTA_LOADING');
- if (slug === 'banner_testing_automation') {
- return (
- <>
- {t('__BANNER_CROSS_FUNCTIONAL_CTA_AUTOMATION')}{' '}
-
- >
- );
- }
- return (
- <>
- {t('__BANNER_CROSS_FUNCTIONAL_CTA_EXPERIENCE')}{' '}
-
- >
- );
- };
-
if (!suggestion) {
return null;
}
return (
- {suggestion.slug === 'banner_testing_automation'
- ? t('__BANNER_CROSS_FUNCTIONAL_MESSAGE_AUTOMATION')
- : t('__BANNER_CROSS_FUNCTIONAL_MESSAGE_EXPERIENCE')}{' '}
- {suggestion.serviceId && (
- {
- sendGTMEvent({
- event: 'reccomendation',
- category: 'bugs',
- action: 'click',
- content: suggestion.slug,
- target: 'view_more',
- });
- }}
- isExternal
- >
- {suggestion.slug === 'banner_testing_automation'
- ? t('__BANNER_CROSS_FUNCTIONAL_MESSAGE_AUTOMATION_ANCHOR')
- : t('__BANNER_CROSS_FUNCTIONAL_MESSAGE_EXPERIENCE_ANCHOR')}
-
- )}
- >
- }
+ type={type}
+ title={title}
+ message={message}
cta={{
- label: getCtaLabel(suggestion.slug),
- onClick: handleCtaClick,
+ label: <>{t('__BANNER_CROSS_CTA')}>,
+ onClick: () => navigate(`/templates/${suggestion.serviceId}`),
}}
style={{ marginBottom: appTheme.space.lg }}
/>
diff --git a/src/pages/Bugs/PageHeader/Tools/index.tsx b/src/pages/Bugs/PageHeader/Tools/index.tsx
index 52e1f3940..33a5b345b 100644
--- a/src/pages/Bugs/PageHeader/Tools/index.tsx
+++ b/src/pages/Bugs/PageHeader/Tools/index.tsx
@@ -12,6 +12,7 @@ import { CampaignStatus } from 'src/types';
import { PageMeta } from 'src/common/components/PageMeta';
import { CampaignSettings } from 'src/common/components/inviteUsers/campaignSettings';
import { useGetCampaignsByCidQuery } from 'src/features/api';
+import { useCanAccessToActiveWorkspace } from 'src/hooks/useCanAccessToActiveWorkspace';
import { UniqueBugsCounter } from './UniqueBugsCounter';
import { useCampaignBugs } from './useCampaignBugs';
@@ -51,6 +52,7 @@ export const Tools = ({
customerTitle: string;
}) => {
const { t } = useTranslation();
+ const hasWorksPacePermission = useCanAccessToActiveWorkspace();
const integrationCenterUrl = getLocalizeIntegrationCenterRoute(campaignId);
const {
isCampaignLoading,
@@ -90,9 +92,9 @@ export const Tools = ({
{status && }
- {campaignData && campaignData.isArchived !== true && (
-
- )}
+ {campaignData &&
+ campaignData.isArchived !== true &&
+ hasWorksPacePermission && }