Skip to content

Commit

Permalink
Merge pull request #1 from prismicio/feat/gui
Browse files Browse the repository at this point in the history
  • Loading branch information
lihbr committed Sep 12, 2023
2 parents 400da42 + df42cc1 commit d59d585
Show file tree
Hide file tree
Showing 37 changed files with 6,782 additions and 360 deletions.
8 changes: 7 additions & 1 deletion .eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@ module.exports = {
"plugin:@typescript-eslint/eslint-recommended",
"plugin:@typescript-eslint/recommended",
"plugin:prettier/recommended",
"plugin:react-hooks/recommended",
],
plugins: ["eslint-plugin-tsdoc"],
plugins: ["eslint-plugin-tsdoc", "react-refresh"],
rules: {
"no-console": ["warn", { allow: ["info", "warn", "error"] }],
"no-debugger": "warn",
Expand All @@ -34,5 +35,10 @@ module.exports = {
"@typescript-eslint/no-var-requires": "off",
"@typescript-eslint/explicit-module-boundary-types": "error",
"tsdoc/syntax": "warn",
"react-refresh/only-export-components": [
"warn",
{ allowConstantExport: true },
],
"react-hooks/rules-of-hooks": "off",
},
};
6 changes: 4 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ jobs:
strategy:
matrix:
os: [ubuntu-latest]
node: [16, 18, 20]
# https://github.com/vitejs/vite/issues/14299
node: [16, 18, 20.5]

steps:
- name: Set up Node
Expand Down Expand Up @@ -39,7 +40,8 @@ jobs:
fail-fast: false
matrix:
os: [ubuntu-latest]
node: [16, 18, 20]
# https://github.com/vitejs/vite/issues/14299
node: [16, 18, 20.5]

steps:
- name: Set up Node
Expand Down
2 changes: 1 addition & 1 deletion bin/prismic-upgrade-from-legacy.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
#!/usr/bin/env node

import("../dist/cli.cjs");
import("../dist/cli-watcher.cjs");
20 changes: 20 additions & 0 deletions gui/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<!doctype html>
<html lang="en">

<head>
<meta charset="UTF-8" />
<link rel="icon"
type="image/svg+xml"
href="/icon.svg" />
<meta name="viewport"
content="width=device-width, initial-scale=1.0" />
<title>Prismic Upgrade</title>
</head>

<body>
<div id="root"></div>
<script type="module"
src="/src/main.tsx"></script>
</body>

</html>
6 changes: 6 additions & 0 deletions gui/postcss.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export default {
plugins: {
tailwindcss: { config: "./gui/tailwind.config.js" },
autoprefixer: {},
},
};
1 change: 1 addition & 0 deletions gui/public/icon.svg
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
64 changes: 64 additions & 0 deletions gui/src/App.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { AnimatedElement, DocumentStatusBar, Tab } from "@prismicio/editor-ui";
import { useEffect, useState } from "react";

import { AppFooter } from "./components/AppFooter";
import { AppHeader } from "./components/AppHeader";
import { CustomTypeList } from "./components/CustomTypeList";
import { SharedSliceList } from "./components/SharedSliceList";
import { useRepository } from "./store/useRepository";
import { useSliceMachineConfig } from "./store/useSliceMachineConfig";
import { useUser } from "./store/useUser";

