Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,7 @@ Config for this plugin is stored as a part of the `config/plugins.{js|ts}` or `c
- `pathDefaultFields` - The attribute to copy the default path from per content type. Syntax: `'api::<collection name>.<content type name>': ['url_slug', 'path']`. If you leave it empty, the default path is generated based on the ID.
- `gql` - If you're using GraphQL that's the right place to put all necessary settings. More **[ here ](#gql-configuration)**
- `cascadeMenuAttached` - If you don't want "Menu attached" to cascade on child items set this value `Disabled`.
- `isOverviewUiEnabled` - Enables the new management interface: an overview page listing all navigations with separate detail pages per navigation.

### Properties

Expand Down
1 change: 1 addition & 0 deletions admin/src/api/validators.ts
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ export const configSchema = z.object({
restrictedContentTypes: z.string().array(),
isCacheEnabled: z.boolean().optional(),
isCachePluginEnabled: z.boolean().optional(),
isOverviewUiEnabled: z.boolean().optional(),
});

export type ConfigFromServerSchema = z.infer<typeof configFromServerSchema>;
Expand Down
2 changes: 2 additions & 0 deletions admin/src/hooks/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export * from './useAPI';
export * from './useSettingsPermissions';
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import { getFetchClient } from '@strapi/strapi/admin';
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import { useCallback } from 'react';

import { getApiClient } from '../../../api';
import { NavigationSchema } from '../../../api/validators';
import { Effect } from '../../../types';
import { getApiClient } from '../api';
import { NavigationSchema } from '../api/validators';
import { Effect } from '../types';
import { appendViewId } from '../utils/appendViewId';

export const useLocale = () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { useMemo } from 'react';

import { useRBAC } from '@strapi/strapi/admin';

import pluginPermissions from '../../../utils/permissions';
import pluginPermissions from '../utils/permissions';

export const useSettingsPermissions = () => {
const viewPermissions = useMemo(
Expand Down
40 changes: 35 additions & 5 deletions admin/src/pages/App.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,48 @@
import { Page } from '@strapi/strapi/admin';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { DndProvider } from 'react-dnd';
import { HTML5Backend } from 'react-dnd-html5-backend';
import { Route, Routes } from 'react-router-dom';

import { useConfig } from '../hooks';
import { DetailPage } from './DetailPage';
import { HomePage } from './HomePage';
import { OverviewPage } from './OverviewPage';

const App = () => {
return (
<DndProvider backend={HTML5Backend}>
const queryClient = new QueryClient();

const AppRoutes = () => {
const configQuery = useConfig();

if (configQuery.isPending) {
return <Page.Loading />;
}

if (configQuery.data?.isOverviewUiEnabled) {
return (
<Routes>
<Route path={`/`} index Component={HomePage} />
<Route path={`/`} index Component={OverviewPage} />
<Route path={`/:documentId`} Component={DetailPage} />
<Route path={`/*`} Component={Page.Error} />
</Routes>
</DndProvider>
);
}

return (
<Routes>
<Route path={`/`} index Component={HomePage} />
<Route path={`/*`} Component={Page.Error} />
</Routes>
);
};

const App = () => {
return (
<QueryClientProvider client={queryClient}>
<DndProvider backend={HTML5Backend}>
<AppRoutes />
</DndProvider>
</QueryClientProvider>
);
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import {
useResetNavigations,
} from '../../hooks';
import { NavigationSchema } from '../../../../api/validators';
import { appendViewId } from '../../utils/appendViewId';
import { appendViewId } from '../../../../utils/appendViewId';

type NavigationEmptyStateProps = {
canUpdate: boolean;
Expand Down
120 changes: 120 additions & 0 deletions admin/src/pages/DetailPage/components/NavigationHeader/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
import { Button, Flex, Link, SingleSelect, SingleSelectOption, Tag } from '@strapi/design-system';
import { ArrowLeft, Check, Feather, Information } from '@strapi/icons';
import { Layouts } from '@strapi/strapi/admin';
import React from 'react';
import { useIntl } from 'react-intl';
import { NavLink } from 'react-router-dom';
import { NavigationSchema } from '../../../../api/validators';
import { PLUGIN_ID } from '../../../../pluginId';
import { getTrad } from '../../../../translations';
import { Effect } from '../../../../types';
import { useConfig, usePluginMediaQuery } from '../../hooks';

const submitIcon = <Check />;

interface Props {
activeNavigation?: NavigationSchema;
structureHasErrors?: boolean;
structureHasChanged?: boolean;
isSaving?: boolean;
handleLocalizationSelection: Effect<string>;
handleSave: Effect<void>;
handleCachePurge: Effect<void>;
permissions: { canUpdate?: boolean };
locale: {
defaultLocale: string;
restLocale: string[];
};
currentLocale?: string;
}

export const NavigationHeader: React.FC<Props> = ({
activeNavigation,
structureHasErrors,
structureHasChanged,
isSaving,
handleLocalizationSelection,
handleSave,
handleCachePurge,
permissions = {},
locale,
currentLocale,
}) => {
const { formatMessage } = useIntl();

const configQuery = useConfig();

const hasLocalizations = !!locale.restLocale?.length;
const hasCache = !!configQuery.data?.isCacheEnabled;

const { canUpdate } = permissions;

const { isMobile } = usePluginMediaQuery();

return (
<Layouts.Header
navigationAction={
<Link tag={NavLink} startIcon={<ArrowLeft />} to={`/plugins/${PLUGIN_ID}`}>
{formatMessage(getTrad('popup.navigation.manage.button.goBack'))}
</Link>
}
title={activeNavigation?.name ?? formatMessage(getTrad('header.title', 'UI Navigation'))}
subtitle={formatMessage(getTrad('header.description'))}
primaryAction={
<Flex direction="row" gap={2}>
{hasLocalizations && (
<SingleSelect
type="select"
placeholder={formatMessage(
getTrad('pages.main.header.localization.select.placeholder')
)}
name="navigationLocalizationSelect"
onChange={handleLocalizationSelection}
value={currentLocale}
size="S"
>
{[locale.defaultLocale, ...locale.restLocale].map((code) => (
<SingleSelectOption key={code} value={code}>
{code}
</SingleSelectOption>
))}
</SingleSelect>
)}
{canUpdate && hasCache && (
<Button
onClick={handleCachePurge}
startIcon={<Feather />}
variant="danger-light"
type="button"
size="S"
>
{formatMessage(getTrad('submit.cta.cache.purge'))}
</Button>
)}
{canUpdate && (
<Button
onClick={handleSave}
startIcon={submitIcon}
disabled={structureHasErrors || !structureHasChanged || isSaving}
type="submit"
size="S"
>
{formatMessage(getTrad('submit.cta.save'))}
</Button>
)}
</Flex>
}
secondaryAction={
!isMobile &&
activeNavigation && (
<Tag icon={<Information aria-hidden={true} />}>
{formatMessage(getTrad('header.meta'), {
id: activeNavigation.documentId,
key: activeNavigation.slug,
})}
</Tag>
)
}
/>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
NavigationItemTypeSchema,
StrapiContentTypeItemSchema,
} from '../../../../../api/validators';
import { extractRelatedItemLabel } from '../../../../HomePage/utils';
import { extractRelatedItemLabel } from '../../../utils';
import { type NavigationItemFormSchema } from './form';

interface GenerateUiRouterKeyInput {
Expand Down
1 change: 1 addition & 0 deletions admin/src/pages/DetailPage/components/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './NavigationHeader';
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
export * from './useAPI';
export * from '../../../hooks';
export * from './useI18nCopyNavigationItemsModal';
export * from './useInvalidateQueries';
export * from './useLocales';
export * from './useNavigationItemPopup';
export * from './useSearch';
export * from './useSettingsPermissions';
export * from './usePluginMediaQuery';
Loading