mirror of
https://github.com/coder/coder.git
synced 2025-07-12 00:14:10 +00:00
Switch to using creation mode in XState
still problems in tests
This commit is contained in:
@ -1,6 +1,6 @@
|
|||||||
import { useActor } from "@xstate/react"
|
import { useActor } from "@xstate/react"
|
||||||
import React, { useContext } from "react"
|
import React, { useContext } from "react"
|
||||||
import { useNavigate } from "react-router"
|
import { Navigate } from "react-router"
|
||||||
import { CreateUserRequest } from "../../../api/typesGenerated"
|
import { CreateUserRequest } from "../../../api/typesGenerated"
|
||||||
import { CreateUserForm } from "../../../components/CreateUserForm/CreateUserForm"
|
import { CreateUserForm } from "../../../components/CreateUserForm/CreateUserForm"
|
||||||
import { XServiceContext } from "../../../xServices/StateContext"
|
import { XServiceContext } from "../../../xServices/StateContext"
|
||||||
@ -13,17 +13,19 @@ export const CreateUserPage = () => {
|
|||||||
const xServices = useContext(XServiceContext)
|
const xServices = useContext(XServiceContext)
|
||||||
const [usersState, usersSend] = useActor(xServices.usersXService)
|
const [usersState, usersSend] = useActor(xServices.usersXService)
|
||||||
const { createUserError, createUserFormErrors } = usersState.context
|
const { createUserError, createUserFormErrors } = usersState.context
|
||||||
const navigate = useNavigate()
|
|
||||||
// There is no field for organization id in Community Edition, so handle its field error like a generic error
|
// There is no field for organization id in Community Edition, so handle its field error like a generic error
|
||||||
const genericError = (createUserError || createUserFormErrors?.organization_id) ? Language.unknownError : undefined
|
const genericError = createUserError || createUserFormErrors?.organization_id ? Language.unknownError : undefined
|
||||||
|
|
||||||
return (
|
if (usersState.matches("creationMode")){
|
||||||
<CreateUserForm
|
return <CreateUserForm
|
||||||
formErrors={createUserFormErrors}
|
formErrors={createUserFormErrors}
|
||||||
onSubmit={(user: CreateUserRequest) => usersSend({ type: "CREATE", user })}
|
onSubmit={(user: CreateUserRequest) => usersSend({ type: "CREATE", user })}
|
||||||
onCancel={() => navigate("/users")}
|
onCancel={() => {usersSend("EXIT_CREATION_MODE")}}
|
||||||
isLoading={usersState.hasTag("loading")}
|
isLoading={usersState.hasTag("loading")}
|
||||||
error={genericError}
|
error={genericError}
|
||||||
/>
|
/>
|
||||||
)
|
} else {
|
||||||
|
// on cancel or success, redirect
|
||||||
|
return <Navigate to="/users"/>
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
import { useActor } from "@xstate/react"
|
import { useActor } from "@xstate/react"
|
||||||
import React, { useContext, useEffect } from "react"
|
import React, { useContext, useEffect } from "react"
|
||||||
import { useNavigate } from "react-router"
|
import { Navigate } from "react-router"
|
||||||
import { ErrorSummary } from "../../components/ErrorSummary/ErrorSummary"
|
import { ErrorSummary } from "../../components/ErrorSummary/ErrorSummary"
|
||||||
import { XServiceContext } from "../../xServices/StateContext"
|
import { XServiceContext } from "../../xServices/StateContext"
|
||||||
import { UsersPageView } from "./UsersPageView"
|
import { UsersPageView } from "./UsersPageView"
|
||||||
@ -9,7 +9,6 @@ export const UsersPage: React.FC = () => {
|
|||||||
const xServices = useContext(XServiceContext)
|
const xServices = useContext(XServiceContext)
|
||||||
const [usersState, usersSend] = useActor(xServices.usersXService)
|
const [usersState, usersSend] = useActor(xServices.usersXService)
|
||||||
const { users, pager, getUsersError } = usersState.context
|
const { users, pager, getUsersError } = usersState.context
|
||||||
const navigate = useNavigate()
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fetch users on component mount
|
* Fetch users on component mount
|
||||||
@ -20,13 +19,16 @@ export const UsersPage: React.FC = () => {
|
|||||||
|
|
||||||
if (usersState.matches("error")) {
|
if (usersState.matches("error")) {
|
||||||
return <ErrorSummary error={getUsersError} />
|
return <ErrorSummary error={getUsersError} />
|
||||||
|
} else if (usersState.matches("creationMode")) {
|
||||||
|
console.log("loop")
|
||||||
|
return <Navigate to="/users/create" />
|
||||||
} else {
|
} else {
|
||||||
return (
|
return (
|
||||||
<UsersPageView
|
<UsersPageView
|
||||||
users={users}
|
users={users}
|
||||||
pager={pager}
|
pager={pager}
|
||||||
openUserCreationDialog={() => {
|
openUserCreationDialog={() => {
|
||||||
navigate("/users/create")
|
usersSend("ENTER_CREATION_MODE")
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
@ -3,6 +3,7 @@ import { render as wrappedRender, RenderResult } from "@testing-library/react"
|
|||||||
import { createMemoryHistory } from "history"
|
import { createMemoryHistory } from "history"
|
||||||
import React from "react"
|
import React from "react"
|
||||||
import { MemoryRouter, Route, Routes, unstable_HistoryRouter as HistoryRouter } from "react-router-dom"
|
import { MemoryRouter, Route, Routes, unstable_HistoryRouter as HistoryRouter } from "react-router-dom"
|
||||||
|
import { AppRouter } from "../AppRouter"
|
||||||
import { RequireAuth } from "../components/RequireAuth/RequireAuth"
|
import { RequireAuth } from "../components/RequireAuth/RequireAuth"
|
||||||
import { dark } from "../theme"
|
import { dark } from "../theme"
|
||||||
import { XServiceProvider } from "../xServices/StateContext"
|
import { XServiceProvider } from "../xServices/StateContext"
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import { useInterpret } from "@xstate/react"
|
import { useInterpret } from "@xstate/react"
|
||||||
import React, { createContext } from "react"
|
import React, { createContext } from "react"
|
||||||
import { useNavigate } from "react-router"
|
|
||||||
import { ActorRefFrom } from "xstate"
|
import { ActorRefFrom } from "xstate"
|
||||||
import { authMachine } from "./auth/authXService"
|
import { authMachine } from "./auth/authXService"
|
||||||
import { buildInfoMachine } from "./buildInfo/buildInfoXService"
|
import { buildInfoMachine } from "./buildInfo/buildInfoXService"
|
||||||
@ -23,13 +22,12 @@ interface XServiceContextType {
|
|||||||
export const XServiceContext = createContext({} as XServiceContextType)
|
export const XServiceContext = createContext({} as XServiceContextType)
|
||||||
|
|
||||||
export const XServiceProvider: React.FC = ({ children }) => {
|
export const XServiceProvider: React.FC = ({ children }) => {
|
||||||
const navigate = useNavigate()
|
|
||||||
return (
|
return (
|
||||||
<XServiceContext.Provider
|
<XServiceContext.Provider
|
||||||
value={{
|
value={{
|
||||||
authXService: useInterpret(authMachine),
|
authXService: useInterpret(authMachine),
|
||||||
buildInfoXService: useInterpret(buildInfoMachine),
|
buildInfoXService: useInterpret(buildInfoMachine),
|
||||||
usersXService: useInterpret(usersMachine.withContext({ users: [], navigate })),
|
usersXService: useInterpret(usersMachine)
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{children}
|
{children}
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
import { NavigateFunction } from "react-router"
|
|
||||||
import { assign, createMachine } from "xstate"
|
import { assign, createMachine } from "xstate"
|
||||||
import * as API from "../../api"
|
import * as API from "../../api"
|
||||||
import { ApiError, FieldErrors, isApiError, mapApiErrorToFieldErrors } from "../../api/errors"
|
import { ApiError, FieldErrors, isApiError, mapApiErrorToFieldErrors } from "../../api/errors"
|
||||||
@ -16,10 +15,13 @@ export interface UsersContext {
|
|||||||
getUsersError?: Error | unknown
|
getUsersError?: Error | unknown
|
||||||
createUserError?: Error | unknown
|
createUserError?: Error | unknown
|
||||||
createUserFormErrors?: FieldErrors
|
createUserFormErrors?: FieldErrors
|
||||||
navigate?: NavigateFunction
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type UsersEvent = { type: "GET_USERS" } | { type: "CREATE"; user: TypesGen.CreateUserRequest }
|
export type UsersEvent =
|
||||||
|
| { type: "GET_USERS" }
|
||||||
|
| { type: "ENTER_CREATION_MODE" }
|
||||||
|
| { type: "EXIT_CREATION_MODE" }
|
||||||
|
| { type: "CREATE"; user: TypesGen.CreateUserRequest }
|
||||||
|
|
||||||
export const usersMachine = createMachine(
|
export const usersMachine = createMachine(
|
||||||
{
|
{
|
||||||
@ -45,7 +47,7 @@ export const usersMachine = createMachine(
|
|||||||
idle: {
|
idle: {
|
||||||
on: {
|
on: {
|
||||||
GET_USERS: "gettingUsers",
|
GET_USERS: "gettingUsers",
|
||||||
CREATE: "creatingUser",
|
ENTER_CREATION_MODE: "creationMode",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
gettingUsers: {
|
gettingUsers: {
|
||||||
@ -67,28 +69,40 @@ export const usersMachine = createMachine(
|
|||||||
},
|
},
|
||||||
tags: "loading",
|
tags: "loading",
|
||||||
},
|
},
|
||||||
creatingUser: {
|
creationMode: {
|
||||||
invoke: {
|
initial: "idle",
|
||||||
src: "createUser",
|
states: {
|
||||||
id: "createUser",
|
idle: {
|
||||||
onDone: {
|
on: {
|
||||||
target: "idle",
|
CREATE: "creatingUser",
|
||||||
actions: ["displayCreateUserSuccess", "redirectToUsersPage", "clearCreateUserError"],
|
EXIT_CREATION_MODE: "#usersState.idle"
|
||||||
|
},
|
||||||
},
|
},
|
||||||
onError: [
|
creatingUser: {
|
||||||
{
|
invoke: {
|
||||||
target: "idle",
|
src: "createUser",
|
||||||
cond: "isFormError",
|
id: "createUser",
|
||||||
actions: ["assignCreateUserFormErrors"],
|
onDone: {
|
||||||
|
target: "#usersState.idle",
|
||||||
|
actions: ["displayCreateUserSuccess", "clearCreateUserError"],
|
||||||
|
},
|
||||||
|
onError: [
|
||||||
|
{
|
||||||
|
target: "idle",
|
||||||
|
cond: "isFormError",
|
||||||
|
actions: ["assignCreateUserFormErrors"],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
target: "idle",
|
||||||
|
actions: ["assignCreateUserError"],
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
{
|
tags: "loading",
|
||||||
target: "idle",
|
},
|
||||||
actions: ["assignCreateUserError"],
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
},
|
||||||
tags: "loading",
|
|
||||||
},
|
},
|
||||||
|
|
||||||
error: {
|
error: {
|
||||||
on: {
|
on: {
|
||||||
GET_USERS: "gettingUsers",
|
GET_USERS: "gettingUsers",
|
||||||
@ -102,7 +116,7 @@ export const usersMachine = createMachine(
|
|||||||
createUser: (_, event) => API.createUser(event.user),
|
createUser: (_, event) => API.createUser(event.user),
|
||||||
},
|
},
|
||||||
guards: {
|
guards: {
|
||||||
isFormError: (_, event) => isApiError(event.data)
|
isFormError: (_, event) => isApiError(event.data),
|
||||||
},
|
},
|
||||||
actions: {
|
actions: {
|
||||||
assignUsers: assign({
|
assignUsers: assign({
|
||||||
@ -121,7 +135,7 @@ export const usersMachine = createMachine(
|
|||||||
}),
|
}),
|
||||||
assignCreateUserFormErrors: assign({
|
assignCreateUserFormErrors: assign({
|
||||||
// the guard ensures it is ApiError
|
// the guard ensures it is ApiError
|
||||||
createUserFormErrors: (_, event) => mapApiErrorToFieldErrors((event.data as ApiError).response.data)
|
createUserFormErrors: (_, event) => mapApiErrorToFieldErrors((event.data as ApiError).response.data),
|
||||||
}),
|
}),
|
||||||
clearCreateUserError: assign((context: UsersContext) => ({
|
clearCreateUserError: assign((context: UsersContext) => ({
|
||||||
...context,
|
...context,
|
||||||
@ -130,9 +144,6 @@ export const usersMachine = createMachine(
|
|||||||
displayCreateUserSuccess: () => {
|
displayCreateUserSuccess: () => {
|
||||||
displaySuccess(Language.createUserSuccess)
|
displaySuccess(Language.createUserSuccess)
|
||||||
},
|
},
|
||||||
redirectToUsersPage: (context) => {
|
|
||||||
context.navigate && context.navigate("/users")
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
Reference in New Issue
Block a user