export function App(): JSX.Element {
const repository = useRepository();
const sliceMachineConfig = useSliceMachineConfig();
const user = useUser();

const tabs = ["Slices", "Custom Types", "Upgrade Summary"] as const;
const [activeTab, setActiveTab] =
useState<(typeof tabs)[number]>("Custom Types");

useEffect(() => {
repository.fetch();
sliceMachineConfig.fetch();
user.fetch();
}, []);

Check warning on line 25 in gui/src/App.tsx

View workflow job for this annotation

GitHub Actions / Suite (ubuntu-latest, Node 16)

React Hook useEffect has missing dependencies: 'repository', 'sliceMachineConfig', and 'user'. Either include them or remove the dependency array

Check warning on line 25 in gui/src/App.tsx

View workflow job for this annotation

GitHub Actions / Suite (ubuntu-latest, Node 18)

React Hook useEffect has missing dependencies: 'repository', 'sliceMachineConfig', and 'user'. Either include them or remove the dependency array

Check warning on line 25 in gui/src/App.tsx

View workflow job for this annotation

GitHub Actions / Suite (ubuntu-latest, Node 20.5)

React Hook useEffect has missing dependencies: 'repository', 'sliceMachineConfig', and 'user'. Either include them or remove the dependency array

return (
<>
<DocumentStatusBar status="release" />
<AppHeader />
<div className="container space-y-4">
<hr className="border-stone-300" />
<nav className="flex gap-2">
{tabs.map((tab) => (
<Tab
key={tab}
title={tab}
selected={activeTab === tab}
onSelect={() => setActiveTab(tab)}
/>
))}
</nav>
<AnimatedElement>
{activeTab === "Slices" ? (
<main key="Slices" className="space-y-4">
TODO: Slices
</main>
) : activeTab === "Custom Types" ? (
<main key="Custom Types" className="space-y-4">
<CustomTypeList key="Custom Types" />
<SharedSliceList key="Slices" />
</main>
) : (
<main key="Upgrade Summary" className="space-y-4">
TODO: Upgrade Summary
</main>
)}
</AnimatedElement>
<hr className="border-stone-300" />
</div>
<AppFooter />
</>
);
}
15 changes: 15 additions & 0 deletions gui/src/components/AppFooter.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
export function AppFooter(): JSX.Element {
return (
<footer className="container py-4 flex justify-between items-center">
<div className="text-stone-600 text-sm">
<a
href="https://prismic.io/docs/help-center"
className="underline"
target="_blank"
>
Help Center
</a>
</div>
</footer>
);
}
42 changes: 42 additions & 0 deletions gui/src/components/AppHeader.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { Icon, Skeleton } from "@prismicio/editor-ui";

import { useSliceMachineConfig } from "../store/useSliceMachineConfig";
import { useUser } from "../store/useUser";

