Skip to content

Commit 066caca

Browse files
committed
Page block's x/y padding based on theme now; Page switcher dropdown; Editor's left pane's header fixed; block hover overlay ui overhaul;
1 parent fde26f4 commit 066caca

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

73 files changed

+1787
-1701
lines changed

apps/web/app/(with-contexts)/(with-layout)/home-page-layout.tsx

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
} from "@components/contexts";
1010
import { MasterLayout } from "@components/public/base-layout";
1111
import { Profile } from "@courselit/common-models";
12+
import { defaultTheme } from "@courselit/page-primitives";
1213
import { getPage } from "@ui-lib/utils";
1314
import { useContext, useEffect, useState } from "react";
1415

@@ -40,7 +41,7 @@ export default function HomepageLayout({
4041
title={page.title}
4142
typefaces={typefaces}
4243
siteInfo={siteinfo}
43-
theme={{ name: "", active: true, styles: {} }}
44+
// theme={{ name: "", active: true, styles: {} }}
4445
dispatch={() => {}}
4546
state={{
4647
config: config,
@@ -52,11 +53,7 @@ export default function HomepageLayout({
5253
checked: profile ? true : false,
5354
},
5455
networkAction: false,
55-
theme: {
56-
name: "",
57-
active: false,
58-
styles: {},
59-
},
56+
theme: defaultTheme,
6057
typefaces,
6158
message: {
6259
message: "",

apps/web/components/admin/page-editor/add-widget.tsx

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,6 @@ import {
55
PageTypeBlog,
66
PageTypeCommunity,
77
} from "@courselit/common-models";
8-
import { IconButton } from "@courselit/components-library";
9-
import { Cross as Close } from "@courselit/icons";
10-
import { EDIT_PAGE_ADD_WIDGET_TITLE } from "../../../ui-config/strings";
118
import widgets from "../../../ui-config/widgets";
129

1310
interface WidgetsListProps {
@@ -19,14 +16,6 @@ interface WidgetsListProps {
1916
function AddWidget({ pageType, onSelection, onClose }: WidgetsListProps) {
2017
return (
2118
<ul>
22-
<li className="flex items-center px-2 py-3 justify-between">
23-
<h2 className="text-lg font-medium">
24-
{EDIT_PAGE_ADD_WIDGET_TITLE}
25-
</h2>
26-
<IconButton onClick={onClose} variant="soft">
27-
<Close fontSize="small" />
28-
</IconButton>
29-
</li>
3019
{Object.keys(widgets)
3120
.filter((widget) => !["header", "footer"].includes(widget))
3221
.map((item, index) =>

apps/web/components/admin/page-editor/edit-widget.tsx

Lines changed: 5 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
import { useState } from "react";
22
import widgets from "../../../ui-config/widgets";
33
import { AppDispatch, AppState } from "@courselit/state-management";
4-
import { Cross as Close } from "@courselit/icons";
4+
import { Button } from "@courselit/components-library";
55
import AdminWidget from "./admin-widget";
6-
import { IconButton, Button } from "@courselit/components-library";
76

87
interface EditWidgetProps {
98
onChange: (widgetId: string, settings: Record<string, unknown>) => void;
@@ -31,30 +30,22 @@ export default function EditWidget({
3130
}: EditWidgetProps) {
3231
const [deleteConfirmation, setDeleteConfirmation] = useState(false);
3332
const [hideActionButtons, setHideActionButtons] = useState(false);
34-
const actualWidget = widgets[widget.name];
3533
const [preservedStateAcrossRerender, setPreservedStateAcrossRerender] =
3634
useState<Record<string, unknown>>({});
3735

36+
const actualWidget = widgets[widget.name];
37+
3838
const onDeleteWidget = () => {
3939
if (deleteConfirmation) {
4040
onDelete(widget.widgetId);
41-
onClose();
4241
} else {
4342
setDeleteConfirmation(true);
43+
setTimeout(() => setDeleteConfirmation(false), 2000);
4444
}
4545
};
4646

4747
return (
48-
<div className="flex flex-col">
49-
<li className="flex items-center px-2 py-3 justify-between">
50-
<h2 className="text-lg font-medium">
51-
{actualWidget && actualWidget.metadata.displayName}
52-
{!actualWidget && widget.name}
53-
</h2>
54-
<IconButton onClick={onClose} variant="soft">
55-
<Close fontSize="small" />
56-
</IconButton>
57-
</li>
48+
<div>
5849
{actualWidget && (
5950
<div className="px-2">
6051
<AdminWidget

apps/web/components/admin/page-editor/fonts-list.tsx

Lines changed: 15 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
import React from "react";
22
import { Typeface } from "@courselit/common-models";
3-
import { EDIT_PAGE_BUTTON_FONTS } from "../../../ui-config/strings";
4-
import { Cross as Close, Star } from "@courselit/icons";
5-
import { IconButton } from "@courselit/components-library";
3+
import { Star } from "@courselit/icons";
64

75
interface FontListProps {
86
draftTypefaces: Typeface[];
@@ -40,26 +38,20 @@ function FontsList({
4038
)[0]?.typeface;
4139

4240
return (
43-
<ul>
44-
<li className="flex items-center px-2 py-3 justify-between">
45-
<h2 className="text-lg font-medium">
46-
{EDIT_PAGE_BUTTON_FONTS}
47-
</h2>
48-
<IconButton onClick={onClose} variant="soft">
49-
<Close fontSize="small" />
50-
</IconButton>
51-
</li>
52-
{fonts.map((font) => (
53-
<li
54-
className="flex items-center px-2 py-3 hover:!bg-slate-100 cursor-pointer justify-between"
55-
onClick={() => saveDraftTypefaces(font)}
56-
key={font}
57-
>
58-
{font}
59-
{defaultTypeface === font && <Star />}
60-
</li>
61-
))}
62-
</ul>
41+
<div>
42+
<ul>
43+
{fonts.map((font) => (
44+
<li
45+
className="flex items-center px-2 py-3 hover:!bg-slate-100 cursor-pointer justify-between"
46+
onClick={() => saveDraftTypefaces(font)}
47+
key={font}
48+
>
49+
{font}
50+
{defaultTypeface === font && <Star />}
51+
</li>
52+
))}
53+
</ul>
54+
</div>
6355
);
6456
}
6557

apps/web/components/admin/page-editor/index.tsx

Lines changed: 94 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import {
2222
TOAST_TITLE_ERROR,
2323
EDIT_PAGE_BUTTON_VIEW,
2424
EDIT_PAGE_BUTTON_THEME,
25+
EDIT_PAGE_ADD_WIDGET_TITLE,
2526
} from "../../../ui-config/strings";
2627
import { useRouter } from "next/navigation";
2728
import {
@@ -40,14 +41,21 @@ import { ArrowUpFromLine, Eye, LogOut, Palette, Earth } from "lucide-react";
4041
import { cn } from "@/lib/shadcn-utils";
4142
import { ScrollArea } from "@/components/ui/scroll-area";
4243
import { Separator } from "@/components/ui/separator";
43-
import { Badge } from "@/components/ui/badge";
4444
import {
4545
Tooltip,
4646
TooltipContent,
4747
TooltipProvider,
4848
TooltipTrigger,
4949
} from "@/components/ui/tooltip";
5050
import Link from "next/link";
51+
import { PanelHeader } from "./panel-header";
52+
import {
53+
Select,
54+
SelectContent,
55+
SelectItem,
56+
SelectTrigger,
57+
SelectValue,
58+
} from "@/components/ui/select";
5159

5260
const EditWidget = dynamic(() => import("./edit-widget"));
5361
const AddWidget = dynamic(() => import("./add-widget"));
@@ -105,6 +113,8 @@ export default function PageEditor({
105113
const [draftTheme, setDraftTheme] = useState<Theme>();
106114
const [theme, setTheme] = useState<Theme>();
107115
const { toast } = useToast();
116+
const [pages, setPages] = useState<Page[]>([]);
117+
const [loadingPages, setLoadingPages] = useState(true);
108118

109119
const router = useRouter();
110120
const debouncedSave = useCallback(
@@ -121,7 +131,44 @@ export default function PageEditor({
121131
useEffect(() => {
122132
loadDraftTypefaces();
123133
loadPage();
124-
}, []);
134+
135+
loadPages();
136+
}, [address.backend, dispatch]);
137+
138+
async function loadPages() {
139+
setLoadingPages(true);
140+
const query = `
141+
query {
142+
pages: getPages(type: SITE) {
143+
pageId,
144+
name,
145+
entityId,
146+
deleteable
147+
}
148+
}
149+
`;
150+
const fetch = new FetchBuilder()
151+
.setUrl(`${address.backend}/api/graph`)
152+
.setPayload(query)
153+
.setIsGraphQLEndpoint(true)
154+
.build();
155+
try {
156+
dispatch && dispatch(networkAction(true));
157+
const response = await fetch.exec();
158+
if (response.pages) {
159+
setPages(response.pages);
160+
}
161+
} catch (err: any) {
162+
toast({
163+
title: TOAST_TITLE_ERROR,
164+
description: err.message || "Failed to load pages",
165+
variant: "destructive",
166+
});
167+
} finally {
168+
setLoadingPages(false);
169+
dispatch && dispatch(networkAction(false));
170+
}
171+
}
125172

126173
useEffect(() => {
127174
if (JSON.stringify(layout) !== JSON.stringify(page.draftLayout)) {
@@ -639,15 +686,37 @@ export default function PageEditor({
639686
<div className="fixed w-full border-b z-10 bg-background">
640687
<header className="flex w-full h-14 px-6 justify-between items-center">
641688
<div className="flex items-center gap-3">
642-
<Badge
643-
variant="outline"
644-
className="text-sm font-medium"
645-
>
646-
{page.type}
647-
</Badge>
648-
<h1 className="text-lg font-semibold">
649-
{page.name}
650-
</h1>
689+
<div className="w-[220px]">
690+
{loadingPages ? (
691+
<div className="flex flex-col gap-2">
692+
<Skeleton className="h-10 w-full rounded-md" />
693+
</div>
694+
) : pages.length > 0 && page.pageId ? (
695+
<Select
696+
value={page.pageId}
697+
onValueChange={(value) => {
698+
if (value !== page.pageId) {
699+
router.push(`/dashboard/page/${value}`);
700+
}
701+
}}
702+
>
703+
<SelectTrigger className="w-full">
704+
<SelectValue placeholder="Select page" />
705+
</SelectTrigger>
706+
<SelectContent>
707+
{pages.map((p) => (
708+
<SelectItem key={p.pageId} value={p.pageId}>
709+
{p.name}
710+
</SelectItem>
711+
))}
712+
</SelectContent>
713+
</Select>
714+
) : (
715+
<div className="h-10 flex items-center px-3 border rounded-md text-sm text-muted-foreground">
716+
No pages found
717+
</div>
718+
)}
719+
</div>
651720
</div>
652721
<div className="flex items-center gap-4">
653722
<div className="flex items-center gap-2">
@@ -769,12 +838,20 @@ export default function PageEditor({
769838
</div>
770839
<div className="flex w-full h-[calc(100vh-56px)] mt-14 gap-4 p-4 bg-muted/10">
771840
{leftPaneContent !== "none" && (
772-
<div className="w-[300px] rounded-xl border bg-background/98 backdrop-blur supports-[backdrop-filter]:bg-background/80 shadow-sm">
773-
<ScrollArea className="h-full">
774-
<div>
775-
<div className="space-y-4">
776-
{activeSidePaneContent}
777-
</div>
841+
<div className="w-[300px] rounded-xl border bg-background/98 backdrop-blur supports-[backdrop-filter]:bg-background/80 shadow-sm flex flex-col overflow-hidden">
842+
<PanelHeader
843+
title={
844+
leftPaneContent === "widgets" ? EDIT_PAGE_ADD_WIDGET_TITLE :
845+
leftPaneContent === "editor" ? "Edit Widget" :
846+
leftPaneContent === "fonts" ? "Fonts" :
847+
leftPaneContent === "theme" ? "Theme" :
848+
leftPaneContent === "seo" ? "SEO" : ""
849+
}
850+
onClose={onClose}
851+
/>
852+
<ScrollArea className="h-[calc(100%-56px)]">
853+
<div className="py-2 space-y-4">
854+
{activeSidePaneContent}
778855
</div>
779856
</ScrollArea>
780857
</div>
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import React from "react";
2+
import { Button } from "@components/ui/button";
3+
import { X } from "lucide-react";
4+
5+
interface PanelHeaderProps {
6+
title: string;
7+
onClose: () => void;
8+
}
9+
10+
export function PanelHeader({ title, onClose }: PanelHeaderProps) {
11+
return (
12+
<div className="sticky top-0 z-10 border-b bg-background/95 backdrop-blur supports-[backdrop-filter]:bg-background/80">
13+
<div className="flex items-center p-2 justify-between">
14+
<h2 className="text-lg font-medium">{title}</h2>
15+
<Button variant="ghost" size="icon" onClick={onClose}>
16+
<X className="w-4 h-4" />
17+
</Button>
18+
</div>
19+
</div>
20+
);
21+
}

apps/web/components/admin/page-editor/seo-editor.tsx

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,18 @@ import {
44
Form,
55
FormField,
66
FormSubmit,
7-
IconButton,
87
MediaSelector,
98
PageBuilderPropertyHeader,
109
} from "@courselit/components-library";
1110
import {
1211
BUTTON_SAVE,
13-
EDIT_PAGE_SEO_HEADER,
1412
SEO_FORM_DESC_LABEL,
1513
SEO_FORM_NAME_LABEL,
1614
SEO_FORM_ROBOTS_LABEL,
1715
SEO_FORM_SOCIAL_IMAGE_LABEL,
1816
SEO_FORM_SOCIAL_IMAGE_TOOLTIP,
1917
} from "@ui-config/strings";
2018
import { ChangeEvent, FormEvent, useState } from "react";
21-
import { Cross as Close } from "@courselit/icons";
2219

2320
export default function SeoEditor({
2421
title,
@@ -59,13 +56,7 @@ export default function SeoEditor({
5956
};
6057

6158
return (
62-
<div className="flex flex-col">
63-
<div className="flex items-center px-2 py-3 justify-between">
64-
<h2 className="text-lg font-medium">{EDIT_PAGE_SEO_HEADER}</h2>
65-
<IconButton onClick={onClose} variant="soft">
66-
<Close fontSize="small" />
67-
</IconButton>
68-
</div>
59+
<div>
6960
<Form className="flex flex-col p-2 gap-4" onSubmit={onSubmit}>
7061
<FormField
7162
required

0 commit comments

Comments
 (0)