all
This commit is contained in:
174
Downloads/Voltaserve/ui/src/pages/new-workspace-page.tsx
Normal file
174
Downloads/Voltaserve/ui/src/pages/new-workspace-page.tsx
Normal file
@ -0,0 +1,174 @@
|
||||
import { useCallback, useState } from 'react'
|
||||
import { Link, useNavigate } from 'react-router-dom'
|
||||
import {
|
||||
Button,
|
||||
FormControl,
|
||||
FormErrorMessage,
|
||||
FormLabel,
|
||||
Heading,
|
||||
Input,
|
||||
} from '@chakra-ui/react'
|
||||
import { useSWRConfig } from 'swr'
|
||||
import {
|
||||
Field,
|
||||
FieldAttributes,
|
||||
FieldProps,
|
||||
Form,
|
||||
Formik,
|
||||
FormikHelpers,
|
||||
} from 'formik'
|
||||
import * as Yup from 'yup'
|
||||
import cx from 'classnames'
|
||||
import { Helmet } from 'react-helmet-async'
|
||||
import WorkspaceAPI from '@/client/api/workspace'
|
||||
import OrganizationSelector from '@/components/common/organization-selector'
|
||||
import StorageInput from '@/components/common/storage-input'
|
||||
import { gigabyteToByte } from '@/helpers/convert-storage'
|
||||
|
||||
type FormValues = {
|
||||
name: string
|
||||
organizationId: string
|
||||
storageCapacity: number
|
||||
}
|
||||
|
||||
const NewWorkspacePage = () => {
|
||||
const navigate = useNavigate()
|
||||
const { mutate } = useSWRConfig()
|
||||
const [isLoading, setIsLoading] = useState(false)
|
||||
const formSchema = Yup.object().shape({
|
||||
name: Yup.string().required('Name is required').max(255),
|
||||
organizationId: Yup.string().required('Organization is required'),
|
||||
storageCapacity: Yup.number()
|
||||
.required('Storage capacity is required')
|
||||
.positive()
|
||||
.integer()
|
||||
.min(1, 'Invalid storage usage value'),
|
||||
})
|
||||
|
||||
const handleSubmit = useCallback(
|
||||
async (
|
||||
{ name, organizationId, storageCapacity }: FormValues,
|
||||
{ setSubmitting }: FormikHelpers<FormValues>,
|
||||
) => {
|
||||
setSubmitting(true)
|
||||
setIsLoading(true)
|
||||
try {
|
||||
const result = await WorkspaceAPI.create({
|
||||
name,
|
||||
organizationId,
|
||||
storageCapacity,
|
||||
})
|
||||
mutate(`/workspaces/${result.id}`, result)
|
||||
mutate(`/workspaces`)
|
||||
setSubmitting(false)
|
||||
navigate(`/workspace/${result.id}/file/${result.rootId}`)
|
||||
} catch (e) {
|
||||
setIsLoading(false)
|
||||
} finally {
|
||||
setSubmitting(false)
|
||||
}
|
||||
},
|
||||
[navigate, mutate],
|
||||
)
|
||||
|
||||
return (
|
||||
<>
|
||||
<Helmet>
|
||||
<title>New Workspace</title>
|
||||
</Helmet>
|
||||
<div className={cx('flex', 'flex-col', 'gap-3.5')}>
|
||||
<Heading className={cx('text-heading')}>New Workspace</Heading>
|
||||
<Formik
|
||||
enableReinitialize={true}
|
||||
initialValues={{
|
||||
name: '',
|
||||
organizationId: '',
|
||||
storageCapacity: gigabyteToByte(100),
|
||||
}}
|
||||
validationSchema={formSchema}
|
||||
validateOnBlur={false}
|
||||
onSubmit={handleSubmit}
|
||||
>
|
||||
{({ errors, touched, isSubmitting, setFieldValue }) => (
|
||||
<Form>
|
||||
<div className={cx('flex', 'flex-col', 'gap-3.5')}>
|
||||
<div className={cx('flex', 'flex-col', 'gap-1.5')}>
|
||||
<Field name="name">
|
||||
{({ field }: FieldAttributes<FieldProps>) => (
|
||||
<FormControl
|
||||
maxW="400px"
|
||||
isInvalid={errors.name && touched.name ? true : false}
|
||||
>
|
||||
<FormLabel>Name</FormLabel>
|
||||
<Input {...field} disabled={isSubmitting} autoFocus />
|
||||
<FormErrorMessage>{errors.name}</FormErrorMessage>
|
||||
</FormControl>
|
||||
)}
|
||||
</Field>
|
||||
<Field name="organizationId">
|
||||
{({ field }: FieldAttributes<FieldProps>) => (
|
||||
<FormControl
|
||||
maxW="400px"
|
||||
isInvalid={
|
||||
errors.organizationId && touched.organizationId
|
||||
? true
|
||||
: false
|
||||
}
|
||||
>
|
||||
<FormLabel>Organization</FormLabel>
|
||||
<OrganizationSelector
|
||||
onConfirm={(value) =>
|
||||
setFieldValue(field.name, value.id)
|
||||
}
|
||||
/>
|
||||
<FormErrorMessage>
|
||||
{errors.organizationId}
|
||||
</FormErrorMessage>
|
||||
</FormControl>
|
||||
)}
|
||||
</Field>
|
||||
<Field name="storageCapacity">
|
||||
{(props: FieldAttributes<FieldProps>) => (
|
||||
<FormControl
|
||||
maxW="400px"
|
||||
isInvalid={
|
||||
errors.storageCapacity && touched.storageCapacity
|
||||
? true
|
||||
: false
|
||||
}
|
||||
>
|
||||
<FormLabel>Storage capacity</FormLabel>
|
||||
<StorageInput {...props} />
|
||||
<FormErrorMessage>
|
||||
{errors.storageCapacity}
|
||||
</FormErrorMessage>
|
||||
</FormControl>
|
||||
)}
|
||||
</Field>
|
||||
</div>
|
||||
<div
|
||||
className={cx('flex', 'flex-row', 'items-center', 'gap-1')}
|
||||
>
|
||||
<Button
|
||||
type="submit"
|
||||
variant="solid"
|
||||
colorScheme="blue"
|
||||
isDisabled={isSubmitting || isLoading}
|
||||
isLoading={isSubmitting || isLoading}
|
||||
>
|
||||
Create
|
||||
</Button>
|
||||
<Button as={Link} to="/workspace" variant="solid">
|
||||
Cancel
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Form>
|
||||
)}
|
||||
</Formik>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default NewWorkspacePage
|
Reference in New Issue
Block a user