Move template management to settings (#5811)
This commit is contained in:
@@ -29,7 +29,7 @@ const EmojiPicker = React.lazy(() => import("~/components/EmojiPicker"));
|
||||
type Props = {
|
||||
/** ID of the associated document */
|
||||
documentId: string;
|
||||
/** Document to display */
|
||||
/** Title to display */
|
||||
title: string;
|
||||
/** Emoji to display */
|
||||
emoji?: string | null;
|
||||
@@ -247,7 +247,7 @@ const DocumentTitle = React.forwardRef(function _DocumentTitle(
|
||||
value={title}
|
||||
$emojiPickerIsOpen={emojiPickerIsOpen}
|
||||
$containsEmoji={!!emoji}
|
||||
autoFocus={!document.title}
|
||||
autoFocus={!title}
|
||||
maxLength={DocumentValidation.maxTitleLength}
|
||||
readOnly={readOnly}
|
||||
dir="auto"
|
||||
|
||||
@@ -26,6 +26,7 @@ import EmojiIcon from "~/components/Icons/EmojiIcon";
|
||||
import Star from "~/components/Star";
|
||||
import Tooltip from "~/components/Tooltip";
|
||||
import { publishDocument } from "~/actions/definitions/documents";
|
||||
import { navigateToTemplateSettings } from "~/actions/definitions/navigation";
|
||||
import { restoreRevision } from "~/actions/definitions/revisions";
|
||||
import useActionContext from "~/hooks/useActionContext";
|
||||
import useMobile from "~/hooks/useMobile";
|
||||
@@ -36,7 +37,7 @@ import NewChildDocumentMenu from "~/menus/NewChildDocumentMenu";
|
||||
import TableOfContentsMenu from "~/menus/TableOfContentsMenu";
|
||||
import TemplatesMenu from "~/menus/TemplatesMenu";
|
||||
import { metaDisplay } from "~/utils/keyboard";
|
||||
import { newDocumentPath, documentEditPath } from "~/utils/routeHelpers";
|
||||
import { documentEditPath } from "~/utils/routeHelpers";
|
||||
import ObservingBanner from "./ObservingBanner";
|
||||
import PublicBreadcrumb from "./PublicBreadcrumb";
|
||||
import ShareButton from "./ShareButton";
|
||||
@@ -243,31 +244,43 @@ function DocumentHeader({
|
||||
{!isEditing &&
|
||||
!isDeleted &&
|
||||
!isRevision &&
|
||||
(!isMobile || !isTemplate) &&
|
||||
!isTemplate &&
|
||||
!isMobile &&
|
||||
document.collectionId && (
|
||||
<Action>
|
||||
<ShareButton document={document} />
|
||||
</Action>
|
||||
)}
|
||||
{isEditing && (
|
||||
<>
|
||||
<Action>
|
||||
<Tooltip
|
||||
tooltip={t("Save")}
|
||||
shortcut={`${metaDisplay}+enter`}
|
||||
delay={500}
|
||||
placement="bottom"
|
||||
<Action>
|
||||
<Tooltip
|
||||
tooltip={t("Save")}
|
||||
shortcut={`${metaDisplay}+enter`}
|
||||
delay={500}
|
||||
placement="bottom"
|
||||
>
|
||||
<Button
|
||||
onClick={handleSave}
|
||||
disabled={savingIsDisabled}
|
||||
neutral={isDraft}
|
||||
>
|
||||
<Button
|
||||
onClick={handleSave}
|
||||
disabled={savingIsDisabled}
|
||||
neutral={isDraft}
|
||||
>
|
||||
{isDraft ? t("Save draft") : t("Done editing")}
|
||||
</Button>
|
||||
</Tooltip>
|
||||
</Action>
|
||||
</>
|
||||
{isDraft ? t("Save draft") : t("Done editing")}
|
||||
</Button>
|
||||
</Tooltip>
|
||||
</Action>
|
||||
)}
|
||||
{isTemplate && (
|
||||
<Action>
|
||||
<Button
|
||||
context={context}
|
||||
action={navigateToTemplateSettings}
|
||||
disabled={savingIsDisabled}
|
||||
neutral={isDraft}
|
||||
hideIcon
|
||||
>
|
||||
{t("Done editing")}
|
||||
</Button>
|
||||
</Action>
|
||||
)}
|
||||
{can.update &&
|
||||
!isEditing &&
|
||||
@@ -296,23 +309,6 @@ function DocumentHeader({
|
||||
/>
|
||||
</Action>
|
||||
)}
|
||||
{can.update &&
|
||||
!isEditing &&
|
||||
isTemplate &&
|
||||
!isDraft &&
|
||||
!isRevision && (
|
||||
<Action>
|
||||
<Button
|
||||
icon={<PlusIcon />}
|
||||
as={Link}
|
||||
to={newDocumentPath(document.collectionId, {
|
||||
templateId: document.id,
|
||||
})}
|
||||
>
|
||||
{t("New from template")}
|
||||
</Button>
|
||||
</Action>
|
||||
)}
|
||||
{revision && revision.createdAt !== document.updatedAt && (
|
||||
<Action>
|
||||
<Tooltip
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { observer } from "mobx-react";
|
||||
import queryString from "query-string";
|
||||
import * as React from "react";
|
||||
import { useEffect } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
@@ -7,23 +6,31 @@ import { useHistory, useLocation, useRouteMatch } from "react-router-dom";
|
||||
import CenteredContent from "~/components/CenteredContent";
|
||||
import Flex from "~/components/Flex";
|
||||
import PlaceholderDocument from "~/components/PlaceholderDocument";
|
||||
import useCurrentUser from "~/hooks/useCurrentUser";
|
||||
import useQuery from "~/hooks/useQuery";
|
||||
import useStores from "~/hooks/useStores";
|
||||
import useToasts from "~/hooks/useToasts";
|
||||
import { documentEditPath } from "~/utils/routeHelpers";
|
||||
import { documentEditPath, documentPath } from "~/utils/routeHelpers";
|
||||
|
||||
function DocumentNew() {
|
||||
type Props = {
|
||||
// If true, the document will be created as a template.
|
||||
template?: boolean;
|
||||
};
|
||||
|
||||
function DocumentNew({ template }: Props) {
|
||||
const history = useHistory();
|
||||
const location = useLocation();
|
||||
const query = useQuery();
|
||||
const user = useCurrentUser();
|
||||
const match = useRouteMatch<{ id?: string }>();
|
||||
const { t } = useTranslation();
|
||||
const { documents, collections } = useStores();
|
||||
const { showToast } = useToasts();
|
||||
const id = match.params.id || "";
|
||||
const id = match.params.id || query.get("collectionId");
|
||||
|
||||
useEffect(() => {
|
||||
async function createDocument() {
|
||||
const params = queryString.parse(location.search);
|
||||
const parentDocumentId = params.parentDocumentId?.toString();
|
||||
const parentDocumentId = query.get("parentDocumentId") ?? undefined;
|
||||
const parentDocument = parentDocumentId
|
||||
? documents.get(parentDocumentId)
|
||||
: undefined;
|
||||
@@ -37,12 +44,17 @@ function DocumentNew() {
|
||||
collectionId: collection?.id,
|
||||
parentDocumentId,
|
||||
fullWidth: parentDocument?.fullWidth,
|
||||
templateId: params.templateId?.toString(),
|
||||
template: params.template === "true" ? true : false,
|
||||
templateId: query.get("templateId") ?? undefined,
|
||||
template,
|
||||
title: "",
|
||||
text: "",
|
||||
});
|
||||
history.replace(documentEditPath(document), location.state);
|
||||
history.replace(
|
||||
template || !user.separateEditMode
|
||||
? documentPath(document)
|
||||
: documentEditPath(document),
|
||||
location.state
|
||||
);
|
||||
} catch (err) {
|
||||
showToast(t("Couldn’t create the document, try again?"), {
|
||||
type: "error",
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { observer } from "mobx-react";
|
||||
import { ShapesIcon } from "outline-icons";
|
||||
import queryString from "query-string";
|
||||
import * as React from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { RouteComponentProps } from "react-router-dom";
|
||||
import { Trans, useTranslation } from "react-i18next";
|
||||
import { Action } from "~/components/Actions";
|
||||
import Empty from "~/components/Empty";
|
||||
import Heading from "~/components/Heading";
|
||||
@@ -10,18 +10,18 @@ import PaginatedDocumentList from "~/components/PaginatedDocumentList";
|
||||
import Scene from "~/components/Scene";
|
||||
import Tab from "~/components/Tab";
|
||||
import Tabs from "~/components/Tabs";
|
||||
import useCurrentTeam from "~/hooks/useCurrentTeam";
|
||||
import usePolicy from "~/hooks/usePolicy";
|
||||
import Text from "~/components/Text";
|
||||
import useQuery from "~/hooks/useQuery";
|
||||
import useStores from "~/hooks/useStores";
|
||||
import NewTemplateMenu from "~/menus/NewTemplateMenu";
|
||||
import { settingsPath } from "~/utils/routeHelpers";
|
||||
|
||||
function Templates(props: RouteComponentProps<{ sort: string }>) {
|
||||
function Templates() {
|
||||
const { documents } = useStores();
|
||||
const { t } = useTranslation();
|
||||
const team = useCurrentTeam();
|
||||
const param = useQuery();
|
||||
const { fetchTemplates, templates, templatesAlphabetical } = documents;
|
||||
const { sort } = props.match.params;
|
||||
const can = usePolicy(team);
|
||||
const sort = param.get("sort") || "recent";
|
||||
|
||||
return (
|
||||
<Scene
|
||||
@@ -34,26 +34,33 @@ function Templates(props: RouteComponentProps<{ sort: string }>) {
|
||||
}
|
||||
>
|
||||
<Heading>{t("Templates")}</Heading>
|
||||
<Text type="secondary">
|
||||
<Trans>
|
||||
You can create templates to help your team create consistent and
|
||||
accurate documentation.
|
||||
</Trans>
|
||||
</Text>
|
||||
|
||||
<PaginatedDocumentList
|
||||
heading={
|
||||
<Tabs>
|
||||
<Tab to="/templates" exact>
|
||||
<Tab to={settingsPath("templates")} exactQueryString>
|
||||
{t("Recently updated")}
|
||||
</Tab>
|
||||
<Tab to="/templates/alphabetical" exact>
|
||||
<Tab
|
||||
to={{
|
||||
pathname: settingsPath("templates"),
|
||||
search: queryString.stringify({
|
||||
sort: "alphabetical",
|
||||
}),
|
||||
}}
|
||||
exactQueryString
|
||||
>
|
||||
{t("Alphabetical")}
|
||||
</Tab>
|
||||
</Tabs>
|
||||
}
|
||||
empty={
|
||||
<Empty>
|
||||
{t("There are no templates just yet.")}{" "}
|
||||
{can.createDocument &&
|
||||
t(
|
||||
"You can create templates to help your team create consistent and accurate documentation."
|
||||
)}
|
||||
</Empty>
|
||||
}
|
||||
empty={<Empty>{t("There are no templates just yet.")}</Empty>}
|
||||
fetch={fetchTemplates}
|
||||
documents={sort === "alphabetical" ? templatesAlphabetical : templates}
|
||||
showCollection
|
||||
Reference in New Issue
Block a user