|
@@ -1,18 +1,20 @@
|
|
|
-import React, {
|
|
|
|
|
- useEffect, useState, useMemo, useCallback,
|
|
|
|
|
-} from 'react';
|
|
|
|
|
-
|
|
|
|
|
-import path from 'path';
|
|
|
|
|
-
|
|
|
|
|
-
|
|
|
|
|
|
|
+import type React from 'react';
|
|
|
|
|
+import { useCallback, useEffect, useMemo, useState } from 'react';
|
|
|
import { Origin } from '@growi/core';
|
|
import { Origin } from '@growi/core';
|
|
|
import { pagePathUtils, pathUtils } from '@growi/core/dist/utils';
|
|
import { pagePathUtils, pathUtils } from '@growi/core/dist/utils';
|
|
|
import { normalizePath } from '@growi/core/dist/utils/path-utils';
|
|
import { normalizePath } from '@growi/core/dist/utils/path-utils';
|
|
|
import { format } from 'date-fns/format';
|
|
import { format } from 'date-fns/format';
|
|
|
import { useAtomValue } from 'jotai';
|
|
import { useAtomValue } from 'jotai';
|
|
|
import { useTranslation } from 'next-i18next';
|
|
import { useTranslation } from 'next-i18next';
|
|
|
|
|
+import path from 'path';
|
|
|
import {
|
|
import {
|
|
|
- Modal, ModalHeader, ModalBody, UncontrolledButtonDropdown, DropdownToggle, DropdownMenu, DropdownItem,
|
|
|
|
|
|
|
+ DropdownItem,
|
|
|
|
|
+ DropdownMenu,
|
|
|
|
|
+ DropdownToggle,
|
|
|
|
|
+ Modal,
|
|
|
|
|
+ ModalBody,
|
|
|
|
|
+ ModalHeader,
|
|
|
|
|
+ UncontrolledButtonDropdown,
|
|
|
} from 'reactstrap';
|
|
} from 'reactstrap';
|
|
|
import { debounce } from 'throttle-debounce';
|
|
import { debounce } from 'throttle-debounce';
|
|
|
|
|
|
|
@@ -21,15 +23,16 @@ import { useCreatePage } from '~/client/services/create-page/use-create-page';
|
|
|
import { useToastrOnError } from '~/client/services/use-toastr-on-error';
|
|
import { useToastrOnError } from '~/client/services/use-toastr-on-error';
|
|
|
import { useCurrentUser } from '~/states/global';
|
|
import { useCurrentUser } from '~/states/global';
|
|
|
import { isSearchServiceReachableAtom } from '~/states/server-configurations';
|
|
import { isSearchServiceReachableAtom } from '~/states/server-configurations';
|
|
|
-import { usePageCreateModalStatus, usePageCreateModalActions } from '~/states/ui/modal/page-create';
|
|
|
|
|
|
|
+import {
|
|
|
|
|
+ usePageCreateModalActions,
|
|
|
|
|
+ usePageCreateModalStatus,
|
|
|
|
|
+} from '~/states/ui/modal/page-create';
|
|
|
|
|
|
|
|
import PagePathAutoComplete from './PagePathAutoComplete';
|
|
import PagePathAutoComplete from './PagePathAutoComplete';
|
|
|
|
|
|
|
|
import styles from './PageCreateModal.module.scss';
|
|
import styles from './PageCreateModal.module.scss';
|
|
|
|
|
|
|
|
-const {
|
|
|
|
|
- isCreatablePage, isUsersHomepage,
|
|
|
|
|
-} = pagePathUtils;
|
|
|
|
|
|
|
+const { isCreatablePage, isUsersHomepage } = pagePathUtils;
|
|
|
|
|
|
|
|
const PageCreateModal: React.FC = () => {
|
|
const PageCreateModal: React.FC = () => {
|
|
|
const { t } = useTranslation();
|
|
const { t } = useTranslation();
|
|
@@ -45,19 +48,34 @@ const PageCreateModal: React.FC = () => {
|
|
|
const isReachable = useAtomValue(isSearchServiceReachableAtom);
|
|
const isReachable = useAtomValue(isSearchServiceReachableAtom);
|
|
|
|
|
|
|
|
// Memoize computed values
|
|
// Memoize computed values
|
|
|
- const userHomepagePath = useMemo(() => pagePathUtils.userHomepagePath(currentUser), [currentUser]);
|
|
|
|
|
- const isCreatable = useMemo(() => isCreatablePage(pathname) || isUsersHomepage(pathname), [pathname]);
|
|
|
|
|
- const pageNameInputInitialValue = useMemo(() => (isCreatable ? pathUtils.addTrailingSlash(pathname) : '/'), [isCreatable, pathname]);
|
|
|
|
|
|
|
+ const userHomepagePath = useMemo(
|
|
|
|
|
+ () => pagePathUtils.userHomepagePath(currentUser),
|
|
|
|
|
+ [currentUser],
|
|
|
|
|
+ );
|
|
|
|
|
+ const isCreatable = useMemo(
|
|
|
|
|
+ () => isCreatablePage(pathname) || isUsersHomepage(pathname),
|
|
|
|
|
+ [pathname],
|
|
|
|
|
+ );
|
|
|
|
|
+ const pageNameInputInitialValue = useMemo(
|
|
|
|
|
+ () => (isCreatable ? pathUtils.addTrailingSlash(pathname) : '/'),
|
|
|
|
|
+ [isCreatable, pathname],
|
|
|
|
|
+ );
|
|
|
const now = useMemo(() => format(new Date(), 'yyyy/MM/dd'), []);
|
|
const now = useMemo(() => format(new Date(), 'yyyy/MM/dd'), []);
|
|
|
const todaysParentPath = useMemo(
|
|
const todaysParentPath = useMemo(
|
|
|
- () => [userHomepagePath, t('create_page_dropdown.todays.memo', { ns: 'commons' }), now].join('/'),
|
|
|
|
|
|
|
+ () =>
|
|
|
|
|
+ [
|
|
|
|
|
+ userHomepagePath,
|
|
|
|
|
+ t('create_page_dropdown.todays.memo', { ns: 'commons' }),
|
|
|
|
|
+ now,
|
|
|
|
|
+ ].join('/'),
|
|
|
[userHomepagePath, t, now],
|
|
[userHomepagePath, t, now],
|
|
|
);
|
|
);
|
|
|
|
|
|
|
|
const [todayInput, setTodayInput] = useState('');
|
|
const [todayInput, setTodayInput] = useState('');
|
|
|
const [pageNameInput, setPageNameInput] = useState(pageNameInputInitialValue);
|
|
const [pageNameInput, setPageNameInput] = useState(pageNameInputInitialValue);
|
|
|
const [template, setTemplate] = useState(null);
|
|
const [template, setTemplate] = useState(null);
|
|
|
- const [isMatchedWithUserHomepagePath, setIsMatchedWithUserHomepagePath] = useState(false);
|
|
|
|
|
|
|
+ const [isMatchedWithUserHomepagePath, setIsMatchedWithUserHomepagePath] =
|
|
|
|
|
+ useState(false);
|
|
|
|
|
|
|
|
const checkIsUsersHomepageDebounce = useMemo(() => {
|
|
const checkIsUsersHomepageDebounce = useMemo(() => {
|
|
|
return debounce(1000, (input: string) => {
|
|
return debounce(1000, (input: string) => {
|
|
@@ -96,11 +114,14 @@ const PageCreateModal: React.FC = () => {
|
|
|
/**
|
|
/**
|
|
|
* access today page
|
|
* access today page
|
|
|
*/
|
|
*/
|
|
|
- const createTodayPage = useCallback(async() => {
|
|
|
|
|
|
|
+ const createTodayPage = useCallback(async () => {
|
|
|
const joinedPath = [todaysParentPath, todayInput].join('/');
|
|
const joinedPath = [todaysParentPath, todayInput].join('/');
|
|
|
return create(
|
|
return create(
|
|
|
{
|
|
{
|
|
|
- path: joinedPath, parentPath: todaysParentPath, wip: true, origin: Origin.View,
|
|
|
|
|
|
|
+ path: joinedPath,
|
|
|
|
|
+ parentPath: todaysParentPath,
|
|
|
|
|
+ wip: true,
|
|
|
|
|
+ origin: Origin.View,
|
|
|
},
|
|
},
|
|
|
{ onTerminated: closeCreateModal },
|
|
{ onTerminated: closeCreateModal },
|
|
|
);
|
|
);
|
|
@@ -109,7 +130,7 @@ const PageCreateModal: React.FC = () => {
|
|
|
/**
|
|
/**
|
|
|
* access input page
|
|
* access input page
|
|
|
*/
|
|
*/
|
|
|
- const createInputPage = useCallback(async() => {
|
|
|
|
|
|
|
+ const createInputPage = useCallback(async () => {
|
|
|
const targetPath = normalizePath(pageNameInput);
|
|
const targetPath = normalizePath(pageNameInput);
|
|
|
const parentPath = path.dirname(targetPath);
|
|
const parentPath = path.dirname(targetPath);
|
|
|
|
|
|
|
@@ -127,9 +148,8 @@ const PageCreateModal: React.FC = () => {
|
|
|
/**
|
|
/**
|
|
|
* access template page
|
|
* access template page
|
|
|
*/
|
|
*/
|
|
|
- const createTemplatePage = useCallback(async() => {
|
|
|
|
|
-
|
|
|
|
|
- const label = (template === 'children') ? '_template' : '__template';
|
|
|
|
|
|
|
+ const createTemplatePage = useCallback(async () => {
|
|
|
|
|
+ const label = template === 'children' ? '_template' : '__template';
|
|
|
|
|
|
|
|
await createTemplate?.(label);
|
|
await createTemplate?.(label);
|
|
|
closeCreateModal();
|
|
closeCreateModal();
|
|
@@ -146,22 +166,28 @@ const PageCreateModal: React.FC = () => {
|
|
|
return (
|
|
return (
|
|
|
<div className="row">
|
|
<div className="row">
|
|
|
<fieldset className="col-12 mb-4">
|
|
<fieldset className="col-12 mb-4">
|
|
|
- <h3 className="pb-2">{t('create_page_dropdown.todays.desc', { ns: 'commons' })}</h3>
|
|
|
|
|
|
|
+ <h3 className="pb-2">
|
|
|
|
|
+ {t('create_page_dropdown.todays.desc', { ns: 'commons' })}
|
|
|
|
|
+ </h3>
|
|
|
|
|
|
|
|
<div className="d-sm-flex align-items-center justify-items-between">
|
|
<div className="d-sm-flex align-items-center justify-items-between">
|
|
|
-
|
|
|
|
|
<div className="d-flex align-items-center flex-fill flex-wrap flex-lg-nowrap">
|
|
<div className="d-flex align-items-center flex-fill flex-wrap flex-lg-nowrap">
|
|
|
<div className="d-flex align-items-center text-nowrap">
|
|
<div className="d-flex align-items-center text-nowrap">
|
|
|
<span>{todaysParentPath}/</span>
|
|
<span>{todaysParentPath}/</span>
|
|
|
</div>
|
|
</div>
|
|
|
- <form className="mt-1 mt-lg-0 ms-lg-2 w-100" onSubmit={(e) => { transitBySubmitEvent(e, createTodaysMemoWithToastr) }}>
|
|
|
|
|
|
|
+ <form
|
|
|
|
|
+ className="mt-1 mt-lg-0 ms-lg-2 w-100"
|
|
|
|
|
+ onSubmit={(e) => {
|
|
|
|
|
+ transitBySubmitEvent(e, createTodaysMemoWithToastr);
|
|
|
|
|
+ }}
|
|
|
|
|
+ >
|
|
|
<input
|
|
<input
|
|
|
type="text"
|
|
type="text"
|
|
|
className="page-today-input2 form-control w-100"
|
|
className="page-today-input2 form-control w-100"
|
|
|
id="page-today-input2"
|
|
id="page-today-input2"
|
|
|
placeholder={t('Input page name (optional)')}
|
|
placeholder={t('Input page name (optional)')}
|
|
|
value={todayInput}
|
|
value={todayInput}
|
|
|
- onChange={e => onChangeTodayInputHandler(e.target.value)}
|
|
|
|
|
|
|
+ onChange={(e) => onChangeTodayInputHandler(e.target.value)}
|
|
|
/>
|
|
/>
|
|
|
</form>
|
|
</form>
|
|
|
</div>
|
|
</div>
|
|
@@ -173,16 +199,23 @@ const PageCreateModal: React.FC = () => {
|
|
|
className="grw-btn-create-page btn btn-outline-primary rounded-pill text-nowrap ms-3"
|
|
className="grw-btn-create-page btn btn-outline-primary rounded-pill text-nowrap ms-3"
|
|
|
onClick={createTodaysMemoWithToastr}
|
|
onClick={createTodaysMemoWithToastr}
|
|
|
>
|
|
>
|
|
|
- <span className="material-symbols-outlined">description</span>{t('Create')}
|
|
|
|
|
|
|
+ <span className="material-symbols-outlined">description</span>
|
|
|
|
|
+ {t('Create')}
|
|
|
</button>
|
|
</button>
|
|
|
</div>
|
|
</div>
|
|
|
-
|
|
|
|
|
</div>
|
|
</div>
|
|
|
-
|
|
|
|
|
</fieldset>
|
|
</fieldset>
|
|
|
</div>
|
|
</div>
|
|
|
);
|
|
);
|
|
|
- }, [isOpened, todaysParentPath, todayInput, t, onChangeTodayInputHandler, transitBySubmitEvent, createTodaysMemoWithToastr]);
|
|
|
|
|
|
|
+ }, [
|
|
|
|
|
+ isOpened,
|
|
|
|
|
+ todaysParentPath,
|
|
|
|
|
+ todayInput,
|
|
|
|
|
+ t,
|
|
|
|
|
+ onChangeTodayInputHandler,
|
|
|
|
|
+ transitBySubmitEvent,
|
|
|
|
|
+ createTodaysMemoWithToastr,
|
|
|
|
|
+ ]);
|
|
|
|
|
|
|
|
const renderInputPageForm = useMemo(() => {
|
|
const renderInputPageForm = useMemo(() => {
|
|
|
if (!isOpened) {
|
|
if (!isOpened) {
|
|
@@ -195,28 +228,30 @@ const PageCreateModal: React.FC = () => {
|
|
|
|
|
|
|
|
<div className="d-sm-flex align-items-center justify-items-between">
|
|
<div className="d-sm-flex align-items-center justify-items-between">
|
|
|
<div className="flex-fill">
|
|
<div className="flex-fill">
|
|
|
- {isReachable
|
|
|
|
|
- ? (
|
|
|
|
|
- <PagePathAutoComplete
|
|
|
|
|
- initializedPath={pageNameInputInitialValue}
|
|
|
|
|
- addTrailingSlash
|
|
|
|
|
- onSubmit={createInputPageWithToastr}
|
|
|
|
|
- onInputChange={value => setPageNameInput(value)}
|
|
|
|
|
- autoFocus
|
|
|
|
|
|
|
+ {isReachable ? (
|
|
|
|
|
+ <PagePathAutoComplete
|
|
|
|
|
+ initializedPath={pageNameInputInitialValue}
|
|
|
|
|
+ addTrailingSlash
|
|
|
|
|
+ onSubmit={createInputPageWithToastr}
|
|
|
|
|
+ onInputChange={(value) => setPageNameInput(value)}
|
|
|
|
|
+ autoFocus
|
|
|
|
|
+ />
|
|
|
|
|
+ ) : (
|
|
|
|
|
+ <form
|
|
|
|
|
+ onSubmit={(e) => {
|
|
|
|
|
+ transitBySubmitEvent(e, createInputPageWithToastr);
|
|
|
|
|
+ }}
|
|
|
|
|
+ >
|
|
|
|
|
+ <input
|
|
|
|
|
+ type="text"
|
|
|
|
|
+ value={pageNameInput}
|
|
|
|
|
+ className="form-control flex-fill"
|
|
|
|
|
+ placeholder={t('Input page name')}
|
|
|
|
|
+ onChange={(e) => setPageNameInput(e.target.value)}
|
|
|
|
|
+ required
|
|
|
/>
|
|
/>
|
|
|
- )
|
|
|
|
|
- : (
|
|
|
|
|
- <form onSubmit={(e) => { transitBySubmitEvent(e, createInputPageWithToastr) }}>
|
|
|
|
|
- <input
|
|
|
|
|
- type="text"
|
|
|
|
|
- value={pageNameInput}
|
|
|
|
|
- className="form-control flex-fill"
|
|
|
|
|
- placeholder={t('Input page name')}
|
|
|
|
|
- onChange={e => setPageNameInput(e.target.value)}
|
|
|
|
|
- required
|
|
|
|
|
- />
|
|
|
|
|
- </form>
|
|
|
|
|
- )}
|
|
|
|
|
|
|
+ </form>
|
|
|
|
|
+ )}
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
<div className="d-flex justify-content-end mt-1 mt-sm-0">
|
|
<div className="d-flex justify-content-end mt-1 mt-sm-0">
|
|
@@ -227,19 +262,29 @@ const PageCreateModal: React.FC = () => {
|
|
|
onClick={createInputPageWithToastr}
|
|
onClick={createInputPageWithToastr}
|
|
|
disabled={isMatchedWithUserHomepagePath}
|
|
disabled={isMatchedWithUserHomepagePath}
|
|
|
>
|
|
>
|
|
|
- <span className="material-symbols-outlined">description</span>{t('Create')}
|
|
|
|
|
|
|
+ <span className="material-symbols-outlined">description</span>
|
|
|
|
|
+ {t('Create')}
|
|
|
</button>
|
|
</button>
|
|
|
</div>
|
|
</div>
|
|
|
-
|
|
|
|
|
</div>
|
|
</div>
|
|
|
- { isMatchedWithUserHomepagePath && (
|
|
|
|
|
- <p className="text-danger mt-2">Error: Cannot create page under /user page directory.</p>
|
|
|
|
|
- ) }
|
|
|
|
|
-
|
|
|
|
|
|
|
+ {isMatchedWithUserHomepagePath && (
|
|
|
|
|
+ <p className="text-danger mt-2">
|
|
|
|
|
+ Error: Cannot create page under /user page directory.
|
|
|
|
|
+ </p>
|
|
|
|
|
+ )}
|
|
|
</fieldset>
|
|
</fieldset>
|
|
|
</div>
|
|
</div>
|
|
|
);
|
|
);
|
|
|
- }, [isOpened, isReachable, pageNameInputInitialValue, createInputPageWithToastr, pageNameInput, isMatchedWithUserHomepagePath, t, transitBySubmitEvent]);
|
|
|
|
|
|
|
+ }, [
|
|
|
|
|
+ isOpened,
|
|
|
|
|
+ isReachable,
|
|
|
|
|
+ pageNameInputInitialValue,
|
|
|
|
|
+ createInputPageWithToastr,
|
|
|
|
|
+ pageNameInput,
|
|
|
|
|
+ isMatchedWithUserHomepagePath,
|
|
|
|
|
+ t,
|
|
|
|
|
+ transitBySubmitEvent,
|
|
|
|
|
+ ]);
|
|
|
|
|
|
|
|
const renderTemplatePageForm = useMemo(() => {
|
|
const renderTemplatePageForm = useMemo(() => {
|
|
|
if (!isOpened) {
|
|
if (!isOpened) {
|
|
@@ -248,28 +293,42 @@ const PageCreateModal: React.FC = () => {
|
|
|
return (
|
|
return (
|
|
|
<div className="row">
|
|
<div className="row">
|
|
|
<fieldset className="col-12">
|
|
<fieldset className="col-12">
|
|
|
-
|
|
|
|
|
<h3 className="pb-2">
|
|
<h3 className="pb-2">
|
|
|
- {t('template.modal_label.Create template under')}<br />
|
|
|
|
|
- <code className="h6" data-testid="grw-page-create-modal-path-name">{pathname}</code>
|
|
|
|
|
|
|
+ {t('template.modal_label.Create template under')}
|
|
|
|
|
+ <br />
|
|
|
|
|
+ <code className="h6" data-testid="grw-page-create-modal-path-name">
|
|
|
|
|
+ {pathname}
|
|
|
|
|
+ </code>
|
|
|
</h3>
|
|
</h3>
|
|
|
|
|
|
|
|
<div className="d-sm-flex align-items-center justify-items-between">
|
|
<div className="d-sm-flex align-items-center justify-items-between">
|
|
|
-
|
|
|
|
|
- <UncontrolledButtonDropdown id="dd-template-type" className="flex-fill text-center">
|
|
|
|
|
|
|
+ <UncontrolledButtonDropdown
|
|
|
|
|
+ id="dd-template-type"
|
|
|
|
|
+ className="flex-fill text-center"
|
|
|
|
|
+ >
|
|
|
<DropdownToggle id="template-type" caret>
|
|
<DropdownToggle id="template-type" caret>
|
|
|
{template == null && t('template.option_label.select')}
|
|
{template == null && t('template.option_label.select')}
|
|
|
{template === 'children' && t('template.children.label')}
|
|
{template === 'children' && t('template.children.label')}
|
|
|
{template === 'descendants' && t('template.descendants.label')}
|
|
{template === 'descendants' && t('template.descendants.label')}
|
|
|
</DropdownToggle>
|
|
</DropdownToggle>
|
|
|
<DropdownMenu>
|
|
<DropdownMenu>
|
|
|
- <DropdownItem onClick={() => onChangeTemplateHandler('children')}>
|
|
|
|
|
- {t('template.children.label')} (_template)<br className="d-block d-md-none" />
|
|
|
|
|
- <small className="text-muted text-wrap">- {t('template.children.desc')}</small>
|
|
|
|
|
|
|
+ <DropdownItem
|
|
|
|
|
+ onClick={() => onChangeTemplateHandler('children')}
|
|
|
|
|
+ >
|
|
|
|
|
+ {t('template.children.label')} (_template)
|
|
|
|
|
+ <br className="d-block d-md-none" />
|
|
|
|
|
+ <small className="text-muted text-wrap">
|
|
|
|
|
+ - {t('template.children.desc')}
|
|
|
|
|
+ </small>
|
|
|
</DropdownItem>
|
|
</DropdownItem>
|
|
|
- <DropdownItem onClick={() => onChangeTemplateHandler('descendants')}>
|
|
|
|
|
- {t('template.descendants.label')} (__template) <br className="d-block d-md-none" />
|
|
|
|
|
- <small className="text-muted">- {t('template.descendants.desc')}</small>
|
|
|
|
|
|
|
+ <DropdownItem
|
|
|
|
|
+ onClick={() => onChangeTemplateHandler('descendants')}
|
|
|
|
|
+ >
|
|
|
|
|
+ {t('template.descendants.label')} (__template){' '}
|
|
|
|
|
+ <br className="d-block d-md-none" />
|
|
|
|
|
+ <small className="text-muted">
|
|
|
|
|
+ - {t('template.descendants.desc')}
|
|
|
|
|
+ </small>
|
|
|
</DropdownItem>
|
|
</DropdownItem>
|
|
|
</DropdownMenu>
|
|
</DropdownMenu>
|
|
|
</UncontrolledButtonDropdown>
|
|
</UncontrolledButtonDropdown>
|
|
@@ -282,16 +341,22 @@ const PageCreateModal: React.FC = () => {
|
|
|
onClick={createTemplateWithToastr}
|
|
onClick={createTemplateWithToastr}
|
|
|
disabled={template == null}
|
|
disabled={template == null}
|
|
|
>
|
|
>
|
|
|
- <span className="material-symbols-outlined">description</span>{t('Edit')}
|
|
|
|
|
|
|
+ <span className="material-symbols-outlined">description</span>
|
|
|
|
|
+ {t('Edit')}
|
|
|
</button>
|
|
</button>
|
|
|
</div>
|
|
</div>
|
|
|
-
|
|
|
|
|
</div>
|
|
</div>
|
|
|
-
|
|
|
|
|
</fieldset>
|
|
</fieldset>
|
|
|
</div>
|
|
</div>
|
|
|
);
|
|
);
|
|
|
- }, [isOpened, pathname, template, onChangeTemplateHandler, createTemplateWithToastr, t]);
|
|
|
|
|
|
|
+ }, [
|
|
|
|
|
+ isOpened,
|
|
|
|
|
+ pathname,
|
|
|
|
|
+ template,
|
|
|
|
|
+ onChangeTemplateHandler,
|
|
|
|
|
+ createTemplateWithToastr,
|
|
|
|
|
+ t,
|
|
|
|
|
+ ]);
|
|
|
|
|
|
|
|
return (
|
|
return (
|
|
|
<Modal
|
|
<Modal
|
|
@@ -311,9 +376,7 @@ const PageCreateModal: React.FC = () => {
|
|
|
{renderTemplatePageForm}
|
|
{renderTemplatePageForm}
|
|
|
</ModalBody>
|
|
</ModalBody>
|
|
|
</Modal>
|
|
</Modal>
|
|
|
-
|
|
|
|
|
);
|
|
);
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
-
|
|
|
|
|
export default PageCreateModal;
|
|
export default PageCreateModal;
|