Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 9 additions & 23 deletions apps/builder/app/dashboard/dashboard.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,38 +5,24 @@ const { getView } = __testing__;

describe("getView", () => {
test("returns 'search' for search path", () => {
expect(getView("/dashboard/search", true, true)).toBe("search");
expect(getView("/dashboard/search", false, true)).toBe("search");
expect(getView("/dashboard/search", true, false)).toBe("search");
expect(getView("/dashboard/search", false, false)).toBe("search");
});

test("returns 'welcome' when no projects on default workspace", () => {
expect(getView("/dashboard", false, true)).toBe("welcome");
expect(getView("/dashboard/templates", false, true)).toBe("welcome");
test("returns 'welcome' when no projects and workspace not suspended", () => {
expect(getView("/dashboard", false, false)).toBe("welcome");
});

test("returns 'projects' when no projects on non-default workspace", () => {
// Non-default workspaces should never show the welcome onboarding page
expect(getView("/dashboard", false, false)).toBe("projects");
test("returns 'projects' when workspace is suspended with no projects", () => {
expect(getView("/dashboard", false, true)).toBe("projects");
});

test("returns 'templates' on non-default workspace with no projects", () => {
expect(getView("/dashboard/templates", false, false)).toBe("templates");
});

test("returns 'templates' for templates path with projects", () => {
expect(getView("/dashboard/templates", true, true)).toBe("templates");
});

test("returns 'projects' for dashboard root with projects", () => {
test("returns 'projects' when there are projects", () => {
expect(getView("/dashboard", true, false)).toBe("projects");
expect(getView("/dashboard", true, true)).toBe("projects");
});

test("returns 'projects' for unknown paths with projects", () => {
expect(getView("/dashboard/unknown", true, true)).toBe("projects");
});

test("search takes priority over welcome", () => {
// Even with no projects, search view should show
expect(getView("/dashboard/search", false, true)).toBe("search");
expect(getView("/dashboard/unknown", true, false)).toBe("projects");
});
});
85 changes: 37 additions & 48 deletions apps/builder/app/dashboard/dashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import {
Grid,
IconButton,
} from "@webstudio-is/design-system";
import { BodyIcon, ExtensionIcon } from "@webstudio-is/icons";
import { BodyIcon } from "@webstudio-is/icons";
import {
NavLink,
useLocation,
Expand All @@ -33,10 +33,9 @@ import { dashboardPath } from "~/shared/router-utils";
import { CollapsibleSection } from "~/builder/shared/collapsible-section";
import { ProfileMenu } from "./profile-menu";
import { Projects } from "./projects/projects";
import { Templates } from "./templates/templates";
import { Welcome } from "./welcome/welcome";
import { Header } from "./shared/layout";
import { help } from "~/shared/help";
import { help, socialLinks } from "~/shared/help";
import { SearchResults } from "./search/search-results";
import type { DashboardData } from "./shared/types";
import { Search } from "./search/search-field";
Expand Down Expand Up @@ -194,22 +193,16 @@ export const DashboardSetup = ({ data }: { data: DashboardData }) => {
const getView = (
pathname: string,
hasProjects: boolean,
isDefaultWorkspace: boolean
isWorkspaceSuspended: boolean
) => {
if (pathname === dashboardPath("search")) {
return "search";
}

// Only show the onboarding welcome page on the default workspace
// when the user has no projects yet. Non-default workspaces that are
// empty should show the normal (empty) projects view.
if (hasProjects === false && isDefaultWorkspace) {
if (hasProjects === false && isWorkspaceSuspended === false) {
return "welcome";
}

if (pathname === dashboardPath("templates")) {
return "templates";
}
return "projects";
};

Expand All @@ -229,7 +222,6 @@ export const Dashboard = () => {
publisherHost,
projectToClone,
projects,
templates,
workspaces,
currentWorkspaceId,
} = data;
Expand All @@ -243,38 +235,21 @@ export const Dashboard = () => {
}

const isWorkspaceSuspended = isDowngradedForMember(currentWorkspace);
const hasProjects = projects.length > 0 || isWorkspaceSuspended;
const isDefaultWorkspace =
currentWorkspaceId === undefined ||
workspaces?.find((w) => w.id === currentWorkspaceId)?.isDefault === true;
const view = getView(location.pathname, hasProjects, isDefaultWorkspace);
const hasProjects = projects.length > 0;
const view = getView(location.pathname, hasProjects, isWorkspaceSuspended);

const showWorkspaceSelector =
workspaces !== undefined &&
workspaces.length > 0 &&
currentWorkspaceId !== undefined;

const navItems =
view === "welcome"
? [
{
to: dashboardPath(),
prefix: <ExtensionIcon />,
children: "Welcome",
},
]
: [
{
to: dashboardPath("projects"),
prefix: <BodyIcon />,
children: "Projects",
},
{
to: dashboardPath("templates"),
prefix: <ExtensionIcon />,
children: "Starter templates",
},
];
const navItems = [
{
to: dashboardPath("projects"),
prefix: <BodyIcon />,
children: "Projects",
},
];

return (
<TooltipProvider>
Expand Down Expand Up @@ -360,6 +335,29 @@ export const Dashboard = () => {
children: item.label,
}))}
/>
<Flex
align="center"
gap="3"
css={{
paddingInline: theme.panel.paddingInline,
paddingBlock: theme.spacing[5],
}}
>
<Text variant="labels" color="subtle">
Follow us:
</Text>
{socialLinks.map(({ label, url, icon }) => (
<Link
key={url}
href={url}
target="_blank"
color="subtle"
aria-label={label}
>
{icon}
</Link>
))}
</Flex>
</CollapsibleSection>
</Grid>
{view === "projects" && (
Expand All @@ -372,17 +370,8 @@ export const Dashboard = () => {
isWorkspaceSuspended={isWorkspaceSuspended}
/>
)}
{view === "templates" && (
<Templates
projects={templates}
currentWorkspaceId={currentWorkspaceId}
/>
)}
{view === "welcome" && (
<Welcome
projects={templates}
currentWorkspaceId={currentWorkspaceId}
/>
<Welcome currentWorkspaceId={currentWorkspaceId} />
)}
{view === "search" && <SearchResults {...data} />}
</Flex>
Expand Down
11 changes: 11 additions & 0 deletions apps/builder/app/dashboard/projects/projects.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import {
ToggleGroupButton,
PanelBanner,
panelBannerIconColor,
Link,
buttonStyle,
} from "@webstudio-is/design-system";
import { RepeatGridIcon, ListViewIcon } from "@webstudio-is/icons";
import type { DashboardProject } from "@webstudio-is/dashboard";
Expand Down Expand Up @@ -145,6 +147,15 @@ export const Projects = (props: ProjectsProps) => {
</ToggleGroupButton>
</ToggleGroup>
<SortSelect value={sortState} onValueChange={handleSortChange} />
<Link
className={buttonStyle({ color: "dark" })}
underline="none"
href="https://webstudio.is/marketplace/templates/"
target="_blank"
color="contrast"
>
Use template
</Link>
{permissions.canCreateProject && (
<CreateProject workspaceId={props.currentWorkspaceId} />
)}
Expand Down
24 changes: 5 additions & 19 deletions apps/builder/app/dashboard/search/search-results.tsx
Original file line number Diff line number Diff line change
@@ -1,42 +1,37 @@
import { useMemo } from "react";
import { matchSorter } from "match-sorter";
import { useSearchParams } from "react-router-dom";
import { Flex, Separator, Text, theme } from "@webstudio-is/design-system";
import { Flex, Text, theme } from "@webstudio-is/design-system";
import type { DashboardProject } from "@webstudio-is/dashboard";
import { ProjectsGrid } from "../projects/projects";
import { Header, Main } from "../shared/layout";
import type { DashboardData } from "../shared/types";
import { NothingFound } from "./nothing-found";
import { TemplatesGrid } from "../templates/templates";

type SearchResults = {
projects: Array<DashboardProject>;
templates: Array<DashboardProject>;
};

const initialSearchResults: SearchResults = {
templates: [],
projects: [],
} as const;

export const SearchResults = (props: DashboardData) => {
const [searchParams] = useSearchParams();
const { projects, templates, publisherHost } = props;
const { projects, publisherHost } = props;
const search = searchParams.get("q");

const results = useMemo(() => {
if (!search || !projects || !templates) {
if (!search || !projects) {
return initialSearchResults;
}
const keys = ["title", "domain"];
return {
projects: matchSorter(projects, search, { keys }),
templates: matchSorter(templates, search, { keys }),
};
}, [projects, templates, search]);
}, [projects, search]);

const nothingFound =
results.projects.length === 0 && results.templates.length === 0;
const nothingFound = results.projects.length === 0;

return (
<Main>
Expand Down Expand Up @@ -66,15 +61,6 @@ export const SearchResults = (props: DashboardData) => {
/>
</>
)}
{results.templates.length > 0 && (
<>
<Separator />
<Text variant="brandSectionTitle" as="h2">
Templates
</Text>
<TemplatesGrid projects={results.templates} />
</>
)}
</Flex>
</Main>
);
Expand Down
1 change: 0 additions & 1 deletion apps/builder/app/dashboard/shared/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import type { Notifications } from "~/shared/polly/types";
export type DashboardData = {
user: User;
projects: Array<DashboardProject>;
templates: Array<DashboardProject>;
planFeatures: PlanFeatures;
purchases: Array<Purchase>;
publisherHost: string;
Expand Down
60 changes: 0 additions & 60 deletions apps/builder/app/dashboard/templates/template-card.tsx

This file was deleted.

Loading
Loading