Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

feat: allow admins to create workspaces for other users in UI#4247

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
Kira-Pilot merged 12 commits intomainfromadmin-create-workspaces/kira-pilot
Sep 29, 2022
Merged
Show file tree
Hide file tree
Changes fromall commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 15 additions & 4 deletionssite/src/components/UserAutocomplete/UserAutocomplete.tsx
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -10,11 +10,20 @@ import { ChangeEvent, useEffect, useState } from "react"
import { searchUserMachine } from "xServices/users/searchUserXService"

export type UserAutocompleteProps = {
value?: User
value: User | null
onChange: (user: User | null) => void
label?: string
inputMargin?: "none" | "dense" | "normal"
inputStyles?: string
}

export const UserAutocomplete: React.FC<UserAutocompleteProps> = ({ value, onChange }) => {
export const UserAutocomplete: React.FC<UserAutocompleteProps> = ({
value,
onChange,
label,
inputMargin,
inputStyles,
}) => {
const styles = useStyles()
const [isAutocompleteOpen, setIsAutocompleteOpen] = useState(false)
const [searchState, sendSearch] = useMachine(searchUserMachine)
Expand DownExpand Up@@ -77,9 +86,11 @@ export const UserAutocomplete: React.FC<UserAutocompleteProps> = ({ value, onCha
renderInput={(params) => (
<TextField
{...params}
margin="none"
variant="outlined"
margin={inputMargin ?? "normal"}
label={label ?? undefined}
placeholder="User email or username"
className={inputStyles}
InputProps={{
...params.InputProps,
onChange: handleFilterChange,
Expand DownExpand Up@@ -111,7 +122,7 @@ export const useStyles = makeStyles((theme) => {
},

"& input": {
fontSize:14,
fontSize:16,
padding: `${theme.spacing(0, 0.5, 0, 0.5)} !important`,
},
},
Expand Down
5 changes: 5 additions & 0 deletionssite/src/i18n/en/createWorkspacePage.json
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
{
"templateLabel": "Template",
"nameLabel": "Name",
"ownerLabel": "Workspace Owner"
}
2 changes: 2 additions & 0 deletionssite/src/i18n/en/index.ts
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
import auditLog from "./auditLog.json"
import common from "./common.json"
import createWorkspacePage from "./createWorkspacePage.json"
import templatePage from "./templatePage.json"
import templatesPage from "./templatesPage.json"
import workspacePage from "./workspacePage.json"
Expand All@@ -10,4 +11,5 @@ export const en = {
auditLog,
templatePage,
templatesPage,
createWorkspacePage,
}
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,16 @@
/* eslint-disable @typescript-eslint/no-floating-promises */
import { screen } from "@testing-library/react"
import {fireEvent,screen, waitFor } from "@testing-library/react"
import userEvent from "@testing-library/user-event"
import * as API from "api/api"
import { Language as FooterLanguage } from "../../components/FormFooter/FormFooter"
import { MockTemplate, MockWorkspace } from "../../testHelpers/entities"
import { renderWithAuth } from "../../testHelpers/renderHelpers"
import { Language as FooterLanguage } from "components/FormFooter/FormFooter"
import i18next from "i18next"
import { MockTemplate, MockUser, MockWorkspace, MockWorkspaceRequest } from "testHelpers/entities"
import { renderWithAuth } from "testHelpers/renderHelpers"
import CreateWorkspacePage from "./CreateWorkspacePage"
import { Language } from "./CreateWorkspacePageView"

const { t } = i18next

const nameLabelText = t("nameLabel", { ns: "createWorkspacePage" })

const renderCreateWorkspacePage = () => {
return renderWithAuth(<CreateWorkspacePage />, {
Expand All@@ -22,14 +26,26 @@ describe("CreateWorkspacePage", () => {
expect(element).toBeDefined()
})

it("succeeds", async () => {
it("succeeds with default owner", async () => {
jest.spyOn(API, "getUsers").mockResolvedValueOnce([MockUser])
jest.spyOn(API, "createWorkspace").mockResolvedValueOnce(MockWorkspace)

renderCreateWorkspacePage()

const nameField = await screen.findByLabelText(Language.nameLabel)
userEvent.type(nameField, "test")
const nameField = await screen.findByLabelText(nameLabelText)

// have to use fireEvent b/c userEvent isn't cleaning up properly between tests
fireEvent.change(nameField, {
target: { value: "test" },
})

const submitButton = screen.getByText(FooterLanguage.defaultSubmitLabel)
userEvent.click(submitButton)

await waitFor(() =>
expect(API.createWorkspace).toBeCalledWith(MockUser.organization_ids[0], MockUser.id, {
...MockWorkspaceRequest,
}),
)
})
})
23 changes: 18 additions & 5 deletionssite/src/pages/CreateWorkspacePage/CreateWorkspacePage.tsx
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
import { useMachine } from "@xstate/react"
import { FC } from "react"
import { useActor, useMachine } from "@xstate/react"
import { User } from "api/typesGenerated"
import { useOrganizationId } from "hooks/useOrganizationId"
import { FC, useContext, useState } from "react"
import { Helmet } from "react-helmet-async"
import { useNavigate, useParams } from "react-router-dom"
import {useOrganizationId } from "../../hooks/useOrganizationId"
import {pageTitle } from "../../util/page"
import {createWorkspaceMachine } from "../../xServices/createWorkspace/createWorkspaceXService"
import {pageTitle } from "util/page"
import {createWorkspaceMachine } from "xServices/createWorkspace/createWorkspaceXService"
import {XServiceContext } from "xServices/StateContext"
import { CreateWorkspaceErrors, CreateWorkspacePageView } from "./CreateWorkspacePageView"

const CreateWorkspacePage: FC = () => {
Expand All@@ -28,8 +30,15 @@ const CreateWorkspacePage: FC = () => {
getTemplateSchemaError,
getTemplatesError,
createWorkspaceError,
permissions,
} = createWorkspaceState.context

const xServices = useContext(XServiceContext)
const [authState] = useActor(xServices.authXService)
const { me } = authState.context

const [owner, setOwner] = useState<User | null>(me ?? null)

return (
<>
<Helmet>
Expand All@@ -49,13 +58,17 @@ const CreateWorkspacePage: FC = () => {
[CreateWorkspaceErrors.GET_TEMPLATE_SCHEMA_ERROR]: getTemplateSchemaError,
[CreateWorkspaceErrors.CREATE_WORKSPACE_ERROR]: createWorkspaceError,
}}
canCreateForUser={permissions?.createWorkspaceForUser}
defaultWorkspaceOwner={me ?? null}
setOwner={setOwner}
onCancel={() => {
navigate("/templates")
}}
onSubmit={(request) => {
send({
type: "CREATE_WORKSPACE",
request,
owner,
})
}}
/>
Expand Down
80 changes: 30 additions & 50 deletionssite/src/pages/CreateWorkspacePage/CreateWorkspacePageView.tsx
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,18 @@
import { makeStyles } from "@material-ui/core/styles"
import TextField from "@material-ui/core/TextField"
import * as TypesGen from "api/typesGenerated"
import { ErrorSummary } from "components/ErrorSummary/ErrorSummary"
import { FormFooter } from "components/FormFooter/FormFooter"
import { FullPageForm } from "components/FullPageForm/FullPageForm"
import { Loader } from "components/Loader/Loader"
import { ParameterInput } from "components/ParameterInput/ParameterInput"
import { Stack } from "components/Stack/Stack"
import { UserAutocomplete } from "components/UserAutocomplete/UserAutocomplete"
import { FormikContextType, FormikTouched, useFormik } from "formik"
import { i18n } from "i18n"
import { FC, useState } from "react"
import { useTranslation } from "react-i18next"
import { getFormHelpers, nameValidator, onChangeTrimmed } from "util/formUtils"
import * as Yup from "yup"
import * as TypesGen from "../../api/typesGenerated"
import { FormFooter } from "../../components/FormFooter/FormFooter"
import { FullPageForm } from "../../components/FullPageForm/FullPageForm"
import { Loader } from "../../components/Loader/Loader"
import { ParameterInput } from "../../components/ParameterInput/ParameterInput"
import { Stack } from "../../components/Stack/Stack"
import { getFormHelpers, nameValidator, onChangeTrimmed } from "../../util/formUtils"

export const Language = {
templateLabel: "Template",
nameLabel: "Name",
}

export enum CreateWorkspaceErrors {
GET_TEMPLATES_ERROR = "getTemplatesError",
Expand All@@ -33,21 +30,27 @@ export interface CreateWorkspacePageViewProps {
selectedTemplate?: TypesGen.Template
templateSchema?: TypesGen.ParameterSchema[]
createWorkspaceErrors: Partial<Record<CreateWorkspaceErrors, Error | unknown>>
canCreateForUser?: boolean
defaultWorkspaceOwner: TypesGen.User | null
setOwner: (arg0: TypesGen.User | null) => void
onCancel: () => void
onSubmit: (req: TypesGen.CreateWorkspaceRequest) => void
// initialTouched is only used for testing the error state of the form.
initialTouched?: FormikTouched<TypesGen.CreateWorkspaceRequest>
}

const { t } = i18n

export const validationSchema = Yup.object({
name: nameValidator(Language.nameLabel),
name: nameValidator(t("nameLabel", { ns: "createWorkspacePage" })),
})

export const CreateWorkspacePageView: FC<React.PropsWithChildren<CreateWorkspacePageViewProps>> = (
props,
) => {
const { t } = useTranslation("createWorkspacePage")

const [parameterValues, setParameterValues] = useState<Record<string, string>>({})
useStyles()

const form: FormikContextType<TypesGen.CreateWorkspaceRequest> =
useFormik<TypesGen.CreateWorkspaceRequest>({
Expand DownExpand Up@@ -114,17 +117,15 @@ export const CreateWorkspacePageView: FC<React.PropsWithChildren<CreateWorkspace
<FullPageForm title="Create workspace" onCancel={props.onCancel}>
<form onSubmit={form.handleSubmit}>
<Stack>
{props.createWorkspaceErrors[CreateWorkspaceErrors.CREATE_WORKSPACE_ERROR] ? (
{Boolean(props.createWorkspaceErrors[CreateWorkspaceErrors.CREATE_WORKSPACE_ERROR]) && (
<ErrorSummary
error={props.createWorkspaceErrors[CreateWorkspaceErrors.CREATE_WORKSPACE_ERROR]}
/>
) : (
<></>
)}
<TextField
disabled
fullWidth
label={Language.templateLabel}
label={t("templateLabel")}
value={props.selectedTemplate?.name || props.templateName}
variant="outlined"
/>
Expand All@@ -138,10 +139,19 @@ export const CreateWorkspacePageView: FC<React.PropsWithChildren<CreateWorkspace
onChange={onChangeTrimmed(form)}
autoFocus
fullWidth
label={Language.nameLabel}
label={t("nameLabel")}
variant="outlined"
/>

{props.canCreateForUser && (
<UserAutocomplete
value={props.defaultWorkspaceOwner}
onChange={(user) => props.setOwner(user)}
label={t("ownerLabel")}
inputMargin="dense"
/>
)}

{props.templateSchema.length > 0 && (
<Stack>
{props.templateSchema.map((schema) => (
Expand All@@ -168,33 +178,3 @@ export const CreateWorkspacePageView: FC<React.PropsWithChildren<CreateWorkspace
</FullPageForm>
)
}

const useStyles = makeStyles((theme) => ({
readMoreLink: {
display: "flex",
alignItems: "center",

"& svg": {
width: 12,
height: 12,
marginLeft: theme.spacing(0.5),
},
},
emptyState: {
padding: 0,
fontFamily: "inherit",
textAlign: "left",
minHeight: "auto",
alignItems: "flex-start",
},
emptyStateDescription: {
lineHeight: "160%",
},
code: {
background: theme.palette.background.paper,
width: "100%",
},
codeButton: {
background: theme.palette.background.paper,
},
}))
7 changes: 7 additions & 0 deletionssite/src/testHelpers/entities.ts
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -324,6 +324,13 @@ export const MockQueuedWorkspace: TypesGen.Workspace = {
},
}

// requests the MockWorkspace
export const MockWorkspaceRequest: TypesGen.CreateWorkspaceRequest = {
name: "test",
parameter_values: [],
template_id: "test-template",
}

export const MockWorkspaceApp: TypesGen.WorkspaceApp = {
id: "test-app",
name: "test-app",
Expand Down
Loading

[8]ページ先頭

©2009-2025 Movatter.jp