mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
feat: display specific errors if templates page fails (#4023)
* Surface templates page errors * Format * Separate error messages * Fix story * Format * Format * Fix imports * Remove unnecessary check * Format
This commit is contained in:
@@ -16,7 +16,7 @@ import { NotFoundPage } from "./pages/404Page/404Page"
|
|||||||
import { CliAuthenticationPage } from "./pages/CliAuthPage/CliAuthPage"
|
import { CliAuthenticationPage } from "./pages/CliAuthPage/CliAuthPage"
|
||||||
import { HealthzPage } from "./pages/HealthzPage/HealthzPage"
|
import { HealthzPage } from "./pages/HealthzPage/HealthzPage"
|
||||||
import { LoginPage } from "./pages/LoginPage/LoginPage"
|
import { LoginPage } from "./pages/LoginPage/LoginPage"
|
||||||
import TemplatesPage from "./pages/TemplatesPage/TemplatesPage"
|
import { TemplatesPage } from "./pages/TemplatesPage/TemplatesPage"
|
||||||
import { AccountPage } from "./pages/UserSettingsPage/AccountPage/AccountPage"
|
import { AccountPage } from "./pages/UserSettingsPage/AccountPage/AccountPage"
|
||||||
import { SecurityPage } from "./pages/UserSettingsPage/SecurityPage/SecurityPage"
|
import { SecurityPage } from "./pages/UserSettingsPage/SecurityPage/SecurityPage"
|
||||||
import { SSHKeysPage } from "./pages/UserSettingsPage/SSHKeysPage/SSHKeysPage"
|
import { SSHKeysPage } from "./pages/UserSettingsPage/SSHKeysPage/SSHKeysPage"
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import auditLog from "./auditLog.json"
|
import auditLog from "./auditLog.json"
|
||||||
import common from "./common.json"
|
import common from "./common.json"
|
||||||
import templatePage from "./templatePage.json"
|
import templatePage from "./templatePage.json"
|
||||||
|
import templatesPage from "./templatesPage.json"
|
||||||
import workspacePage from "./workspacePage.json"
|
import workspacePage from "./workspacePage.json"
|
||||||
|
|
||||||
export const en = {
|
export const en = {
|
||||||
@@ -8,4 +9,5 @@ export const en = {
|
|||||||
workspacePage,
|
workspacePage,
|
||||||
auditLog,
|
auditLog,
|
||||||
templatePage,
|
templatePage,
|
||||||
|
templatesPage,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"errors": {
|
||||||
|
"getOrganizationError": "Something went wrong fetching organizations.",
|
||||||
|
"getTemplatesError": "Something went wrong fetching templates."
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,7 +4,7 @@ import * as CreateDayString from "util/createDayString"
|
|||||||
import { MockTemplate } from "../../testHelpers/entities"
|
import { MockTemplate } from "../../testHelpers/entities"
|
||||||
import { history, render } from "../../testHelpers/renderHelpers"
|
import { history, render } from "../../testHelpers/renderHelpers"
|
||||||
import { server } from "../../testHelpers/server"
|
import { server } from "../../testHelpers/server"
|
||||||
import TemplatesPage from "./TemplatesPage"
|
import { TemplatesPage } from "./TemplatesPage"
|
||||||
import { Language } from "./TemplatesPageView"
|
import { Language } from "./TemplatesPageView"
|
||||||
|
|
||||||
describe("TemplatesPage", () => {
|
describe("TemplatesPage", () => {
|
||||||
|
|||||||
@@ -6,10 +6,11 @@ import { XServiceContext } from "../../xServices/StateContext"
|
|||||||
import { templatesMachine } from "../../xServices/templates/templatesXService"
|
import { templatesMachine } from "../../xServices/templates/templatesXService"
|
||||||
import { TemplatesPageView } from "./TemplatesPageView"
|
import { TemplatesPageView } from "./TemplatesPageView"
|
||||||
|
|
||||||
const TemplatesPage: React.FC = () => {
|
export const TemplatesPage: React.FC = () => {
|
||||||
const xServices = useContext(XServiceContext)
|
const xServices = useContext(XServiceContext)
|
||||||
const [authState] = useActor(xServices.authXService)
|
const [authState] = useActor(xServices.authXService)
|
||||||
const [templatesState] = useMachine(templatesMachine)
|
const [templatesState] = useMachine(templatesMachine)
|
||||||
|
const { templates, getOrganizationsError, getTemplatesError } = templatesState.context
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
@@ -17,12 +18,12 @@ const TemplatesPage: React.FC = () => {
|
|||||||
<title>{pageTitle("Templates")}</title>
|
<title>{pageTitle("Templates")}</title>
|
||||||
</Helmet>
|
</Helmet>
|
||||||
<TemplatesPageView
|
<TemplatesPageView
|
||||||
templates={templatesState.context.templates}
|
templates={templates}
|
||||||
canCreateTemplate={authState.context.permissions?.createTemplates}
|
canCreateTemplate={authState.context.permissions?.createTemplates}
|
||||||
loading={templatesState.hasTag("loading")}
|
loading={templatesState.hasTag("loading")}
|
||||||
|
getOrganizationsError={getOrganizationsError}
|
||||||
|
getTemplatesError={getTemplatesError}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default TemplatesPage
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { ComponentMeta, Story } from "@storybook/react"
|
import { ComponentMeta, Story } from "@storybook/react"
|
||||||
import { MockTemplate } from "../../testHelpers/entities"
|
import { makeMockApiError, MockTemplate } from "../../testHelpers/entities"
|
||||||
import { TemplatesPageView, TemplatesPageViewProps } from "./TemplatesPageView"
|
import { TemplatesPageView, TemplatesPageViewProps } from "./TemplatesPageView"
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@@ -49,3 +49,8 @@ EmptyCanCreate.args = {
|
|||||||
|
|
||||||
export const EmptyCannotCreate = Template.bind({})
|
export const EmptyCannotCreate = Template.bind({})
|
||||||
EmptyCannotCreate.args = {}
|
EmptyCannotCreate.args = {}
|
||||||
|
|
||||||
|
export const Error = Template.bind({})
|
||||||
|
Error.args = {
|
||||||
|
getTemplatesError: makeMockApiError({ message: "Something went wrong fetching templates." }),
|
||||||
|
}
|
||||||
|
|||||||
@@ -8,7 +8,9 @@ import TableHead from "@material-ui/core/TableHead"
|
|||||||
import TableRow from "@material-ui/core/TableRow"
|
import TableRow from "@material-ui/core/TableRow"
|
||||||
import KeyboardArrowRight from "@material-ui/icons/KeyboardArrowRight"
|
import KeyboardArrowRight from "@material-ui/icons/KeyboardArrowRight"
|
||||||
import useTheme from "@material-ui/styles/useTheme"
|
import useTheme from "@material-ui/styles/useTheme"
|
||||||
|
import { ErrorSummary } from "components/ErrorSummary/ErrorSummary"
|
||||||
import { FC } from "react"
|
import { FC } from "react"
|
||||||
|
import { useTranslation } from "react-i18next"
|
||||||
import { useNavigate } from "react-router-dom"
|
import { useNavigate } from "react-router-dom"
|
||||||
import { createDayString } from "util/createDayString"
|
import { createDayString } from "util/createDayString"
|
||||||
import { formatTemplateActiveDevelopers } from "util/templates"
|
import { formatTemplateActiveDevelopers } from "util/templates"
|
||||||
@@ -77,12 +79,20 @@ export interface TemplatesPageViewProps {
|
|||||||
loading?: boolean
|
loading?: boolean
|
||||||
canCreateTemplate?: boolean
|
canCreateTemplate?: boolean
|
||||||
templates?: TypesGen.Template[]
|
templates?: TypesGen.Template[]
|
||||||
|
getOrganizationsError?: Error | unknown
|
||||||
|
getTemplatesError?: Error | unknown
|
||||||
}
|
}
|
||||||
|
|
||||||
export const TemplatesPageView: FC<React.PropsWithChildren<TemplatesPageViewProps>> = (props) => {
|
export const TemplatesPageView: FC<React.PropsWithChildren<TemplatesPageViewProps>> = (props) => {
|
||||||
const styles = useStyles()
|
const styles = useStyles()
|
||||||
const navigate = useNavigate()
|
const navigate = useNavigate()
|
||||||
|
const { t } = useTranslation("templatesPage")
|
||||||
const theme: Theme = useTheme()
|
const theme: Theme = useTheme()
|
||||||
|
const empty =
|
||||||
|
!props.loading &&
|
||||||
|
!props.getOrganizationsError &&
|
||||||
|
!props.getTemplatesError &&
|
||||||
|
!props.templates?.length
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Margins>
|
<Margins>
|
||||||
@@ -114,94 +124,110 @@ export const TemplatesPageView: FC<React.PropsWithChildren<TemplatesPageViewProp
|
|||||||
)}
|
)}
|
||||||
</PageHeader>
|
</PageHeader>
|
||||||
|
|
||||||
<TableContainer>
|
{props.getOrganizationsError ? (
|
||||||
<Table>
|
<ErrorSummary
|
||||||
<TableHead>
|
error={props.getOrganizationsError}
|
||||||
<TableRow>
|
defaultMessage={t("errors.getOrganizationsError")}
|
||||||
<TableCell width="50%">{Language.nameLabel}</TableCell>
|
/>
|
||||||
<TableCell width="16%">{Language.usedByLabel}</TableCell>
|
) : props.getTemplatesError ? (
|
||||||
<TableCell width="16%">{Language.lastUpdatedLabel}</TableCell>
|
<ErrorSummary
|
||||||
<TableCell width="16%">{Language.createdByLabel}</TableCell>
|
error={props.getTemplatesError}
|
||||||
<TableCell width="1%"></TableCell>
|
defaultMessage={t("errors.getTemplatesError")}
|
||||||
</TableRow>
|
/>
|
||||||
</TableHead>
|
) : (
|
||||||
<TableBody>
|
<TableContainer>
|
||||||
{props.loading && <TableLoader />}
|
<Table>
|
||||||
{!props.loading && !props.templates?.length && (
|
<TableHead>
|
||||||
<TableRow>
|
<TableRow>
|
||||||
<TableCell colSpan={999}>
|
<TableCell width="50%">{Language.nameLabel}</TableCell>
|
||||||
<EmptyState
|
<TableCell width="16%">{Language.usedByLabel}</TableCell>
|
||||||
message={Language.emptyMessage}
|
<TableCell width="16%">{Language.lastUpdatedLabel}</TableCell>
|
||||||
description={
|
<TableCell width="16%">{Language.createdByLabel}</TableCell>
|
||||||
props.canCreateTemplate
|
<TableCell width="1%"></TableCell>
|
||||||
? Language.emptyDescription
|
|
||||||
: Language.emptyViewNoPerms
|
|
||||||
}
|
|
||||||
descriptionClassName={styles.emptyDescription}
|
|
||||||
cta={<CodeExample code="coder templates init" />}
|
|
||||||
/>
|
|
||||||
</TableCell>
|
|
||||||
</TableRow>
|
</TableRow>
|
||||||
)}
|
</TableHead>
|
||||||
{props.templates?.map((template) => {
|
<TableBody>
|
||||||
const templatePageLink = `/templates/${template.name}`
|
{props.loading && <TableLoader />}
|
||||||
const hasIcon = template.icon && template.icon !== ""
|
|
||||||
|
|
||||||
return (
|
{empty ? (
|
||||||
<TableRow
|
<TableRow>
|
||||||
key={template.id}
|
<TableCell colSpan={999}>
|
||||||
hover
|
<EmptyState
|
||||||
data-testid={`template-${template.id}`}
|
message={Language.emptyMessage}
|
||||||
tabIndex={0}
|
description={
|
||||||
onKeyDown={(event) => {
|
props.canCreateTemplate
|
||||||
if (event.key === "Enter") {
|
? Language.emptyDescription
|
||||||
navigate(templatePageLink)
|
: Language.emptyViewNoPerms
|
||||||
}
|
|
||||||
}}
|
|
||||||
className={styles.clickableTableRow}
|
|
||||||
>
|
|
||||||
<TableCellLink to={templatePageLink}>
|
|
||||||
<AvatarData
|
|
||||||
title={template.name}
|
|
||||||
subtitle={template.description}
|
|
||||||
highlightTitle
|
|
||||||
avatar={
|
|
||||||
hasIcon ? (
|
|
||||||
<div className={styles.templateIconWrapper}>
|
|
||||||
<img alt="" src={template.icon} />
|
|
||||||
</div>
|
|
||||||
) : undefined
|
|
||||||
}
|
}
|
||||||
|
descriptionClassName={styles.emptyDescription}
|
||||||
|
cta={<CodeExample code="coder templates init" />}
|
||||||
/>
|
/>
|
||||||
</TableCellLink>
|
</TableCell>
|
||||||
|
|
||||||
<TableCellLink to={templatePageLink}>
|
|
||||||
<span style={{ color: theme.palette.text.secondary }}>
|
|
||||||
{Language.developerCount(template.active_user_count)}
|
|
||||||
</span>
|
|
||||||
</TableCellLink>
|
|
||||||
|
|
||||||
<TableCellLink data-chromatic="ignore" to={templatePageLink}>
|
|
||||||
<span style={{ color: theme.palette.text.secondary }}>
|
|
||||||
{createDayString(template.updated_at)}
|
|
||||||
</span>
|
|
||||||
</TableCellLink>
|
|
||||||
<TableCellLink to={templatePageLink}>
|
|
||||||
<span style={{ color: theme.palette.text.secondary }}>
|
|
||||||
{template.created_by_name}
|
|
||||||
</span>
|
|
||||||
</TableCellLink>
|
|
||||||
<TableCellLink to={templatePageLink}>
|
|
||||||
<div className={styles.arrowCell}>
|
|
||||||
<KeyboardArrowRight className={styles.arrowRight} />
|
|
||||||
</div>
|
|
||||||
</TableCellLink>
|
|
||||||
</TableRow>
|
</TableRow>
|
||||||
)
|
) : (
|
||||||
})}
|
props.templates?.map((template) => {
|
||||||
</TableBody>
|
const templatePageLink = `/templates/${template.name}`
|
||||||
</Table>
|
const hasIcon = template.icon && template.icon !== ""
|
||||||
</TableContainer>
|
|
||||||
|
return (
|
||||||
|
<TableRow
|
||||||
|
key={template.id}
|
||||||
|
hover
|
||||||
|
data-testid={`template-${template.id}`}
|
||||||
|
tabIndex={0}
|
||||||
|
onKeyDown={(event) => {
|
||||||
|
if (event.key === "Enter") {
|
||||||
|
navigate(templatePageLink)
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
className={styles.clickableTableRow}
|
||||||
|
>
|
||||||
|
<TableCellLink to={templatePageLink}>
|
||||||
|
<AvatarData
|
||||||
|
title={template.name}
|
||||||
|
subtitle={template.description}
|
||||||
|
highlightTitle
|
||||||
|
avatar={
|
||||||
|
hasIcon && (
|
||||||
|
<div className={styles.templateIconWrapper}>
|
||||||
|
<img alt="" src={template.icon} />
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</TableCellLink>
|
||||||
|
|
||||||
|
<TableCellLink to={templatePageLink}>
|
||||||
|
<span style={{ color: theme.palette.text.secondary }}>
|
||||||
|
{Language.developerCount(template.active_user_count)}
|
||||||
|
</span>
|
||||||
|
</TableCellLink>
|
||||||
|
|
||||||
|
<TableCellLink data-chromatic="ignore" to={templatePageLink}>
|
||||||
|
<span style={{ color: theme.palette.text.secondary }}>
|
||||||
|
{createDayString(template.updated_at)}
|
||||||
|
</span>
|
||||||
|
</TableCellLink>
|
||||||
|
|
||||||
|
<TableCellLink to={templatePageLink}>
|
||||||
|
<span style={{ color: theme.palette.text.secondary }}>
|
||||||
|
{template.created_by_name}
|
||||||
|
</span>
|
||||||
|
</TableCellLink>
|
||||||
|
|
||||||
|
<TableCellLink to={templatePageLink}>
|
||||||
|
<div className={styles.arrowCell}>
|
||||||
|
<KeyboardArrowRight className={styles.arrowRight} />
|
||||||
|
</div>
|
||||||
|
</TableCellLink>
|
||||||
|
</TableRow>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
)}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
</TableContainer>
|
||||||
|
)}
|
||||||
</Margins>
|
</Margins>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,99 +6,101 @@ interface TemplatesContext {
|
|||||||
organizations?: TypesGen.Organization[]
|
organizations?: TypesGen.Organization[]
|
||||||
templates?: TypesGen.Template[]
|
templates?: TypesGen.Template[]
|
||||||
canCreateTemplate?: boolean
|
canCreateTemplate?: boolean
|
||||||
permissionsError?: Error | unknown
|
getOrganizationsError?: Error | unknown
|
||||||
organizationsError?: Error | unknown
|
getTemplatesError?: Error | unknown
|
||||||
templatesError?: Error | unknown
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const templatesMachine = createMachine(
|
export const templatesMachine =
|
||||||
{
|
/** @xstate-layout N4IgpgJg5mDOIC5QBcwFsAOAbAhq2AysnmAHQzLICWAdlAPIBOUONVAXnlQPY2wDEEXmVoA3bgGsyFJizadqveEhAZusKopqJQAD0QAWAMwHSATgDsANgCsAJgt2rRuwAYAHBYsAaEAE9EAEZAu1IDMwiIm3dXG0DjdwBfRN9UTFx8IhJyMEpaBmZWDi4lfjBGRm5GUmw8ADMqtBzkWSKFHj4dVXVNDq79BGNTS1sHJxcPL18AhBMjUhtXJaWrOzM7O3cDG2TU9FrM4lRm6joAFX2MuEFhUjFJaVyL9JJlUDUNLX6gpeH1wJcIQs7giVmmiBB5kilgM7iMHmcOxSIDSBzgWWOFFOUGeaIE5Uq1QODUYTQouKub26nz6KgGgV+ULsAOZDhBZjB-kQbhspGWSxC0W2VncSORNG4EDgXVRlIxjzydFa8hKnRUH16vG+gzs4IQwTMYWhjgsRjm9l2KMur3lJ3yFNeXQ1XzpiCsVlcpFW4QMFnisJZeo5UMicQsNjMgRsSL2L0O2SENDATp6Lr0QTcFjCa2cfqsgRNeuC7j5-Ncq3WmwMgUtsptRzIBKqKZpWtd+sz2Y5RjzBYceo2WbNw9hrijZtr1vjqBbmu07cC7iLSWSiSAA */
|
||||||
tsTypes: {} as import("./templatesXService.typegen").Typegen0,
|
createMachine(
|
||||||
schema: {
|
{
|
||||||
context: {} as TemplatesContext,
|
tsTypes: {} as import("./templatesXService.typegen").Typegen0,
|
||||||
services: {} as {
|
schema: {
|
||||||
getOrganizations: {
|
context: {} as TemplatesContext,
|
||||||
data: TypesGen.Organization[]
|
services: {} as {
|
||||||
}
|
getOrganizations: {
|
||||||
getPermissions: {
|
data: TypesGen.Organization[]
|
||||||
data: boolean
|
}
|
||||||
}
|
getTemplates: {
|
||||||
getTemplates: {
|
data: TypesGen.Template[]
|
||||||
data: TypesGen.Template[]
|
}
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
id: "templatesState",
|
|
||||||
initial: "gettingOrganizations",
|
|
||||||
states: {
|
|
||||||
gettingOrganizations: {
|
|
||||||
entry: "clearOrganizationsError",
|
|
||||||
invoke: {
|
|
||||||
src: "getOrganizations",
|
|
||||||
id: "getOrganizations",
|
|
||||||
onDone: [
|
|
||||||
{
|
|
||||||
actions: ["assignOrganizations", "clearOrganizationsError"],
|
|
||||||
target: "gettingTemplates",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
onError: [
|
|
||||||
{
|
|
||||||
actions: "assignOrganizationsError",
|
|
||||||
target: "error",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
tags: "loading",
|
|
||||||
},
|
},
|
||||||
gettingTemplates: {
|
id: "templatesState",
|
||||||
entry: "clearTemplatesError",
|
initial: "gettingOrganizations",
|
||||||
invoke: {
|
states: {
|
||||||
src: "getTemplates",
|
gettingOrganizations: {
|
||||||
id: "getTemplates",
|
entry: "clearGetOrganizationsError",
|
||||||
onDone: {
|
invoke: {
|
||||||
target: "done",
|
src: "getOrganizations",
|
||||||
actions: ["assignTemplates", "clearTemplatesError"],
|
id: "getOrganizations",
|
||||||
},
|
onDone: [
|
||||||
onError: {
|
{
|
||||||
target: "error",
|
actions: ["assignOrganizations"],
|
||||||
actions: "assignTemplatesError",
|
target: "gettingTemplates",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
onError: [
|
||||||
|
{
|
||||||
|
actions: "assignGetOrganizationsError",
|
||||||
|
target: "error",
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
|
tags: "loading",
|
||||||
},
|
},
|
||||||
tags: "loading",
|
gettingTemplates: {
|
||||||
},
|
entry: "clearGetTemplatesError",
|
||||||
done: {},
|
invoke: {
|
||||||
error: {},
|
src: "getTemplates",
|
||||||
},
|
id: "getTemplates",
|
||||||
},
|
onDone: [
|
||||||
{
|
{
|
||||||
actions: {
|
actions: ["assignTemplates"],
|
||||||
assignOrganizations: assign({
|
target: "done",
|
||||||
organizations: (_, event) => event.data,
|
},
|
||||||
}),
|
],
|
||||||
assignOrganizationsError: assign({
|
onError: [
|
||||||
organizationsError: (_, event) => event.data,
|
{
|
||||||
}),
|
actions: "assignGetTemplatesError",
|
||||||
clearOrganizationsError: assign((context) => ({
|
target: "error",
|
||||||
...context,
|
},
|
||||||
organizationsError: undefined,
|
],
|
||||||
})),
|
},
|
||||||
assignTemplates: assign({
|
tags: "loading",
|
||||||
templates: (_, event) => event.data,
|
},
|
||||||
}),
|
done: {},
|
||||||
assignTemplatesError: assign({
|
error: {},
|
||||||
templatesError: (_, event) => event.data,
|
|
||||||
}),
|
|
||||||
clearTemplatesError: (context) => assign({ ...context, getWorkspacesError: undefined }),
|
|
||||||
},
|
|
||||||
services: {
|
|
||||||
getOrganizations: API.getOrganizations,
|
|
||||||
getTemplates: async (context) => {
|
|
||||||
if (!context.organizations || context.organizations.length === 0) {
|
|
||||||
throw new Error("no organizations")
|
|
||||||
}
|
|
||||||
return API.getTemplates(context.organizations[0].id)
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
{
|
||||||
)
|
actions: {
|
||||||
|
assignOrganizations: assign({
|
||||||
|
organizations: (_, event) => event.data,
|
||||||
|
}),
|
||||||
|
assignGetOrganizationsError: assign({
|
||||||
|
getOrganizationsError: (_, event) => event.data,
|
||||||
|
}),
|
||||||
|
clearGetOrganizationsError: assign((context) => ({
|
||||||
|
...context,
|
||||||
|
getOrganizationsError: undefined,
|
||||||
|
})),
|
||||||
|
assignTemplates: assign({
|
||||||
|
templates: (_, event) => event.data,
|
||||||
|
}),
|
||||||
|
assignGetTemplatesError: assign({
|
||||||
|
getTemplatesError: (_, event) => event.data,
|
||||||
|
}),
|
||||||
|
clearGetTemplatesError: (context) => assign({ ...context, getTemplatesError: undefined }),
|
||||||
|
},
|
||||||
|
services: {
|
||||||
|
getOrganizations: API.getOrganizations,
|
||||||
|
getTemplates: async (context) => {
|
||||||
|
if (!context.organizations || context.organizations.length === 0) {
|
||||||
|
throw new Error("no organizations")
|
||||||
|
}
|
||||||
|
return API.getTemplates(context.organizations[0].id)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user