Error processing and search fixes in Admin UI (#2666)

* Error processing and search fixes in Admin UI

* Fixed permalink validation in the page admin modal
This commit is contained in:
Elena Shorohova 2024-08-28 18:19:02 +03:00 коммит произвёл GitHub
Родитель a8b70bff88
Коммит dfe3d6c382
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
9 изменённых файлов: 34 добавлений и 46 удалений

2
package-lock.json сгенерированный
Просмотреть файл

@ -6,7 +6,7 @@
"packages": {
"": {
"name": "apim-developer-portal",
"version": "2.29.0",
"version": "2.30.0",
"license": "MIT",
"dependencies": {
"@azure/api-management-custom-widgets-scaffolder": "^1.0.0-beta.4",

Просмотреть файл

@ -6,11 +6,10 @@ import { EventManager } from '@paperbits/common/events';
import { ViewManager } from '@paperbits/common/ui';
import { IMediaService } from '@paperbits/common/media';
import { MediaContract } from '@paperbits/common/media/mediaContract';
import { Query, Operator } from '@paperbits/common/persistence';
import { MimeTypes } from '@paperbits/common';
import { Checkbox, DefaultButton, IconButton, IIconProps, Image, ImageFit, IOverflowSetItemProps, Link, Modal, OverflowSet, SearchBox, Spinner, Stack, Text, TextField } from '@fluentui/react';
import { DeleteConfirmationOverlay } from '../utils/components/deleteConfirmationOverlay';
import { getAllValues, getThumbnailUrl } from '../utils/helpers';
import { createSearchQuery, getAllValues, getThumbnailUrl } from '../utils/helpers';
import { ImageDetailsModal } from './imageDetailsModal';
import { NonImageDetailsModal } from './nonImageDetailsModal';
@ -69,11 +68,7 @@ export class MediaModal extends React.Component<MediaModalProps, MediaModalState
searchMedia = async (searchPattern: string = ''): Promise<void> => {
this.setState({ isLoading: true });
const query = Query.from().orderBy('fileName');
if (searchPattern) {
query.where('fileName', Operator.contains, searchPattern);
}
const query = createSearchQuery(searchPattern, 'fileName');
const mediaSearchResult = await this.mediaService.search(query);
const allMedia = await getAllValues(mediaSearchResult, mediaSearchResult.value);
this.setState({ media: allMedia, isLoading: false });

Просмотреть файл

@ -6,9 +6,8 @@ import { EventManager } from '@paperbits/common/events';
import { ViewManager } from '@paperbits/common/ui';
import { IMediaService } from '@paperbits/common/media';
import { MediaContract } from '@paperbits/common/media/mediaContract';
import { Query, Operator } from '@paperbits/common/persistence';
import { DefaultButton, IIconProps, Image, ImageFit, IOverflowSetItemProps, Link, Modal, SearchBox, Stack, Text } from '@fluentui/react';
import { getAllValues } from '../utils/helpers';
import { createSearchQuery, getAllValues } from '../utils/helpers';
import { NonImageDetailsModal } from './nonImageDetailsModal';
interface MediaSelectionItemModalState {
@ -50,11 +49,7 @@ export class MediaSelectionItemModal extends React.Component<MediaSelectionItemM
}
searchMedia = async (searchPattern: string = ''): Promise<void> => {
const query = Query.from().orderBy('fileName');
if (searchPattern) {
query.where('fileName', Operator.contains, searchPattern);
}
const query = createSearchQuery(searchPattern, 'fileName');
const mediaSearchResult = await this.mediaService.search(query);
const allMedia = await getAllValues(mediaSearchResult, mediaSearchResult.value);
this.setState({ media: allMedia });
@ -171,8 +166,10 @@ export class MediaSelectionItemModal extends React.Component<MediaSelectionItemM
</Stack.Item>
</Stack>
<Stack horizontal tokens={{ childrenGap: 20 }} wrap>
{this.state.media.map(mediaItem =>
this.renderMediaItem(mediaItem)
{this.state.media.length === 0
? <Text block>It seems that you don't have media items yet.</Text>
: this.state.media.map(mediaItem =>
this.renderMediaItem(mediaItem)
)}
</Stack>
</div>

Просмотреть файл

@ -178,6 +178,10 @@ export class NavigationItemModal extends React.Component<NavigationItemModalProp
}, 300)
validatePermalink = async (permalink: string): Promise<string> => {
if (!permalink) {
return URL_REQUIRED_MESSAGE;
}
const isPermalinkNotDefined = await this.permalinkService.isPermalinkDefined(permalink) && !reservedPermalinks.includes(permalink);
let errorMessage = validateField(UNIQUE_REQUIRED, permalink, isPermalinkNotDefined);
@ -440,7 +444,7 @@ export class NavigationItemModal extends React.Component<NavigationItemModalProp
errors = { [LinkOptionKey.SavedUrl]: errorMessage };
} else if (this.state.selectedUrlType === LinkOptionKey.NewUrl) {
const errorMessage = await this.validatePermalink(this.state.navItem?.[LinkOptionKey.NewUrl]);
errors = { [LinkOptionKey.NewUrl]: errorMessage };
if (errorMessage) errors = { [LinkOptionKey.NewUrl]: errorMessage };
}
} else if (this.state.selectedLinkOption === LinkOptionKey.Media && !this.state.selectedMedia) {
errors = { media: 'Please, select a media file' };

Просмотреть файл

@ -49,7 +49,7 @@ export class PageDetailsModal extends React.Component<PageDetailsModalProps, Pag
}
onInputChange = async (field: string, newValue: string, validationType?: string): Promise<void> => {
let permalink = '';
let permalink = null;
if (!this.props.page && field === 'title') {
permalink = newValue.replace(/\s+/g, '-').toLowerCase();
@ -80,7 +80,7 @@ export class PageDetailsModal extends React.Component<PageDetailsModalProps, Pag
errorMessage = validateField(validationType, newValue);
}
if (permalink) {
if (permalink !== null) {
permalinkErrorMessage = await this.validatePermalink('/' + permalink);
}

Просмотреть файл

@ -2,11 +2,10 @@ import * as React from 'react';
import { Resolve } from '@paperbits/react/decorators';
import { IPageService, PageContract } from '@paperbits/common/pages';
import { ILayoutService, LayoutContract } from '@paperbits/common/layouts';
import { Query, Operator } from '@paperbits/common/persistence';
import { ViewManager } from '@paperbits/common/ui';
import { Router } from '@paperbits/common/routing';
import { CommandBarButton, FontIcon, IIconProps, Pivot, PivotItem, SearchBox, Spinner, Stack, Text } from '@fluentui/react';
import { getAllValues } from '../utils/helpers';
import { createSearchQuery, getAllValues } from '../utils/helpers';
import { lightTheme } from '../utils/themes';
import { BackButton } from '../utils/components/backButton';
import { PageDetailsModal } from './pageDetailsModal';
@ -78,22 +77,14 @@ export class Pages extends React.Component<PagesProps, PagesState> {
}
searchPages = async (searchPattern: string = ''): Promise<void> => {
const query = Query.from().orderBy('title');
if (searchPattern) {
query.where('title', Operator.contains, searchPattern);
}
const query = createSearchQuery(searchPattern);
const pagesSearchResult = await this.pageService.search(query);
const allPages = await getAllValues(pagesSearchResult, pagesSearchResult.value);
this.setState({ pages: allPages });
}
searchLayouts = async (searchPattern: string = ''): Promise<void> => {
const query = Query.from().orderBy('title');
if (searchPattern) {
query.where('title', Operator.contains, searchPattern);
}
const query = createSearchQuery(searchPattern);
const layoutsSearchResult = await this.layoutService.search(query);
const allLayouts = await getAllValues(layoutsSearchResult, layoutsSearchResult.value);
this.setState({ layouts: allLayouts });

Просмотреть файл

@ -1,10 +1,9 @@
import * as React from 'react';
import { Resolve } from '@paperbits/react/decorators';
import { IPopupService, PopupContract } from '@paperbits/common/popups';
import { Query, Operator } from '@paperbits/common/persistence';
import { ViewManager } from '@paperbits/common/ui';
import { CommandBarButton, FontIcon, IIconProps, SearchBox, Spinner, Stack, Text } from '@fluentui/react';
import { getAllValues } from '../utils/helpers';
import { createSearchQuery, getAllValues } from '../utils/helpers';
import { lightTheme } from '../utils/themes';
import { BackButton } from '../utils/components/backButton';
import { PopupDetailsModal } from './popupDetailsModal';
@ -54,11 +53,7 @@ export class Popups extends React.Component<PopupsProps, PopupsState> {
}
searchPopups = async (searchPattern: string = ''): Promise<void> => {
const query = Query.from().orderBy('title');
if (searchPattern) {
query.where('title', Operator.contains, searchPattern);
}
const query = createSearchQuery(searchPattern);
const popupsSearchResult = await this.popupService.search(query);
const allPopups = await getAllValues(popupsSearchResult, popupsSearchResult.value);
this.setState({ popups: allPopups });

Просмотреть файл

@ -1,10 +1,9 @@
import * as React from 'react';
import { Resolve } from '@paperbits/react/decorators';
import { IUrlService, UrlContract } from '@paperbits/common/urls';
import { Query, Operator } from '@paperbits/common/persistence';
import { ViewManager } from '@paperbits/common/ui';
import { CommandBarButton, FontIcon, IIconProps, SearchBox, Spinner, Stack, Text } from '@fluentui/react';
import { getAllValues } from '../utils/helpers';
import { createSearchQuery, getAllValues } from '../utils/helpers';
import { lightTheme } from '../utils/themes';
import { BackButton } from '../utils/components/backButton';
import { UrlDetailsModal } from './urlDetailsModal';
@ -54,11 +53,7 @@ export class Urls extends React.Component<UrlsProps, UrlsState> {
}
searchUrls = async (searchPattern: string = ''): Promise<void> => {
const query = Query.from().orderBy('title');
if (searchPattern) {
query.where('title', Operator.contains, searchPattern);
}
const query = createSearchQuery(searchPattern);
const urlsSearchResult = await this.urlService.search(query);
const allUrls = await getAllValues(urlsSearchResult, urlsSearchResult.value);
this.setState({ urls: allUrls });

Просмотреть файл

@ -1,5 +1,6 @@
import * as MediaUtils from "@paperbits/common/media/mediaUtils";
import { MediaContract } from "@paperbits/common/media";
import { Operator, Query } from "@paperbits/common/persistence";
export const getThumbnailUrl = (mediaItem: MediaContract): string => {
if (mediaItem?.mimeType?.startsWith("video")) {
@ -35,4 +36,14 @@ export const getAllValues = async (page: any, values: any) => {
}
return values;
}
export const createSearchQuery = (searchPattern: string, fieldName: string = 'title') => {
const patternProcessed = searchPattern.replaceAll("#", "%23"); // TODO: Remove this when the issue with # in search is fixed on the Paperbits side
const query = Query.from().orderBy(fieldName);
if (patternProcessed) {
query.where(fieldName, Operator.contains, patternProcessed);
}
return query;
}