mirror of
https://github.com/usebruno/bruno.git
synced 2026-07-08 22:45:25 +00:00
@@ -0,0 +1,144 @@
|
||||
import React, { useRef, useEffect, useState } from 'react';
|
||||
import { useFormik } from 'formik';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import * as Yup from 'yup';
|
||||
import toast from 'react-hot-toast';
|
||||
import Modal from 'components/Modal';
|
||||
import { createWorkspaceAction } from 'providers/ReduxStore/slices/workspaces/actions';
|
||||
import { browseDirectory } from 'providers/ReduxStore/slices/collections/actions';
|
||||
import { multiLineMsg } from 'utils/common/index';
|
||||
import { formatIpcError } from 'utils/common/error';
|
||||
|
||||
const CreateWorkspace = ({ onClose }) => {
|
||||
const inputRef = useRef();
|
||||
const dispatch = useDispatch();
|
||||
const workspaces = useSelector((state) => state.workspaces.workspaces);
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
const formik = useFormik({
|
||||
enableReinitialize: true,
|
||||
initialValues: {
|
||||
workspaceName: '',
|
||||
workspaceLocation: ''
|
||||
},
|
||||
validationSchema: Yup.object({
|
||||
workspaceName: Yup.string()
|
||||
.min(1, 'must be at least 1 character')
|
||||
.max(255, 'must be 255 characters or less')
|
||||
.required('workspace name is required')
|
||||
.test('unique-name', 'A workspace with this name already exists', function (value) {
|
||||
if (!value) return true;
|
||||
|
||||
return !workspaces.some((w) =>
|
||||
w.name.toLowerCase() === value.toLowerCase());
|
||||
}),
|
||||
workspaceLocation: Yup.string().min(1, 'location is required').required('location is required')
|
||||
}),
|
||||
onSubmit: async (values) => {
|
||||
if (isSubmitting) return;
|
||||
|
||||
try {
|
||||
setIsSubmitting(true);
|
||||
|
||||
await dispatch(createWorkspaceAction(values.workspaceName, values.workspaceName, values.workspaceLocation));
|
||||
toast.success('Workspace created!');
|
||||
onClose();
|
||||
} catch (error) {
|
||||
toast.error(multiLineMsg('An error occurred while creating the workspace', formatIpcError(error)));
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const browse = () => {
|
||||
dispatch(browseDirectory())
|
||||
.then((dirPath) => {
|
||||
if (typeof dirPath === 'string') {
|
||||
formik.setFieldValue('workspaceLocation', dirPath);
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
formik.setFieldValue('workspaceLocation', '');
|
||||
console.error(error);
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (inputRef && inputRef.current) {
|
||||
inputRef.current.focus();
|
||||
}
|
||||
}, [inputRef]);
|
||||
|
||||
return (
|
||||
<Modal
|
||||
size="md"
|
||||
title="Create Workspace"
|
||||
description="Give your new workspace a name and choose its type to get started."
|
||||
confirmText={isSubmitting ? 'Creating...' : 'Create Workspace'}
|
||||
handleConfirm={formik.handleSubmit}
|
||||
handleCancel={onClose}
|
||||
style="new"
|
||||
confirmDisabled={isSubmitting}
|
||||
>
|
||||
<div>
|
||||
<form className="bruno-form" onSubmit={formik.handleSubmit}>
|
||||
<div className="mb-4">
|
||||
<label htmlFor="workspaceName" className="block font-semibold mb-2">
|
||||
Name
|
||||
</label>
|
||||
<input
|
||||
id="workspace-name"
|
||||
type="text"
|
||||
name="workspaceName"
|
||||
ref={inputRef}
|
||||
className="block textbox w-full"
|
||||
autoComplete="off"
|
||||
autoCorrect="off"
|
||||
autoCapitalize="off"
|
||||
spellCheck="false"
|
||||
onChange={formik.handleChange}
|
||||
value={formik.values.workspaceName || ''}
|
||||
/>
|
||||
{formik.touched.workspaceName && formik.errors.workspaceName ? (
|
||||
<div className="text-red-500 text-sm mt-1">{formik.errors.workspaceName}</div>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
<div className="mb-4">
|
||||
<label htmlFor="workspaceLocation" className="block font-semibold mb-2">
|
||||
Location
|
||||
<span className="ml-1 text-gray-500 text-sm">
|
||||
<svg className="inline w-4 h-4" fill="currentColor" viewBox="0 0 20 20">
|
||||
<path fillRule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z" clipRule="evenodd" />
|
||||
</svg>
|
||||
</span>
|
||||
</label>
|
||||
<div className="flex gap-2">
|
||||
<input
|
||||
id="workspace-location"
|
||||
type="text"
|
||||
name="workspaceLocation"
|
||||
readOnly={true}
|
||||
className="block textbox flex-1 bg-gray-50"
|
||||
autoComplete="off"
|
||||
autoCorrect="off"
|
||||
autoCapitalize="off"
|
||||
spellCheck="false"
|
||||
value={formik.values.workspaceLocation || ''}
|
||||
/>
|
||||
<button type="button" className="btn btn-sm btn-secondary" onClick={browse}>
|
||||
Browse
|
||||
</button>
|
||||
</div>
|
||||
{formik.touched.workspaceLocation && formik.errors.workspaceLocation ? (
|
||||
<div className="text-red-500 text-sm mt-1">{formik.errors.workspaceLocation}</div>
|
||||
) : null}
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default CreateWorkspace;
|
||||
Reference in New Issue
Block a user