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;