Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(AAS List): Handle protected repositories in aas list #77

Open
wants to merge 11 commits into
base: dev
Choose a base branch
from
Open
4 changes: 2 additions & 2 deletions src/app/[locale]/_components/DashboardInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ export const DashboardInput = () => {
const navigate = useRouter();

const browseAasUrl = async (searchString: string) => {
const { isSuccess, result } = await performFullAasSearch(searchString.trim());
if (!isSuccess) throw new LocalizedError(messages.mnestix.aasUrlNotFound);
const { isSuccess, result } = await performFullAasSearch(searchString.trim());
if (!isSuccess) throw new LocalizedError('url-not-found');

if (result.aas) {
setAas(result.aas);
Expand Down
18 changes: 9 additions & 9 deletions src/app/[locale]/_components/ManualAasInput.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
import { Box, IconButton, InputAdornment, TextField } from '@mui/material';
import { FormattedMessage, useIntl } from 'react-intl';
import { FormattedMessage } from 'react-intl';
import React, { useEffect, useRef, useState } from 'react';
import { ArrowForward } from '@mui/icons-material';
import { messages } from 'lib/i18n/localization';
import CloseIcon from '@mui/icons-material/Close';
import { SquaredIconButton } from 'components/basics/Buttons';
import { LocalizedError } from 'lib/util/LocalizedError';
import { useNotificationSpawner } from 'lib/hooks/UseNotificationSpawner';
import { showError } from 'lib/util/ErrorHandlerUtil';
import { useShowError } from 'lib/hooks/UseShowError';
import { useTranslations } from 'next-intl';

export function ManualAasInput(props: { onSubmit: (input: string) => Promise<void> }) {
const [inputValue, setInputValue] = useState<string>('');
const [isLoading, setIsLoading] = useState<boolean>(false);
const [isError, setIsError] = useState<boolean>(false);
const [errorText, setErrorText] = useState<string>('');
const intl = useIntl();
const inputRef = useRef<HTMLInputElement>(null);
const notificationSpawner = useNotificationSpawner();

const { showError } = useShowError();
const t = useTranslations('errors');

useEffect(() => {
inputRef?.current?.focus();
}, []);
Expand All @@ -38,9 +38,9 @@ export function ManualAasInput(props: { onSubmit: (input: string) => Promise<voi
await props.onSubmit(inputValue);
} catch (e) {
setIsLoading(false);
const msg = e instanceof LocalizedError ? e.descriptor : messages.mnestix.unexpectedError;
setError(intl.formatMessage(msg));
if (!(e instanceof LocalizedError)) showError(e, notificationSpawner);
const msg = e instanceof LocalizedError ? e.descriptor : 'unexpected-error';
setError(t(msg));
if (!(e instanceof LocalizedError)) showError(e);
}
};

Expand Down
12 changes: 5 additions & 7 deletions src/app/[locale]/_components/QrScanner.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,11 @@ import { Box, CircularProgress, IconButton, useTheme } from '@mui/material';
import { QrStream } from 'app/[locale]/_components/QrStream';
import CancelIcon from '@mui/icons-material/Cancel';
import { useNotificationSpawner } from 'lib/hooks/UseNotificationSpawner';
import { messages } from 'lib/i18n/localization';
import { useIntl } from 'react-intl';
import { LocalizedError } from 'lib/util/LocalizedError';
import { keyframes, styled } from '@mui/system';
import { ThemeProvider } from '@mui/material/styles';
import CircleIcon from '@mui/icons-material/Circle';
import { useTranslations } from 'next-intl';

enum State {
Stopped,
Expand All @@ -24,7 +23,7 @@ export function QrScanner(props: { onScan: (scanResult: string) => Promise<void>
const [state, setState] = useState<State>(State.Stopped);

const notificationSpawner = useNotificationSpawner();
const intl = useIntl();
const t = useTranslations('errors');

const theme = useTheme();
const size = props.size || 250;
Expand All @@ -34,7 +33,7 @@ export function QrScanner(props: { onScan: (scanResult: string) => Promise<void>
setState(State.ShowVideo);
} else {
notificationSpawner.spawn({
message: intl.formatMessage(messages.mnestix.qrScanner.errorOnQrScannerOpen),
message: t('qr-scanner.error-on-qr-scanner-open'),
severity: 'error',
});
setState(State.Stopped);
Expand Down Expand Up @@ -87,10 +86,9 @@ export function QrScanner(props: { onScan: (scanResult: string) => Promise<void>
await props.onScan(result);
setState(State.Stopped);
} catch (e) {
const msg =
e instanceof LocalizedError ? e.descriptor : messages.mnestix.qrScanner.defaultCallbackErrorMsg;
const msg = e instanceof LocalizedError ? e.descriptor : 'qr-scanner.default-callback-error-msg';
notificationSpawner.spawn({
message: intl.formatMessage(msg),
message: t(msg),
severity: 'error',
});
setState(State.LoadScanner);
Expand Down
12 changes: 5 additions & 7 deletions src/app/[locale]/asset/_components/RedirectToViewer.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
'use client';
import { useAsyncEffect } from 'lib/hooks/UseAsyncEffect';
import { encodeBase64 } from 'lib/util/Base64Util';
import { useNotificationSpawner } from 'lib/hooks/UseNotificationSpawner';
import { Box } from '@mui/material';
import { showError } from 'lib/util/ErrorHandlerUtil';
import { NotFoundError } from 'lib/errors/NotFoundError';
import { useState } from 'react';
import { CenteredLoadingSpinner } from 'components/basics/CenteredLoadingSpinner';
Expand All @@ -13,17 +11,17 @@ import { useAasOriginSourceState, useAasState } from 'components/contexts/Curren
import { performDiscoveryAasSearch } from 'lib/services/search-actions/searchActions';
import { wrapSuccess } from 'lib/util/apiResponseWrapper/apiResponseWrapper';
import { LocalizedError } from 'lib/util/LocalizedError';
import { messages } from 'lib/i18n/localization';
import { useShowError } from 'lib/hooks/UseShowError';

export const RedirectToViewer = () => {
const navigate = useRouter();
const searchParams = useSearchParams();
const assetIdParam = searchParams.get('assetId')?.toString();
const notificationSpawner = useNotificationSpawner();
const [isLoading, setIsLoading] = useState(false);
const [isError, setIsError] = useState(false);
const [, setAas] = useAasState();
const [, setAasOriginUrl] = useAasOriginSourceState();
const { showError } = useShowError();

useAsyncEffect(async () => {
try {
Expand All @@ -32,19 +30,19 @@ export const RedirectToViewer = () => {
} catch (e) {
setIsLoading(false);
setIsError(true);
showError(e, notificationSpawner);
showError(e);
}
}, []);

async function navigateToViewerOfAsset(assetId: string | undefined): Promise<void> {
const { isSuccess, result: aasIds } = await getAasIdsOfAsset(assetId);

if (!isSuccess) throw new LocalizedError(messages.mnestix.aasUrlNotFound);
if (!isSuccess) throw new LocalizedError('url-not-found');

assertAtLeastOneAasIdExists(aasIds);
const targetUrl = determineViewerTargetUrl(aasIds);
setAas(null);
setAasOriginUrl(null)
setAasOriginUrl(null);
navigate.replace(targetUrl);
}

Expand Down
17 changes: 8 additions & 9 deletions src/app/[locale]/compare/_components/CompareView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,20 @@ import { CompareSubmodelsAccordion } from 'app/[locale]/compare/_components/Comp
import { CompareAasAddDialog } from 'app/[locale]/compare/_components/add-aas/CompareAasAddDialog';
import { useCompareAasContext } from 'components/contexts/CompareAasContext';
import { useEffect, useState } from 'react';
import { useNotificationSpawner } from 'lib/hooks/UseNotificationSpawner';
import { useSearchParams } from 'next/navigation';
import { showError } from 'lib/util/ErrorHandlerUtil';
import { LocalizedError } from 'lib/util/LocalizedError';
import { performFullAasSearch } from 'lib/services/search-actions/searchActions';
import { useShowError } from 'lib/hooks/UseShowError';

export function CompareView() {
const { compareAas, addSeveralAas, deleteAas, addAas } = useCompareAasContext();
const [isLoadingAas, setIsLoadingAas] = useState(false);
const notificationSpawner = useNotificationSpawner();
const searchParams = useSearchParams();
const aasIds = searchParams.getAll('aasId').map((aasId) => {
return decodeURIComponent(aasId);
});
const [addModalOpen, setAddModalOpen] = useState(false);
const { showError } = useShowError();

useEffect(() => {
async function _fetchAas() {
Expand All @@ -32,14 +31,14 @@ export function CompareView() {
addSeveralAas(aasIds);
}
} catch (e) {
showError(e, notificationSpawner);
showError(e);
} finally {
setIsLoadingAas(false);
}
}

_fetchAas().catch((reason) => {
showError(reason, notificationSpawner);
showError(reason);
});
}, []);

Expand All @@ -57,21 +56,21 @@ export function CompareView() {

const handleAddAas = async (aasId: string) => {
const { isSuccess, result } = await performFullAasSearch(aasId);
if (!isSuccess) throw new LocalizedError(messages.mnestix.aasUrlNotFound);
if (!isSuccess) throw new LocalizedError('url-not-found');

if (!result.aas) {
throw new LocalizedError(messages.mnestix.compare.moreAasFound);
throw new LocalizedError('compare-error.more-aas-found');
}

const aasExists = compareAas.find((compareAas) => compareAas.aas.id === result.aas!.id);
if (aasExists) {
throw new LocalizedError(messages.mnestix.compare.aasAlreadyAdded);
throw new LocalizedError('compare-error.aas-already-added');
}

try {
await addAas(result.aas, result.aasData);
} catch (e) {
throw new LocalizedError(messages.mnestix.compare.aasAddError);
throw new LocalizedError('compare-error.aas-add-error');
}

setAddModalOpen(false);
Expand Down
104 changes: 63 additions & 41 deletions src/app/[locale]/list/_components/AasListDataWrapper.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import { AasListDto, ListEntityDto } from 'lib/services/list-service/ListService';
import { useAsyncEffect } from 'lib/hooks/UseAsyncEffect';
import { getAasListEntities } from 'lib/services/list-service/aasListApiActions';
import { showError } from 'lib/util/ErrorHandlerUtil';
import { useShowError } from 'lib/hooks/UseShowError';
import { useState } from 'react';
import { useNotificationSpawner } from 'lib/hooks/UseNotificationSpawner';
import { CenteredLoadingSpinner } from 'components/basics/CenteredLoadingSpinner';
import AasList from './AasList';
import { useEnv } from 'app/env/provider';
Expand All @@ -13,22 +12,33 @@ import ArrowBackIosNewIcon from '@mui/icons-material/ArrowBackIosNew';
import ArrowForwardIosIcon from '@mui/icons-material/ArrowForwardIos';
import { SelectRepository } from './filter/SelectRepository';
import { useTranslations } from 'next-intl';
import { ApiResponseWrapperError, ApiResultStatus } from 'lib/util/apiResponseWrapper/apiResponseWrapper';
import { AuthenticationPrompt } from 'components/azureAuthentication/AuthenticationPrompt';

export default function AasListDataWrapper() {
const [isLoadingList, setIsLoadingList] = useState(false);
const [aasList, setAasList] = useState<AasListDto>();
const [, setAasListFiltered] = useState<ListEntityDto[]>();
const [selectedAasList, setSelectedAasList] = useState<string[]>();
const notificationSpawner = useNotificationSpawner();
const [selectedRepository, setSelectedRepository] = useState<string | undefined>();
const env = useEnv();
const t = useTranslations('aas-list');
const { showError } = useShowError();

//Pagination
const [currentCursor, setCurrentCursor] = useState<string>();
const [cursorHistory, setCursorHistory] = useState<(string | undefined)[]>([]);
const [currentPage, setCurrentPage] = useState(0);

//Authentication
const [needAuthentication, setNeedAuthentication] = useState<boolean>(false);

const clearResults = () => {
setAasList(undefined);
setCurrentCursor(undefined);
setNeedAuthentication(false);
};

useAsyncEffect(async () => {
resetPagination();
await fetchListData();
Expand All @@ -38,6 +48,7 @@ export default function AasListDataWrapper() {
if (!selectedRepository) return;

setIsLoadingList(true);
clearResults();
const response = await getAasListEntities(selectedRepository!, 10, newCursor);

if (response.success) {
Expand All @@ -49,7 +60,11 @@ export default function AasListDataWrapper() {
setCursorHistory((prevHistory) => [...prevHistory, newCursor]);
}
} else {
showError(response.error, notificationSpawner);
if ((response.error as ApiResponseWrapperError<AasListDto>).errorCode == ApiResultStatus.UNAUTHORIZED) {
setNeedAuthentication(true);
} else {
showError(response.error);
}
}
setIsLoadingList(false);
};
Expand Down Expand Up @@ -95,6 +110,49 @@ export default function AasListDataWrapper() {
setSelectedAasList(selected);
};

const pagination = (
<Box display="flex" justifyContent="flex-end" alignItems="center" marginTop={0}>
<Typography paddingRight="1.625rem" fontSize="0.75rem">
{t('page') + ' ' + (currentPage + 1)}
</Typography>
<IconButton onClick={handleGoBack} disabled={currentPage === 0} data-testid="list-back-button">
<ArrowBackIosNewIcon fontSize="small" />
</IconButton>
<IconButton onClick={handleNextPage} disabled={!currentCursor} data-testid="list-next-button">
<ArrowForwardIosIcon fontSize="small" />
</IconButton>
</Box>
);

const ListContent = (props: { selectedRepository: string | undefined }) => {
const selectedRepository = props.selectedRepository;
if (!selectedRepository) {
return (
<Box>
<Typography data-testid="select-repository-text">{t('select-repository')}</Typography>
</Box>
);
}

if (needAuthentication) {
return <AuthenticationPrompt />;
}

return (
<>
<AasList
data-testid="aas-list"
repositoryUrl={selectedRepository}
shells={aasList}
selectedAasList={selectedAasList}
updateSelectedAasList={updateSelectedAasList}
comparisonFeatureFlag={env.COMPARISON_FEATURE_FLAG}
></AasList>
{pagination}
</>
);
};

return (
<Card>
<CardContent sx={{ paddingX: 0, paddingY: '1.625rem', '&:last-child': { paddingBottom: '0' } }}>
Expand All @@ -112,43 +170,7 @@ export default function AasListDataWrapper() {
{isLoadingList ? (
<CenteredLoadingSpinner sx={{ my: 10 }} />
) : (
<>
{selectedRepository ? (
<>
<AasList
data-testid="aas-list"
repositoryUrl={selectedRepository}
shells={aasList}
selectedAasList={selectedAasList}
updateSelectedAasList={updateSelectedAasList}
comparisonFeatureFlag={env.COMPARISON_FEATURE_FLAG}
></AasList>
<Box display="flex" justifyContent="flex-end" alignItems="center" marginTop={0}>
<Typography paddingRight="1.625rem" fontSize="0.75rem">
{t('page') + ' ' + (currentPage + 1)}
</Typography>
<IconButton
onClick={handleGoBack}
disabled={currentPage === 0}
data-testid="list-back-button"
>
<ArrowBackIosNewIcon fontSize="small" />
</IconButton>
<IconButton
onClick={handleNextPage}
disabled={!currentCursor}
data-testid="list-next-button"
>
<ArrowForwardIosIcon fontSize="small" />
</IconButton>
</Box>
</>
) : (
<Box>
<Typography data-testid="select-repository-text">{t('select-repository')}</Typography>
</Box>
)}
</>
<ListContent selectedRepository={selectedRepository} />
)}
</CardContent>
</Card>
Expand Down
2 changes: 1 addition & 1 deletion src/app/[locale]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { GoToListButton } from './_components/GoToListButton';

export default async function page() {
const t = await getTranslations('dashboard');

return (
<Box sx={{ p: 2, m: 'auto' }}>
<Typography data-testid="welcome-text" variant="h1" color="primary" align="center" sx={{ mt: 2 }}>
Expand All @@ -18,7 +19,6 @@ export default async function page() {

<DashboardInput />
<GoToListButton />

<Typography align="center" sx={{ mt: 4 }}>
{t('findOutMore-text')}:
</Typography>
Expand Down
Loading
Loading