diff --git a/apps/designer/app/designer/features/sidebar-left/lib/header.tsx b/apps/designer/app/designer/features/sidebar-left/lib/header.tsx
index 5bfc9f0e31..48d5b9cdaf 100644
--- a/apps/designer/app/designer/features/sidebar-left/lib/header.tsx
+++ b/apps/designer/app/designer/features/sidebar-left/lib/header.tsx
@@ -1,4 +1,4 @@
-import { Flex, IconButton, Text } from "@webstudio-is/design-system";
+import { Flex, IconButton, Text, Tooltip } from "@webstudio-is/design-system";
import { Separator } from "@webstudio-is/design-system";
import { CrossIcon } from "@webstudio-is/icons";
@@ -24,7 +24,9 @@ export const Header = ({ title, suffix }: HeaderProps) => {
};
export const CloseButton = ({ onClick }: { onClick: () => void }) => (
-
-
-
+
+
+
+
+
);
diff --git a/apps/designer/app/designer/features/sidebar-left/panels/pages/pages.tsx b/apps/designer/app/designer/features/sidebar-left/panels/pages/pages.tsx
index e3c2b9e8e6..2ae389aaae 100644
--- a/apps/designer/app/designer/features/sidebar-left/panels/pages/pages.tsx
+++ b/apps/designer/app/designer/features/sidebar-left/panels/pages/pages.tsx
@@ -6,6 +6,7 @@ import {
type TreeItemRenderProps,
styled,
Flex,
+ Tooltip,
} from "@webstudio-is/design-system";
import { type Publish } from "~/shared/pubsub";
import {
@@ -119,13 +120,19 @@ const PagesPanel = ({
{isEditing ? (
) : (
- onEdit(props.itemData.id)}
+
-
-
+ onEdit(props.itemData.id)}
+ >
+
+
+
)}
)
@@ -153,13 +160,15 @@ const PagesPanel = ({
suffix={
<>
{onCreateNewPage && (
- onCreateNewPage()}
- aria-label="New Page"
- >
-
-
+
+ onCreateNewPage()}
+ aria-label="New page"
+ >
+
+
+
)}
{onClose && }
>
diff --git a/apps/designer/app/designer/features/sidebar-left/panels/pages/settings.tsx b/apps/designer/app/designer/features/sidebar-left/panels/pages/settings.tsx
index ec91f817ef..bf7cc891cf 100644
--- a/apps/designer/app/designer/features/sidebar-left/panels/pages/settings.tsx
+++ b/apps/designer/app/designer/features/sidebar-left/panels/pages/settings.tsx
@@ -3,10 +3,12 @@ import {
Button,
Box,
Label,
+ TextArea,
TextField,
styled,
Flex,
InputErrorsTooltip,
+ Tooltip,
} from "@webstudio-is/design-system";
import { useFetcher } from "@remix-run/react";
import { ChevronDoubleLeftIcon, TrashIcon } from "@webstudio-is/icons";
@@ -37,9 +39,20 @@ const Group = styled(Flex, {
defaultVariants: { direction: "column" },
});
-const fieldNames = ["name", "path"] as const;
+const fieldNames = ["name", "path", "title", "description"] as const;
type FieldName = typeof fieldNames[number];
-type EditablePage = Pick;
+type FormPage = Pick & {
+ description: string;
+};
+
+const toFormPage = (page: Page): FormPage => {
+ return {
+ name: page.name,
+ path: page.path,
+ title: page.title,
+ description: page.meta.description ?? "",
+ };
+};
const FormFields = ({
disabled,
@@ -48,10 +61,10 @@ const FormFields = ({
fieldErrors,
}: {
disabled?: boolean;
- values: EditablePage;
+ values: FormPage;
onChange: (event: {
field: Name;
- value: EditablePage[Name];
+ value: FormPage[Name];
}) => void;
fieldErrors: ZodError["formErrors"]["fieldErrors"];
}) => {
@@ -89,6 +102,36 @@ const FormFields = ({
/>
+
+
+
+ {
+ onChange({ field: "title", value: event.target.value });
+ }}
+ />
+
+
+
+
+
+
+
>
);
};
@@ -117,7 +160,12 @@ export const NewPageSettings = ({
fieldNames,
});
- const [values, setValues] = useState({ name: "", path: "" });
+ const [values, setValues] = useState({
+ name: "",
+ path: "",
+ title: "",
+ description: "",
+ });
const handleSubmit = () => {
fetcher.submit(values, {
@@ -158,9 +206,11 @@ const NewPageSettingsView = ({
title="New Page Settings"
suffix={
onClose && (
-
-
-
+
+
+
+
+
)
}
/>
@@ -183,7 +233,7 @@ const NewPageSettingsView = ({
);
};
-const toFormData = (page: Partial & { id: string }): FormData => {
+const toFormData = (page: Partial & { id: string }): FormData => {
const formData = new FormData();
for (const [key, value] of Object.entries(page)) {
// @todo: handle "meta"
@@ -212,10 +262,8 @@ export const PageSettings = ({
const [pages] = usePages();
const page = pages && projectUtils.pages.findById(pages, pageId);
- const [unsavedValues, setUnsavedValues] = useState>({});
- const [submittedValues, setSubmittedValues] = useState>(
- {}
- );
+ const [unsavedValues, setUnsavedValues] = useState>({});
+ const [submittedValues, setSubmittedValues] = useState>({});
const { fieldErrors, resetFieldError } = useFetcherErrors({
fetcher,
@@ -223,10 +271,7 @@ export const PageSettings = ({
});
const handleChange = useCallback(
- (event: {
- field: Name;
- value: EditablePage[Name];
- }) => {
+ (event: { field: Name; value: FormPage[Name] }) => {
resetFieldError(event.field);
setUnsavedValues((values) => ({ ...values, [event.field]: event.value }));
},
@@ -304,7 +349,7 @@ export const PageSettings = ({
onClose={onClose}
onDelete={hanldeDelete}
fieldErrors={fieldErrors}
- values={{ ...page, ...submittedValues, ...unsavedValues }}
+ values={{ ...toFormPage(page), ...submittedValues, ...unsavedValues }}
onChange={handleChange}
/>
);
@@ -327,18 +372,26 @@ const PageSettingsView = ({
suffix={
<>
{isHomePage === false && (
-
-
-
+
+
+
+
+
)}
{onClose && (
-
-
-
+
+
+
+
+
)}
>
}
diff --git a/apps/designer/app/designer/features/sidebar-left/sidebar-left.tsx b/apps/designer/app/designer/features/sidebar-left/sidebar-left.tsx
index 4d567a6151..2e6c0abb29 100644
--- a/apps/designer/app/designer/features/sidebar-left/sidebar-left.tsx
+++ b/apps/designer/app/designer/features/sidebar-left/sidebar-left.tsx
@@ -38,8 +38,6 @@ export const SidebarLeft = ({ publish }: SidebarLeftProps) => {
switch (panel) {
case "assets":
return isFeatureEnabled("assets");
- case "pages":
- return isFeatureEnabled("pages");
case "navigator":
return clientSettings.navigatorLayout === "docked";
}
diff --git a/apps/designer/app/routes/rest/pages.$projectId.ts b/apps/designer/app/routes/rest/pages.$projectId.ts
index 9b23253750..e5dc2c9d18 100644
--- a/apps/designer/app/routes/rest/pages.$projectId.ts
+++ b/apps/designer/app/routes/rest/pages.$projectId.ts
@@ -33,6 +33,8 @@ const commonPageInput = {
(path) => /^[-_a-z0-9\\/]*$/.test(path),
"Only a-z, 0-9, -, _ and / are allowed"
),
+ title: z.string().optional(),
+ description: z.string().optional(),
} as const;
const CreatePageInput = zfd.formData(commonPageInput);
@@ -45,7 +47,11 @@ const handlePut = async (
if (result.success === false) {
return { status: "error", errors: result.error.formErrors };
}
- const data = result.data;
+ const { description, ...restData } = result.data;
+ const data = {
+ ...restData,
+ meta: description !== undefined ? { description } : undefined,
+ };
const devBuild = await db.build.loadByProjectId(projectId, "dev");
@@ -69,6 +75,8 @@ const EditPageInput = zfd.formData({
id: z.string(),
name: z.optional(commonPageInput.name),
path: z.optional(commonPageInput.path),
+ title: z.optional(commonPageInput.title),
+ description: z.optional(commonPageInput.description),
});
const handlePost = async (
@@ -79,7 +87,11 @@ const handlePost = async (
if (result.success === false) {
return { status: "error", errors: result.error.formErrors };
}
- const { id, ...data } = result.data;
+ const { id, description, ...restData } = result.data;
+ const data = {
+ ...restData,
+ meta: description !== undefined ? { description } : undefined,
+ };
const devBuild = await db.build.loadByProjectId(projectId, "dev");
diff --git a/apps/designer/app/shared/feature-flags/flags.ts b/apps/designer/app/shared/feature-flags/flags.ts
index cdbf11dacf..12dd85ea82 100644
--- a/apps/designer/app/shared/feature-flags/flags.ts
+++ b/apps/designer/app/shared/feature-flags/flags.ts
@@ -1,4 +1,3 @@
export const assets = false;
export const fonts = false;
export const example = false;
-export const pages = false;
diff --git a/packages/design-system/src/index.ts b/packages/design-system/src/index.ts
index 4f5d0e30ae..a158012adc 100644
--- a/packages/design-system/src/index.ts
+++ b/packages/design-system/src/index.ts
@@ -11,6 +11,7 @@ export * as ToggleGroup from "./components/toggle-group";
export * from "./components/progress-radial";
export * from "./components/select";
export * from "./components/text-field";
+export * from "./components/text-area";
export { SearchField } from "./components/search-field";
export { Flex } from "./components/flex";
export { Image } from "./components/image";
diff --git a/packages/project/src/db/build.ts b/packages/project/src/db/build.ts
index 4338a2c5f2..b87a67151d 100644
--- a/packages/project/src/db/build.ts
+++ b/packages/project/src/db/build.ts
@@ -120,7 +120,7 @@ export const editPage = async (
name: data.name ?? currentPage.name,
path: data.path ?? currentPage.path,
title: data.title ?? currentPage.title,
- meta: data.meta ?? currentPage.meta,
+ meta: { ...currentPage.meta, ...data.meta },
};
return {