Skip to content

Commit

Permalink
feat(slice-machine): add GitHub settings page
Browse files Browse the repository at this point in the history
  • Loading branch information
bapmrl committed Jan 29, 2024
1 parent c4705c8 commit e962c2d
Show file tree
Hide file tree
Showing 14 changed files with 1,137 additions and 65 deletions.
2 changes: 2 additions & 0 deletions packages/manager/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ export { createSliceMachineManager } from "./managers/createSliceMachineManager"
export { createSliceMachineManagerMiddleware } from "./managers/createSliceMachineManagerMiddleware";
export type { CreateSliceMachineManagerMiddlewareArgs } from "./managers/createSliceMachineManagerMiddleware";

export type { GitRepoSpecifier, Owner } from "./managers/git/types";

export type {
Environment,
PushChangesLimit,
Expand Down
25 changes: 13 additions & 12 deletions packages/slice-machine/components/Navigation/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { telemetry } from "@src/apiClient";
import VideoItem from "@components/Navigation/VideoItem";
import { LightningIcon } from "@src/icons/Lightning";
import { MathPlusIcon } from "@src/icons/MathPlusIcon";
import { SettingsIcon } from "@src/icons/SettingsIcon";
import { CUSTOM_TYPES_CONFIG } from "@src/features/customTypes/customTypesConfig";
import {
SideNavSeparator,
Expand Down Expand Up @@ -141,21 +142,11 @@ const Navigation: FC = () => {
)}

<SideNavList position="bottom">
<SideNavListItem>
<SideNavLink
title="Settings"
href="/settings"
Icon={(props) => <MathPlusIcon {...props} />}
active={router.asPath.startsWith("/settings")}
component={Link}
/>
</SideNavListItem>

<SideNavListItem>
<SideNavLink
title="Invite team"
href={`${repositoryUrl}/settings/users`}
Icon={(props) => <MathPlusIcon {...props} />}
Icon={MathPlusIcon}
onClick={() => {
void telemetry.track({
event: "users-invite-button-clicked",
Expand All @@ -174,11 +165,21 @@ const Navigation: FC = () => {
</Suspense>
</ErrorBoundary>

<SideNavListItem>
<SideNavLink
title="Settings"
href="/settings"
Icon={SettingsIcon}
active={router.asPath.startsWith("/settings")}
component={Link}
/>
</SideNavListItem>

<SideNavListItem>
<SideNavLink
title="Changelog"
href="/changelog"
Icon={(props) => <LightningIcon {...props} />}
Icon={LightningIcon}
active={router.asPath.startsWith("/changelog")}
component={Link}
RightElement={
Expand Down
3 changes: 3 additions & 0 deletions packages/slice-machine/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,9 @@
"next-router-mock": "0.8.0",
"node-fetch": "3.3.1",
"parse-multipart-data": "1.5.0",
"postcss": "8.4.21",
"postcss-flexbugs-fixes": "5.0.2",
"postcss-preset-env": "9.3.0",
"rc-drawer": "4.4.3",
"react": "18.2.0",
"react-beautiful-dnd": "13.1.1",
Expand Down
33 changes: 1 addition & 32 deletions packages/slice-machine/pages/settings.tsx
Original file line number Diff line number Diff line change
@@ -1,32 +1 @@
import React from "react";
import Head from "next/head";
import { BaseStyles } from "theme-ui";
import {
AppLayout,
AppLayoutBreadcrumb,
AppLayoutContent,
AppLayoutHeader,
} from "@components/AppLayout";
import { ConnectGitRepository } from "@src/features/git/ConnectGitRepository/ConnectGitRepository";

const Settings: React.FC = () => {
return (
<>
<Head>
<title>Settings - Slice Machine</title>
</Head>
<AppLayout>
<AppLayoutHeader>
<AppLayoutBreadcrumb folder="Settings" />
</AppLayoutHeader>
<AppLayoutContent>
<BaseStyles sx={{ display: "flex", flexDirection: "column" }}>
<ConnectGitRepository />
</BaseStyles>
</AppLayoutContent>
</AppLayout>
</>
);
};

export default Settings;
export { SettingsPage as default } from "@src/features/settings/SettingsPage";
10 changes: 10 additions & 0 deletions packages/slice-machine/postcss.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
module.exports = {
plugins: {
"postcss-flexbugs-fixes": true,
"postcss-preset-env": {
autoprefixer: { flexbox: "no-2009" },
features: { "custom-properties": false, "nesting-rules": true },
stage: 3,
},
},
};
Original file line number Diff line number Diff line change
Expand Up @@ -193,15 +193,17 @@ export const WithHeaderAndBoxContent = {
<OwnerSelect />
</FieldSetHeader>
<FieldSetContent>
{/*
* TODO: these `padding` values actually don't match Figma, but they
* are the closest allowed by the `Box` component.
*/}
<Box flexDirection="column" padding={{ block: 72, inline: 100 }}>
<Text align="center" variant="emphasized">
No Results Found
</Text>
<Text align="center" color="grey11">
<Box
alignItems="center"
flexDirection="column"
/*
* TODO: these `padding` values actually don't match Figma, but they
* are the closest allowed by the `Box` component.
*/
padding={{ block: 72, inline: 100 }}
>
<Text variant="emphasized">No Results Found</Text>
<Text color="grey11">
Try selecting a different Git account or organization on the top
left.
</Text>
Expand Down
29 changes: 29 additions & 0 deletions packages/slice-machine/src/features/settings/SettingsPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import { Box } from "@prismicio/editor-ui";
import Head from "next/head";
import type { FC } from "react";

import {
AppLayout,
AppLayoutBreadcrumb,
AppLayoutContent,
AppLayoutHeader,
} from "@components/AppLayout";
import { ConnectGitRepository } from "@src/features/settings/git/ConnectGitRepository";

export const SettingsPage: FC = () => (
<>
<Head>
<title>Settings - Slice Machine</title>
</Head>
<AppLayout>
<AppLayoutHeader>
<AppLayoutBreadcrumb folder="Settings" />
</AppLayoutHeader>
<AppLayoutContent>
<Box flexDirection="column" maxWidth={600}>
<ConnectGitRepository />
</Box>
</AppLayoutContent>
</AppLayout>
</>
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
import { keys } from "@prismicio/editor-support/Object";
import {
Box,
Button,
ButtonGroup,
ErrorBoundary,
IconButton,
Select,
SelectItem,
Skeleton,
Text,
tokens,
} from "@prismicio/editor-ui";
import type { GitRepoSpecifier } from "@slicemachine/manager";
import { type ComponentProps, type FC, type ReactNode, Suspense } from "react";

import {
FieldSet,
FieldSetContent,
FieldSetFooter,
FieldSetHeader,
FieldSetLegend,
FieldSetList,
FieldSetListItem,
} from "@src/components/FieldSet";
import {
type GitProvider,
gitProviderToConfig,
} from "@src/features/settings/git/GitProvider";
import { useGitOwners } from "@src/features/settings/git/useGitOwners";
import { useLinkedGitRepos } from "@src/features/settings/git/useLinkedGitRepos";
import { useUser } from "@src/hooks/useUser";
import useSliceMachineActions from "@src/modules/useSliceMachineActions";

// TODO(DT-1928): export the `SX` type from `@prismicio/editor-ui`.
type SX = ComponentProps<typeof Button>["sx"];

export const ConnectGitRepository: FC = () => (
<FieldSet>
<FieldSetLegend>Connected Git Repository</FieldSetLegend>
<ErrorBoundary
renderError={(error) => {
console.error("TODO: error", error);
return <div>TODO: Error</div>;
}}
>
<Suspense fallback={<ContentSkeleton />}>
<Content />
</Suspense>
</ErrorBoundary>
<FieldSetFooter action={<IconButton icon="openInNew" />}>
Learn more about Prismic for Git
</FieldSetFooter>
</FieldSet>
);

const Content: FC = () => {
const { isLoggedIn } = useUser();
return isLoggedIn ? <LoggedInContent /> : <LoggedOutContent />;
};

const LoggedInContent: FC = () => {
const linkedGitRepos = useLinkedGitRepos();
return linkedGitRepos.length > 0 ? (
<LinkedRepositoryContent linkedRepo={linkedGitRepos[0]} />
) : (
<UnlinkedRepositoryContent />
);
};

type LinkedRepositoryContentProps = { linkedRepo: GitRepoSpecifier };

const LinkedRepositoryContent: FC<LinkedRepositoryContentProps> = () => (
<div>Linked repository</div>
);

const UnlinkedRepositoryContent: FC = () => {
const owners = useGitOwners();
if (owners.length > 0) {
return <div>Has owners</div>;
} else {
return (
<FieldSetContent>
<ButtonGroup>
{keys(gitProviderToConfig).map((provider) => (
<ConnectButton
key={provider}
provider={provider}
sx={{ flexBasis: 0, flexGrow: 1 }}
/>
))}
</ButtonGroup>
</FieldSetContent>
);
}
};

type OwnerSelectProps = { sx?: SX };

const OwnerSelect: FC<OwnerSelectProps> = ({ sx }) => {
const { Icon } = gitProviderToConfig.gitHub;
return (
<Select
color="grey"
constrainContentWidth
disabled
flexContent
placeholder="Owner"
renderStartIcon={() => <Icon color={tokens.color.greyLight11} />}
size="large"
sx={sx}
>
<SelectItem size="large" value="owner-1">
Owner
</SelectItem>
<SelectItem size="large" value="owner-2">
Owner
</SelectItem>
</Select>
);
};

type ConnectButtonProps = { provider: GitProvider; sx?: SX };

const ConnectButton: FC<ConnectButtonProps> = ({ provider, sx }) => {
const { connect, Icon, name, supported } = gitProviderToConfig[provider];
return (
<Button
color="grey"
disabled={!supported}
onClick={() => connect()}
renderStartIcon={() => <Icon color={tokens.color.greyLight11} />}
sx={sx}
>
{name}
{supported ? undefined : (
<Text color="inherit" variant="small">
{" "}
(soon)
</Text>
)}
</Button>
);
};

const ContentSkeleton: FC = () => (
<>
<FieldSetHeader>
<OwnerSelect sx={{ width: "calc(50% - 8px)" }} />
</FieldSetHeader>
<FieldSetList>
{[...Array(4).keys()].map((index) => (
<FieldSetListItem
action={<Skeleton height={32} width={67.59} />}
key={index}
>
<Skeleton
height={24}
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore TODO(DT-1918): add `verticalAlign: "middle"` to the `sx` prop.
sx={{ verticalAlign: "middle" }}
width={129.92}
/>
</FieldSetListItem>
))}
</FieldSetList>
</>
);

const LoggedOutContent: FC = () => {
const { openLoginModal } = useSliceMachineActions();
return (
<FieldSetContent>
<BlankSlate
title="It seems like you are logged out"
description="Log in to connect a Git repository."
action={
<Button onClick={() => openLoginModal()}>Log in to Prismic</Button>
}
/>
</FieldSetContent>
);
};

type BlankSlateProps = {
title: string;
description: string;
action: ReactNode;
};

const BlankSlate: FC<BlankSlateProps> = ({ title, description, action }) => (
<Box
alignItems="center"
flexDirection="column"
/*
* TODO: these `padding` values actually don't match Figma, but they are
* the closest allowed by the `Box` component.
*/
padding={{ block: 72, inline: 100 }}
>
<Text variant="emphasized">{title}</Text>
<Text color="grey11">{description}</Text>
<ButtonGroup sx={{ marginTop: 8 }}>{action}</ButtonGroup>
</Box>
);
Loading

0 comments on commit e962c2d

Please sign in to comment.