From 2d9d8255a6cb38b542103b4286a126603e72f956 Mon Sep 17 00:00:00 2001
From: d-beezee <59012086+d-beezee@users.noreply.github.com>
Date: Tue, 12 May 2026 09:42:30 +0200
Subject: [PATCH 1/4] fix: add unique keys to Editor components for bug details
---
src/common/components/BugDetail/Description.tsx | 17 ++++++++++++-----
1 file changed, 12 insertions(+), 5 deletions(-)
diff --git a/src/common/components/BugDetail/Description.tsx b/src/common/components/BugDetail/Description.tsx
index 909f92690..bfd443df1 100644
--- a/src/common/components/BugDetail/Description.tsx
+++ b/src/common/components/BugDetail/Description.tsx
@@ -1,8 +1,7 @@
import { Editor, MD, TextDescription } from '@appquality/unguess-design-system';
+import { useTranslation } from 'react-i18next';
import { Bug } from 'src/features/api';
import styled from 'styled-components';
-import { useTranslation } from 'react-i18next';
-import { WrappedText } from 'src/common/components/WrappedText';
const Container = styled.div`
display: inline-block;
@@ -40,7 +39,7 @@ export default ({
{t('__BUGS_PAGE_BUG_DETAIL_DESCRIPTION_LABEL')}
-
+
{bug.step_by_step}
@@ -48,7 +47,11 @@ export default ({
{t('__BUGS_PAGE_BUG_DETAIL_EXPECTED_RESULT_LABEL')}
-
+
{bug.expected_result}
@@ -56,7 +59,11 @@ export default ({
{t('__BUGS_PAGE_BUG_DETAIL_CURRENT_RESULT_LABEL')}
-
+
{bug.current_result}
From d7a73e0464c86d186e8e0861b467067e339f8572 Mon Sep 17 00:00:00 2001
From: "Luca Cannarozzo (@cannarocks)"
Date: Tue, 12 May 2026 10:19:32 +0200
Subject: [PATCH 2/4] fix: refactor login logic to remove WordPress fallback
and improve error handling
---
src/pages/LoginPage/index.tsx | 77 +++++------------------------------
1 file changed, 10 insertions(+), 67 deletions(-)
diff --git a/src/pages/LoginPage/index.tsx b/src/pages/LoginPage/index.tsx
index 1ac6edcea..274f88c3c 100644
--- a/src/pages/LoginPage/index.tsx
+++ b/src/pages/LoginPage/index.tsx
@@ -8,17 +8,16 @@ import { FormikHelpers } from 'formik';
import { useEffect, useState } from 'react';
import { Trans, useTranslation } from 'react-i18next';
import { useLocation, useNavigate, useSearchParams } from 'react-router-dom';
-import WPAPI from 'src/common/wpapi';
+import { AuthCardWrapper } from 'src/common/components/AuthCardWrapper';
+import { Track } from 'src/common/Track';
import { useGetUsersMeQuery } from 'src/features/api';
import { useAuth } from 'src/features/auth/context';
import { useLocalizeRoute } from 'src/hooks/useLocalizedRoute';
import styled from 'styled-components';
-import { AuthCardWrapper } from 'src/common/components/AuthCardWrapper';
-import { Track } from 'src/common/Track';
import { LoginForm } from './LoginForm';
-import { LoginFormFields } from './type';
-import { AuthHeader } from './parts/AuthHeader';
import { AuthFooter } from './parts/AuthFooter';
+import { AuthHeader } from './parts/AuthHeader';
+import { LoginFormFields } from './type';
const StyledLogo = styled(Logo)`
margin: ${({ theme }) => theme.space.md};
@@ -81,21 +80,6 @@ const LoginPage = () => {
}
}, []);
- const showGenericErrorToast = (title?: string) => {
- addToast(
- ({ close }) => (
-
- ),
- { placement: 'top' }
- );
- };
-
const showInvalidCredentialsToast = () => {
addToast(
({ close }) => (
@@ -151,57 +135,16 @@ const LoginPage = () => {
// Login Cognito riuscito
setCta(`${t('__LOGIN_FORM_CTA_REDIRECT_STATE')}`);
document.location.href = from || '/';
- return;
} catch (cognitoError: any) {
- // Non è possibile gestire l'errore, se utente Cognito sbaglia password verrà fatto fallback al login legacy
+ showInvalidCredentialsToast();
+ setStatus({
+ message: t('__LOGIN_FORM_FAILED_INVALID'),
+ type: 'invalid',
+ });
+ setSubmitting(false);
// eslint-disable-next-line no-console
console.error('Cognito login error:', cognitoError);
}
-
- // STEP 2: Fallback a WordPress legacy login (se utente non esiste su Cognito)
- let nonce;
- try {
- nonce = await WPAPI.getNonce();
- } catch (err: any) {
- if (err?.status !== 200) {
- showInvalidCredentialsToast();
- setStatus({
- message: t('__LOGIN_FORM_FAILED_INVALID'),
- type: 'invalid',
- });
- setSubmitting(false);
- return;
- }
- }
- try {
- await WPAPI.login({
- username: values.email,
- password: values.password,
- security: nonce,
- });
-
- setCta(`${t('__LOGIN_FORM_CTA_REDIRECT_STATE')}`);
- document.location.href = from || '/';
- } catch (e: any) {
- if (e?.status !== 200) {
- showGenericErrorToast('Login: ');
- setSubmitting(false);
- return;
- }
- const { message } = e as Error;
- const error = JSON.parse(message);
-
- if (error.type === 'invalid') {
- setStatus({
- message: t('__LOGIN_FORM_FAILED_INVALID'),
- type: 'invalid',
- });
- } else {
- showGenericErrorToast();
- }
- }
-
- setSubmitting(false);
};
return (
From dce13b8b12a218dad5bc148685aa35a92f56d164 Mon Sep 17 00:00:00 2001
From: ZecD
Date: Thu, 14 May 2026 12:07:02 +0200
Subject: [PATCH 3/4] chore(react): regenerate schema and api client for
/signedMedia/:id
---
src/common/schema.ts | 28 ++++++++++++++++++++++++++++
src/features/api/index.ts | 13 +++++++++++++
2 files changed, 41 insertions(+)
diff --git a/src/common/schema.ts b/src/common/schema.ts
index 211da81f1..d374e2820 100644
--- a/src/common/schema.ts
+++ b/src/common/schema.ts
@@ -372,6 +372,14 @@ export interface paths {
};
};
};
+ "/signedMedia/{id}": {
+ get: operations["get-signedMedia-id"];
+ parameters: {
+ path: {
+ id: string;
+ };
+ };
+ };
"/plans/{pid}": {
/** */
get: operations["get-workspaces-wid-plans-pid"];
@@ -3596,6 +3604,26 @@ export interface operations {
302: never;
};
};
+ /** Returns a short-lived presigned S3 URL for a media item if the caller has access. Returns 403 otherwise. Public bugs (unexpired wp_appq_bug_link) bypass auth. */
+ "get-signedMedia-id": {
+ parameters: {
+ path: {
+ id: string;
+ };
+ };
+ responses: {
+ /** OK */
+ 200: {
+ content: {
+ "application/json": {
+ url: string;
+ };
+ };
+ };
+ /** Forbidden */
+ 403: unknown;
+ };
+ };
/** */
"get-workspaces-wid-plans-pid": {
parameters: {
diff --git a/src/features/api/index.ts b/src/features/api/index.ts
index b031a5907..3bc4a7892 100644
--- a/src/features/api/index.ts
+++ b/src/features/api/index.ts
@@ -451,6 +451,12 @@ const injectedRtkApi = api.injectEndpoints({
getMediaById: build.query({
query: (queryArg) => ({ url: `/media/${queryArg.id}` }),
}),
+ getSignedMediaById: build.query<
+ GetSignedMediaByIdApiResponse,
+ GetSignedMediaByIdApiArg
+ >({
+ query: (queryArg) => ({ url: `/signedMedia/${queryArg.id}` }),
+ }),
deletePlansByPid: build.mutation<
DeletePlansByPidApiResponse,
DeletePlansByPidApiArg
@@ -1748,6 +1754,12 @@ export type GetMediaByIdApiResponse = unknown;
export type GetMediaByIdApiArg = {
id: string;
};
+export type GetSignedMediaByIdApiResponse = /** status 200 OK */ {
+ url: string;
+};
+export type GetSignedMediaByIdApiArg = {
+ id: string;
+};
export type DeletePlansByPidApiResponse = unknown;
export type DeletePlansByPidApiArg = {
pid: string;
@@ -3468,6 +3480,7 @@ export const {
useGetInvitesByProfileAndTokenQuery,
useDeleteMediaCommentByMcidMutation,
useGetMediaByIdQuery,
+ useGetSignedMediaByIdQuery,
useDeletePlansByPidMutation,
useGetPlansByPidQuery,
usePatchPlansByPidMutation,
From e019998b230ca894d48cb444639288927215e14a Mon Sep 17 00:00:00 2001
From: ZecD
Date: Thu, 14 May 2026 12:08:45 +0200
Subject: [PATCH 4/4] feat(react): add /media/:id page resolving signedMedia
and routing on result
---
src/common/Pages.tsx | 5 ++++
src/pages/Media/index.tsx | 60 +++++++++++++++++++++++++++++++++++++++
2 files changed, 65 insertions(+)
create mode 100644 src/pages/Media/index.tsx
diff --git a/src/common/Pages.tsx b/src/common/Pages.tsx
index 43a448ff2..d6c2b57a8 100644
--- a/src/common/Pages.tsx
+++ b/src/common/Pages.tsx
@@ -18,6 +18,7 @@ import Project from 'src/pages/Dashboard/Project';
import InsightsPage from 'src/pages/Insights';
import LoginPage from 'src/pages/LoginPage';
import Manual from 'src/pages/Manual';
+import MediaPage from 'src/pages/Media';
import MediaNotFound from 'src/pages/NotFound/MediaNotFound';
import NotFound from 'src/pages/NotFound/NotFound';
import Plan from 'src/pages/Plan';
@@ -128,6 +129,10 @@ const Pages = () => {
path={`/${langPrefix}/media/oops`}
element={}
/>
+ }
+ />
} />
} />
} />
diff --git a/src/pages/Media/index.tsx b/src/pages/Media/index.tsx
new file mode 100644
index 000000000..7a9a37b0a
--- /dev/null
+++ b/src/pages/Media/index.tsx
@@ -0,0 +1,60 @@
+import { useEffect } from 'react';
+import { useLocation, useNavigate, useParams } from 'react-router-dom';
+import { PageLoader } from 'src/common/components/PageLoader';
+import {
+ useGetSignedMediaByIdQuery,
+ useGetUsersMeQuery,
+} from 'src/features/api';
+import { useLocalizeRoute } from 'src/hooks/useLocalizedRoute';
+
+const MediaPage = () => {
+ const { id } = useParams<{ id: string }>();
+ const { pathname } = useLocation();
+ const navigate = useNavigate();
+ const loginRoute = useLocalizeRoute('login');
+ const oopsRoute = useLocalizeRoute('media/oops');
+
+ const {
+ data,
+ error: signedError,
+ isLoading: isSignedLoading,
+ isFetching: isSignedFetching,
+ } = useGetSignedMediaByIdQuery({ id: id ?? '' }, { skip: !id });
+
+ const {
+ error: userError,
+ isLoading: isUserLoading,
+ isFetching: isUserFetching,
+ } = useGetUsersMeQuery();
+
+ useEffect(() => {
+ if (isSignedLoading || isSignedFetching) return;
+ if (data?.url) {
+ window.location.assign(data.url);
+ return;
+ }
+ if (!signedError) return;
+ if (isUserLoading || isUserFetching) return;
+ if (userError) {
+ navigate(loginRoute, { state: { from: pathname }, replace: true });
+ } else {
+ navigate(oopsRoute, { replace: true });
+ }
+ }, [
+ data,
+ signedError,
+ isSignedLoading,
+ isSignedFetching,
+ userError,
+ isUserLoading,
+ isUserFetching,
+ navigate,
+ loginRoute,
+ oopsRoute,
+ pathname,
+ ]);
+
+ return ;
+};
+
+export default MediaPage;