Move template management to settings (#5811)

This commit is contained in:
Tom Moor
2023-09-10 15:46:12 -04:00
committed by GitHub
parent ac068c0c07
commit 0856f5f6ae
32 changed files with 432 additions and 267 deletions

View File

@@ -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"

View File

@@ -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

View File

@@ -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("Couldnt create the document, try again?"), {
type: "error",

View File

@@ -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