Skip to content

Commit

Permalink
fix: improve in context errors (#3344)
Browse files Browse the repository at this point in the history
  • Loading branch information
stepan662 authored Jun 13, 2024
1 parent de15b78 commit 93dfbd1
Show file tree
Hide file tree
Showing 31 changed files with 3,864 additions and 3,671 deletions.
2 changes: 1 addition & 1 deletion e2e/cypress/common/simulateReqAndResponse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export const simulateReqAndResponse = ({
cy.intercept({ path: '/v2/projects/*/keys/**', method: 'put' }, (req) => {
checkRequest?.(req.body);
// response is not checked by the client
req.reply({});
req.reply({ response: 'success' });
}).as(randomId);

getDevUi().contains('Update').click();
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@
"prettier": "^3.2.5",
"prettier-plugin-svelte": "3.2.2",
"semantic-release": "^21.0.6",
"terminate": "^2.6.1",
"ts-node": "^10.9.1",
"turbo": "1.13.2",
"typescript": "^4.9.5"
Expand Down
3 changes: 2 additions & 1 deletion packages/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
"test": "jest --collect-coverage",
"lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
"preview": "vite preview",
"schema": "openapi-typescript http://localhost:8202/v3/api-docs/Accessible%20with%20API%20key --output ./src/ui/client/apiSchema.generated.ts",
"schema": "openapi-typescript 'http://localhost:8080/v3/api-docs/Accessible%20with%20Project%20API%20key%20(V2)' --output ./src/package/ui/client/apiSchema.generated.ts",
"tsc": "tsc --noEmit",
"clean": "rm -rf dist lib coverage"
},
Expand Down Expand Up @@ -69,6 +69,7 @@
"fast-text-encoding": "^1.0.6",
"jest": "^29.7.0",
"jest-environment-jsdom": "^29.7.0",
"openapi-typescript": "^6.7.6",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-query": "^3.39.3",
Expand Down
2 changes: 1 addition & 1 deletion packages/web/src/package/ui/InContextUi.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { Root, createRoot } from 'react-dom/client';
import { KeyContextMenu } from './KeyContextMenu/KeyContextMenu';

export const InContextUi = (props: UiProps) => {
let rootElement: ShadowRoot | undefined;
let rootElement: Element | undefined;
let tolgeeModalRoot: Root;
let contextMenuRoot: Root;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ export class KeyContextMenu extends React.Component<
this.setState({ opened: false });
this.state.onSelect?.(undefined);
}}
container={getRootElement().host}
container={getRootElement()}
style={{ zIndex: DEVTOOLS_Z_INDEX }}
>
{Array.from(this.state.keys || []).map(
Expand Down
97 changes: 97 additions & 0 deletions packages/web/src/package/ui/KeyDialog/ErrorAlert.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
import { Alert, AlertTitle } from '@mui/material';
import { HttpError } from '../client/HttpError';
import { useDialogContext } from './dialogContext';
import { NewTabLink } from './Link';

type Props = {
error: HttpError | Error;
severity?: 'error' | 'info';
};

export const ErrorAlert = ({ error, severity = 'error' }: Props) => {
const apiUrl = useDialogContext((c) => c.uiProps.apiUrl);

return (
<Alert sx={{ mt: 2 }} severity={severity}>
{error instanceof HttpError
? getErrorContent(error, apiUrl)
: error.message}
</Alert>
);
};

function DocsInContext() {
return (
<NewTabLink href="https://tolgee.io/js-sdk/in-context">
Learn more in Docs
</NewTabLink>
);
}

function DocsAPIKeys() {
return (
<NewTabLink href="https://tolgee.io/platform/account_settings/api_keys_and_pat_tokens">
Learn more in Docs
</NewTabLink>
);
}

function getErrorContent({ code, params, message }: HttpError, apiUrl: string) {
switch (code) {
case 'operation_not_permitted':
return (
<>
<AlertTitle>Operation not permitted</AlertTitle>
{Boolean(params?.length) && 'Missing scopes: ' + params?.join(', ')}
</>
);

case 'invalid_project_api_key':
return (
<>
<AlertTitle>Invalid API key</AlertTitle>
Check it in the code or in the chrome plugin. <DocsInContext />
</>
);

case 'api_url_not_specified':
return (
<>
<AlertTitle>Oops... I miss the API url</AlertTitle>
Add it in the code or via the chrome plugin. <DocsInContext />
</>
);

case 'api_url_not_valid':
return (
<>
<AlertTitle>API url is not correct ({apiUrl})</AlertTitle>
Check it in the code or in the chrome plugin. <DocsInContext />
</>
);

case 'api_key_not_specified':
return (
<>
<AlertTitle>Oops... I miss the API key</AlertTitle>
Add it in the code or via the chrome plugin. <DocsInContext />
</>
);

case 'permissions_not_sufficient_to_edit':
return (
<>
<AlertTitle>
Sorry, you don't have permissions to make changes
</AlertTitle>
Update your API key or ask admin for more permissions <DocsAPIKeys />
</>
);

case 'fetch_error':
return `Failed to fetch (${apiUrl})`;

default:
return message;
}
}
71 changes: 42 additions & 29 deletions packages/web/src/package/ui/KeyDialog/KeyForm.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { IconButton, Button, styled, useTheme } from '@mui/material';
import { IconButton, Button, styled, useTheme, Link } from '@mui/material';
import { OpenInNew } from '@mui/icons-material';

import { TranslationFields } from './TranslationFields';
Expand All @@ -11,6 +11,9 @@ import { NsSelect } from './NsSelect';
import { TOLGEE_RESTRICT_ATTRIBUTE } from '../../constants';
import { Tags } from './Tags/Tags';
import { PluralFormCheckbox } from './PluralFormCheckbox';
import { ErrorAlert } from './ErrorAlert';
import { HttpError } from '../client/HttpError';
import { Tooltip } from '../common/Tooltip';

const ScContainer = styled('div')`
font-family: Rubik, Roboto, Arial;
Expand Down Expand Up @@ -68,14 +71,16 @@ const ScControls = styled('div')`
min-height: 36px;
`;

const ScRestriction = styled('div')`
margin-top: 8px;
color: ${({ theme }) => theme.palette.text.secondary};
const ScKeyTitle = styled(ScFieldTitle)`
justify-content: start;
gap: 4px;
align-items: center;
`;

const ScError = styled('div')`
padding-top: 16px;
color: red;
const ScLinkIcon = styled(Link)`
display: grid;
font-size: 16px;
margin: 0px 0px;
`;

export const KeyForm = () => {
Expand Down Expand Up @@ -130,64 +135,72 @@ export const KeyForm = () => {
<path d="M97.16,7.27a16.94,16.94,0,0,0-1.9,24.47,16.36,16.36,0,0,0,5,3.83,3.23,3.23,0,0,1-2.9,5.77,23.14,23.14,0,0,1-11.41-13C73.83,31.1,63.46,37.09,52.82,46.51c-27.44,24.3-34.35,61.74-16.38,85.26-4.57,5.79-8,12.22-8.9,18.69a20.88,20.88,0,0,0,5.62,18c9.18,9.61,21.42,7.13,31.26,5.14,6.58-1.34,12.8-2.6,16.5-.23,3.22,2.07,3.47,3.87,3.61,4.45,2.1,9.32-5.79,13.89-7.67,16.27a1.48,1.48,0,0,0,1.13,2.4c3.48,0,9-1.18,12.34-4.08s7.16-7.9,5.89-16.32c-.08-.5-.18-1-.32-1.58-.86-3.35-3.1-7.57-8.61-11.09-7.72-4.95-17-3.07-25.22-1.41-9.76,2-16,2.85-20.37-1.71a9.13,9.13,0,0,1-2.46-8.19c.54-3.77,2.65-7.89,5.62-11.86,21.71,16.89,56.87,13.47,82.67-9.39a75.34,75.34,0,0,0,20.81-28.09A23.14,23.14,0,0,1,134.8,89a3.23,3.23,0,0,1,6.08-2.19,16.37,16.37,0,0,0,3.2,5.39,16.85,16.85,0,1,0,11.48-28,3.23,3.23,0,0,1-.51-6.44,23.41,23.41,0,0,1,12.88,2.69c2.6-14.08,3.34-31.41-2.06-37.51-4.08-4.61-20.62-8-35.18-7.76A23.48,23.48,0,0,1,130.8,25a3.23,3.23,0,0,1-6.33-1.28A16.94,16.94,0,0,0,97.16,7.27Zm63.25,21a5.29,5.29,0,0,1-.57,6.19c-1.29,1.14-2.72-.51-4.1-2.06s-3.1-3.42-1.81-4.56A5.74,5.74,0,0,1,160.41,28.27Z"></path>
</svg>
</a>

<ScHeadingTitle>Quick translation</ScHeadingTitle>

{!useBrowserWindow && (
<IconButton
title="Open in new window"
onClick={() => setUseBrowserWindow(true)}
color="inherit"
size="small"
>
<OpenInNew fontSize="small" />
</IconButton>
<Tooltip title="Open in separate window">
<IconButton
onClick={() => setUseBrowserWindow(true)}
color="inherit"
size="small"
>
<OpenInNew fontSize="small" />
</IconButton>
</Tooltip>
)}
<ScHeadingRight>{!loading && <LanguageSelect />}</ScHeadingRight>
</ScHeading>

<ScFieldTitle>Key</ScFieldTitle>
<ScKeyTitle>
Key
{linkToPlatform && (
<Tooltip title="Open key in Tolgee platform">
<ScLinkIcon
role="button"
href={linkToPlatform}
target="_blank"
rel="noreferrer noopener"
sx={{ color: 'inherit' }}
>
<OpenInNew />
</ScLinkIcon>
</Tooltip>
)}
</ScKeyTitle>
<ScKey>
{input}
<ScKeyHint>
{!keyExists && ready && " (key doesn't exist yet)"}
</ScKeyHint>
</ScKey>

<NsSelect
options={fallbackNamespaces}
value={selectedNs}
onChange={setSelectedNs}
/>

{ready && (
<ScTagsWrapper>
<ScFieldTitle>Tags</ScFieldTitle>
<Tags />
</ScTagsWrapper>
)}

{ready && viewPluralCheckbox && <PluralFormCheckbox />}

{!error && (
<ScFieldsWrapper>
<TranslationFields />
</ScFieldsWrapper>
)}

{screenshotsView && ready && (
<ScGalleryWrapper>
<ScreenshotGallery />
</ScGalleryWrapper>
)}

{formDisabled && ready && (
<ScRestriction>
Modification is restricted due to missing permissions.
</ScRestriction>
<ErrorAlert
error={new HttpError('permissions_not_sufficient_to_edit')}
severity="info"
/>
)}

{generalError && <ScError>{generalError}</ScError>}
{generalError && <ErrorAlert error={generalError} />}
<ScControls>
<Button onClick={onClose} color="secondary">
{useBrowserWindow ? 'Close' : 'Cancel'}
Expand Down
26 changes: 26 additions & 0 deletions packages/web/src/package/ui/KeyDialog/Link.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { OpenInNew } from '@mui/icons-material';
import { styled } from '@mui/material';

type Props = {
href: string;
children: React.ReactNode;
};

const StyledLink = styled('a')``;

const StyledIcon = styled(OpenInNew)`
width: 17px;
height: 17px;
position: relative;
top: 3px;
margin-left: 1px;
`;

export const NewTabLink = ({ href, children }: Props) => {
return (
<StyledLink href={href} target="_blank" rel="noreferrer noopener">
{children}
<StyledIcon />
</StyledLink>
);
};
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import React, { useRef, useState } from 'react';
import { styled, CircularProgress, IconButton, Tooltip } from '@mui/material';
import { styled, CircularProgress, IconButton } from '@mui/material';
import { CameraAlt, AddCircleOutline } from '@mui/icons-material';

import { ScreenshotDropzone } from './ScreenshotDropzone';
import { ScreenshotThumbnail } from './ScreenshotThumbnail';
import { MAX_FILE_COUNT } from './utils';
import { DEVTOOLS_Z_INDEX } from '../../../constants';
import { useDialogContext, useDialogActions } from '../dialogContext';
import { ScreenshotDetail } from './ScreenshotDetail';
import { ScFieldTitle } from '../../common/FieldTitle';
import { ExtensionPrompt } from './ExtensionPrompt';
import { Tooltip } from '../../common/Tooltip';

const ScPlaceholder = styled('div')`
display: flex;
Expand Down Expand Up @@ -134,13 +134,7 @@ export const ScreenshotGallery: React.FC = () => {
{uploadEnabled && (
<>
{(isChrome || ableToTakeScreenshot) && (
<Tooltip
title="Take screenshot"
PopperProps={{
disablePortal: true,
style: { zIndex: DEVTOOLS_Z_INDEX },
}}
>
<Tooltip title="Take screenshot">
<IconButton
sx={{ fontSize: 24 }}
onClick={
Expand All @@ -154,13 +148,7 @@ export const ScreenshotGallery: React.FC = () => {
</Tooltip>
)}

<Tooltip
title="Add image"
PopperProps={{
disablePortal: true,
style: { zIndex: DEVTOOLS_Z_INDEX },
}}
>
<Tooltip title="Add image">
<IconButton sx={{ fontSize: 24 }} onClick={onFileSelect}>
<AddCircleOutline />
</IconButton>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import clsx from 'clsx';
import React, { useState } from 'react';
import { styled, Tooltip, IconButton } from '@mui/material';
import { styled, IconButton } from '@mui/material';
import { Clear } from '@mui/icons-material';

import { ScreenshotInterface } from '../dialogContext/useGallery';
import { DEVTOOLS_Z_INDEX } from '../../../constants';
import { ScreenshotWithLabels } from './ScreenshotWithLabels';
import { Tooltip } from '../../common/Tooltip';

const ScreenshotBox = styled('div')`
border: 1px solid ${({ theme }) => theme.palette.grey[300]};
Expand Down Expand Up @@ -85,13 +85,7 @@ export const ScreenshotThumbnail: React.FC<Props> = (props) => {
return (
<ScreenshotBox onMouseOver={onMouseOver} onMouseOut={onMouseOut}>
{props.onDelete && (
<Tooltip
title="Delete"
PopperProps={{
disablePortal: true,
style: { zIndex: DEVTOOLS_Z_INDEX },
}}
>
<Tooltip title="Delete">
<DeleteIconButton onClick={onDeleteClick} className={clsx({ hover })}>
<DeleteIcon />
</DeleteIconButton>
Expand Down
Loading

0 comments on commit 93dfbd1

Please sign in to comment.