chore(site): refactor starter templates to use react-query (#9697)

* Remove starter templates service

* Remove starter template service

* Remove template x service
This commit is contained in:
Bruno Quaresma
2023-09-18 11:02:55 -03:00
committed by GitHub
parent e1bd6ddc25
commit e74d8a7b21
8 changed files with 56 additions and 204 deletions
@@ -1,31 +1,30 @@
import { useMachine } from "@xstate/react";
import { useOrganizationId } from "hooks/useOrganizationId";
import { FC } from "react";
import { Helmet } from "react-helmet-async";
import { useParams } from "react-router-dom";
import { pageTitle } from "utils/page";
import { starterTemplateMachine } from "xServices/starterTemplates/starterTemplateXService";
import { StarterTemplatePageView } from "./StarterTemplatePageView";
import { useQuery } from "@tanstack/react-query";
import { templateExamples } from "api/queries/templates";
const StarterTemplatePage: FC = () => {
const { exampleId } = useParams() as { exampleId: string };
const organizationId = useOrganizationId();
const [state] = useMachine(starterTemplateMachine, {
context: {
organizationId,
exampleId,
},
});
const templateExamplesQuery = useQuery(templateExamples(organizationId));
const starterTemplate = templateExamplesQuery.data?.find(
(example) => example.id === exampleId,
);
return (
<>
<Helmet>
<title>
{pageTitle(state.context.starterTemplate?.name ?? exampleId)}
</title>
<title>{pageTitle(starterTemplate?.name ?? exampleId)}</title>
</Helmet>
<StarterTemplatePageView context={state.context} />
<StarterTemplatePageView
starterTemplate={starterTemplate}
error={templateExamplesQuery.error}
/>
</>
);
};
@@ -1,8 +1,4 @@
import {
mockApiError,
MockOrganization,
MockTemplateExample,
} from "testHelpers/entities";
import { mockApiError, MockTemplateExample } from "testHelpers/entities";
import { StarterTemplatePageView } from "./StarterTemplatePageView";
import type { Meta, StoryObj } from "@storybook/react";
@@ -17,23 +13,15 @@ type Story = StoryObj<typeof StarterTemplatePageView>;
export const Default: Story = {
args: {
context: {
exampleId: MockTemplateExample.id,
organizationId: MockOrganization.id,
error: undefined,
starterTemplate: MockTemplateExample,
},
error: undefined,
starterTemplate: MockTemplateExample,
},
};
export const Error: Story = {
args: {
context: {
exampleId: MockTemplateExample.id,
organizationId: MockOrganization.id,
error: mockApiError({
message: `Example ${MockTemplateExample.id} not found.`,
}),
starterTemplate: undefined,
},
error: mockApiError({
message: `Example ${MockTemplateExample.id} not found.`,
}),
starterTemplate: undefined,
},
};
@@ -9,27 +9,28 @@ import {
PageHeaderTitle,
} from "components/PageHeader/PageHeader";
import { FC } from "react";
import { StarterTemplateContext } from "xServices/starterTemplates/starterTemplateXService";
import ViewCodeIcon from "@mui/icons-material/OpenInNewOutlined";
import PlusIcon from "@mui/icons-material/AddOutlined";
import { Stack } from "components/Stack/Stack";
import { Link } from "react-router-dom";
import { ErrorAlert } from "components/Alert/ErrorAlert";
import { TemplateExample } from "api/typesGenerated";
export interface StarterTemplatePageViewProps {
context: StarterTemplateContext;
starterTemplate?: TemplateExample;
error?: unknown;
}
export const StarterTemplatePageView: FC<StarterTemplatePageViewProps> = ({
context,
starterTemplate,
error,
}) => {
const styles = useStyles();
const { starterTemplate } = context;
if (context.error) {
if (error) {
return (
<Margins>
<ErrorAlert error={context.error} />
<ErrorAlert error={error} />
</Margins>
);
}
@@ -1,16 +1,18 @@
import { useMachine } from "@xstate/react";
import { useOrganizationId } from "hooks/useOrganizationId";
import { FC } from "react";
import { Helmet } from "react-helmet-async";
import { pageTitle } from "utils/page";
import { starterTemplatesMachine } from "xServices/starterTemplates/starterTemplatesXService";
import { StarterTemplatesPageView } from "./StarterTemplatesPageView";
import { useQuery } from "@tanstack/react-query";
import { templateExamples } from "api/queries/templates";
import { getTemplatesByTag } from "utils/starterTemplates";
const StarterTemplatesPage: FC = () => {
const organizationId = useOrganizationId();
const [state] = useMachine(starterTemplatesMachine, {
context: { organizationId },
});
const templateExamplesQuery = useQuery(templateExamples(organizationId));
const starterTemplatesByTag = templateExamplesQuery.data
? getTemplatesByTag(templateExamplesQuery.data)
: undefined;
return (
<>
@@ -18,7 +20,10 @@ const StarterTemplatesPage: FC = () => {
<title>{pageTitle("Starter Templates")}</title>
</Helmet>
<StarterTemplatesPageView context={state.context} />
<StarterTemplatesPageView
error={templateExamplesQuery.error}
starterTemplatesByTag={starterTemplatesByTag}
/>
</>
);
};
@@ -1,6 +1,5 @@
import {
mockApiError,
MockOrganization,
MockTemplateExample,
MockTemplateExample2,
} from "testHelpers/entities";
@@ -18,25 +17,19 @@ type Story = StoryObj<typeof StarterTemplatesPageView>;
export const Default: Story = {
args: {
context: {
organizationId: MockOrganization.id,
error: undefined,
starterTemplatesByTag: getTemplatesByTag([
MockTemplateExample,
MockTemplateExample2,
]),
},
error: undefined,
starterTemplatesByTag: getTemplatesByTag([
MockTemplateExample,
MockTemplateExample2,
]),
},
};
export const Error: Story = {
args: {
context: {
organizationId: MockOrganization.id,
error: mockApiError({
message: "Error on loading the template examples",
}),
starterTemplatesByTag: undefined,
},
error: mockApiError({
message: "Error on loading the template examples",
}),
starterTemplatesByTag: undefined,
},
};
@@ -13,7 +13,7 @@ import { TemplateExampleCard } from "components/TemplateExampleCard/TemplateExam
import { FC } from "react";
import { Link, useSearchParams } from "react-router-dom";
import { combineClasses } from "utils/combineClasses";
import { StarterTemplatesContext } from "xServices/starterTemplates/starterTemplatesXService";
import { StarterTemplatesByTag } from "utils/starterTemplates";
const getTagLabel = (tag: string) => {
const labelByTag: Record<string, string> = {
@@ -26,22 +26,25 @@ const getTagLabel = (tag: string) => {
return labelByTag[tag] ?? tag;
};
const selectTags = ({ starterTemplatesByTag }: StarterTemplatesContext) => {
const selectTags = (starterTemplatesByTag: StarterTemplatesByTag) => {
return starterTemplatesByTag
? Object.keys(starterTemplatesByTag).sort((a, b) => a.localeCompare(b))
: undefined;
};
export interface StarterTemplatesPageViewProps {
context: StarterTemplatesContext;
starterTemplatesByTag?: StarterTemplatesByTag;
error?: unknown;
}
export const StarterTemplatesPageView: FC<StarterTemplatesPageViewProps> = ({
context,
starterTemplatesByTag,
error,
}) => {
const [urlParams] = useSearchParams();
const styles = useStyles();
const { starterTemplatesByTag } = context;
const tags = selectTags(context);
const tags = starterTemplatesByTag
? selectTags(starterTemplatesByTag)
: undefined;
const activeTag = urlParams.get("tag") ?? "all";
const visibleTemplates = starterTemplatesByTag
? starterTemplatesByTag[activeTag]
@@ -56,8 +59,8 @@ export const StarterTemplatesPageView: FC<StarterTemplatesPageViewProps> = ({
</PageHeaderSubtitle>
</PageHeader>
<Maybe condition={Boolean(context.error)}>
<ErrorAlert error={context.error} />
<Maybe condition={Boolean(error)}>
<ErrorAlert error={error} />
</Maybe>
<Maybe condition={Boolean(!starterTemplatesByTag)}>
@@ -1,71 +0,0 @@
import { getTemplateExamples } from "api/api";
import { TemplateExample } from "api/typesGenerated";
import { assign, createMachine } from "xstate";
export interface StarterTemplateContext {
organizationId: string;
exampleId: string;
starterTemplate?: TemplateExample;
error?: unknown;
}
export const starterTemplateMachine = createMachine(
{
id: "starterTemplate",
predictableActionArguments: true,
schema: {
context: {} as StarterTemplateContext,
services: {} as {
loadStarterTemplate: {
data: TemplateExample;
};
},
},
tsTypes: {} as import("./starterTemplateXService.typegen").Typegen0,
initial: "loading",
states: {
loading: {
invoke: {
src: "loadStarterTemplate",
onDone: {
actions: ["assignStarterTemplate"],
target: "idle.ok",
},
onError: {
actions: ["assignError"],
target: "idle.error",
},
},
},
idle: {
initial: "ok",
states: {
ok: { type: "final" },
error: { type: "final" },
},
},
},
},
{
services: {
loadStarterTemplate: async ({ organizationId, exampleId }) => {
const examples = await getTemplateExamples(organizationId);
const starterTemplate = examples.find(
(example) => example.id === exampleId,
);
if (!starterTemplate) {
throw new Error(`Example ${exampleId} not found.`);
}
return starterTemplate;
},
},
actions: {
assignError: assign({
error: (_, { data }) => data,
}),
assignStarterTemplate: assign({
starterTemplate: (_, { data }) => data,
}),
},
},
);
@@ -1,66 +0,0 @@
import { getTemplateExamples } from "api/api";
import { TemplateExample } from "api/typesGenerated";
import {
getTemplatesByTag,
StarterTemplatesByTag,
} from "utils/starterTemplates";
import { assign, createMachine } from "xstate";
export interface StarterTemplatesContext {
organizationId: string;
starterTemplatesByTag?: StarterTemplatesByTag;
error?: unknown;
}
export const starterTemplatesMachine = createMachine(
{
id: "starterTemplates",
predictableActionArguments: true,
schema: {
context: {} as StarterTemplatesContext,
services: {} as {
loadStarterTemplates: {
data: TemplateExample[];
};
},
},
tsTypes: {} as import("./starterTemplatesXService.typegen").Typegen0,
initial: "loading",
states: {
loading: {
invoke: {
src: "loadStarterTemplates",
onDone: {
actions: ["assignStarterTemplates"],
target: "idle.ok",
},
onError: {
actions: ["assignError"],
target: "idle.error",
},
},
},
idle: {
initial: "ok",
states: {
ok: { type: "final" },
error: { type: "final" },
},
},
},
},
{
services: {
loadStarterTemplates: ({ organizationId }) =>
getTemplateExamples(organizationId),
},
actions: {
assignError: assign({
error: (_, { data }) => data,
}),
assignStarterTemplates: assign({
starterTemplatesByTag: (_, { data }) => getTemplatesByTag(data),
}),
},
},
);