export function AppHeader(): JSX.Element {
const [config, dashboard] = useSliceMachineConfig((state) => [
state.config,
state.dashboard,
]);
const profile = useUser((state) => state.profile);

return (
<header className="container py-4 flex justify-between items-center">
{config ? (
<div>
<h1 className="heading-1">{config.repositoryName}</h1>
<div className="text-stone-600 text-sm">
Dashboard:{" "}
<a href={dashboard ?? ""} className="underline" target="_blank">
{dashboard?.replace(/^https?:\/\//i, "")}
</a>
</div>
</div>
) : (
<div className="leading-[0]">
<Skeleton width={256} height={56} />
</div>
)}
{profile ? (
<em className="badge badge--medium">
<Icon name="public" size="small" />
Logged in as {profile.email}
</em>
) : (
<div className="leading-[0]">
<Skeleton width={256} height={28} />
</div>
)}
</header>
);
}
145 changes: 145 additions & 0 deletions gui/src/components/CustomTypeCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
import { Button, Icon, Tooltip } from "@prismicio/editor-ui";
import { useMemo } from "react";

import { useSliceConflicts } from "../store/useSliceConflicts";
import { useSliceMachineConfig } from "../store/useSliceMachineConfig";

import { StatusDot } from "./StatusDot";

import { CompositeSlice } from "../../../src/models/CompositeSlice";
import { CustomType } from "../../../src/models/CustomType";

export function CustomTypeCard({
customType,
}: {
customType: CustomType;
}): JSX.Element {
const dashboard = useSliceMachineConfig((state) => state.dashboard);
const conflicts = useSliceConflicts((state) => state.conflicts);

const sliceZones = useMemo(() => {
const sliceZoneMap: Record<
string,
{ id: string; tabID: string; compositeSlices: CompositeSlice[] }
> = {};
const compositeSlices = customType.getAllCompositeSlices();

for (const compositeSlice of compositeSlices) {
sliceZoneMap[compositeSlice.meta.path.sliceZoneID] ||= {
id: compositeSlice.meta.path.sliceZoneID,
tabID: compositeSlice.meta.path.tabID,
compositeSlices: [],
};
sliceZoneMap[compositeSlice.meta.path.sliceZoneID].compositeSlices.push(
compositeSlice,
);
}

return Object.values(sliceZoneMap);
}, [customType]);

const hasConflicts = useMemo(() => {
return Object.values(conflicts ?? {}).some((slices) => {
return slices.some((slice) => {
return (
slice instanceof CompositeSlice &&
slice.meta.parent.id === customType.id
);
});
});
}, [conflicts, customType]);

return (
<article className="rounded border-stone-300 border">
<header className="p-4 flex justify-between items-center">
<div className="space-x-2 flex items-end">
<h3 className="heading-3 flex gap-2 items-center">
<StatusDot type={hasConflicts ? "error" : "success"} />
{customType.definition.label}
</h3>
<em className="badge">
<Icon name="description" size="extraSmall" /> ID: {customType.id}
</em>
</div>
<div>
<a
href={`${dashboard}/masks/${customType.id}.json`}
target="_blank"
tabIndex={-1}
>
<Button size="small" variant="secondary" startIcon="link">
View on Prismic
</Button>
</a>
</div>
</header>
<hr />
<section className="px-4 py-3">
<ul className="space-y-3">
{sliceZones.length ? (
sliceZones.map((sliceZone) => (
<li key={sliceZone.id} className="space-y-2">
<header>
<h4 className="heading-4 flex gap-1 items-center">
<Icon name="viewDay" size="extraSmall" />
{sliceZone.id}
</h4>
<small className="italic">
{sliceZone.compositeSlices.length} legacy slice
{sliceZone.compositeSlices.length > 1 ? "s" : ""} in this
slice zone.
</small>
</header>
<ul className="grid grid-cols-3 gap-2">
{sliceZone.compositeSlices.map((compositeSlice) => (
<li
key={compositeSlice.id}
className="text-sm p-2 rounded border-stone-300 border hover:bg-stone-100"
>
<header className="space-y-2">
<h5 className="heading-5">
<Tooltip
title={`${compositeSlice.definition.fieldset} has a conflicting ID`}
content={`The ID of this slice is also present in other slice zones or among your shared slices. Head over to the "Slices" tab to resolve them!`}
open={
compositeSlice.id in (conflicts ?? {})
? undefined
: false
}
>
<span className="inline-flex gap-2 items-center">
<StatusDot
type={
conflicts
? compositeSlice.id in conflicts
? "error"
: "success"
: "unknown"
}
/>
{compositeSlice.definition.fieldset}
</span>
</Tooltip>
</h5>
<em className="badge">
<Icon name="tag" size="extraSmall" /> ID:{" "}
{compositeSlice.id}
</em>
</header>
</li>
))}
</ul>
</li>
))
) : (
<li>
<small className="italic">
No slice zone in this custom type.
</small>
</li>
)}
</ul>
</section>
</article>
);
}
23 changes: 23 additions & 0 deletions gui/src/components/CustomTypeList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { useRepository } from "../store/useRepository";

import { CustomTypeCard } from "./CustomTypeCard";

export function CustomTypeList(): JSX.Element {
const customTypes = useRepository((state) => state.customTypes);

return (
<section className="space-y-4">
<h2 className="heading-2">Custom Types ({customTypes?.length})</h2>
<ul className="space-y-2">
{customTypes?.map((customType) => (
<li key={customType.id}>
<CustomTypeCard customType={customType} />
</li>
))}
{customTypes?.length === 0 ? (
<li className="italic p-4">No Shared Slices found.</li>
) : null}
</ul>
</section>
);
}
20 changes: 20 additions & 0 deletions gui/src/components/SharedSliceList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { useRepository } from "../store/useRepository";

// TODO: Rendering
export function SharedSliceList(): JSX.Element {
const sharedSlices = useRepository((state) => state.sharedSlices);

return (
<section className="space-y-4">
<h2 className="heading-2">Shared Slices ({sharedSlices?.length})</h2>
<ul>
{sharedSlices?.map((sharedSlice) => (
<li key={sharedSlice.id}>{sharedSlice.id}</li>
))}
{sharedSlices?.length === 0 ? (
<li className="italic p-4">No Shared Slices found.</li>
) : null}
</ul>
</section>
);
}

0 comments on commit d59d585

Please sign in to comment.