mirror of
https://github.com/usebruno/bruno.git
synced 2024-11-25 09:23:17 +01:00
feat: environment configuration (resolves #8)
This commit is contained in:
parent
008704c4e1
commit
4a5378a2e1
@ -0,0 +1,70 @@
|
|||||||
|
import React, { useEffect, useRef } from 'react';
|
||||||
|
import Portal from "components/Portal/index";
|
||||||
|
import Modal from "components/Modal/index";
|
||||||
|
import { useFormik } from 'formik';
|
||||||
|
import { addWorkspace } from 'providers/ReduxStore/slices/workspaces';
|
||||||
|
import * as Yup from 'yup';
|
||||||
|
import { useDispatch } from 'react-redux';
|
||||||
|
|
||||||
|
const CreateEnvironment = ({onClose}) => {
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
const inputRef = useRef();
|
||||||
|
const formik = useFormik({
|
||||||
|
enableReinitialize: true,
|
||||||
|
initialValues: {
|
||||||
|
name: ""
|
||||||
|
},
|
||||||
|
validationSchema: Yup.object({
|
||||||
|
name: Yup.string()
|
||||||
|
.min(1, 'must be atleast 1 characters')
|
||||||
|
.max(30, 'must be 30 characters or less')
|
||||||
|
.required('name is required')
|
||||||
|
}),
|
||||||
|
onSubmit: (values) => {
|
||||||
|
// dispatch(addWorkspace({name: values.name}));
|
||||||
|
// onClose();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if(inputRef && inputRef.current) {
|
||||||
|
inputRef.current.focus();
|
||||||
|
}
|
||||||
|
}, [inputRef]);
|
||||||
|
|
||||||
|
const onSubmit = () => {
|
||||||
|
formik.handleSubmit();
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Portal>
|
||||||
|
<Modal
|
||||||
|
size="sm"
|
||||||
|
title={"Create Environment"}
|
||||||
|
confirmText='Create'
|
||||||
|
handleConfirm={onSubmit}
|
||||||
|
handleCancel={onClose}
|
||||||
|
>
|
||||||
|
<form className="bruno-form" onSubmit={formik.handleSubmit}>
|
||||||
|
<div>
|
||||||
|
<label htmlFor="name" className="block font-semibold">Environment Name</label>
|
||||||
|
<input
|
||||||
|
id="environment-name" type="text" name="name"
|
||||||
|
ref={inputRef}
|
||||||
|
className="block textbox mt-2 w-full"
|
||||||
|
autoComplete="off" autoCorrect="off" autoCapitalize="off" spellCheck="false"
|
||||||
|
onChange={formik.handleChange}
|
||||||
|
value={formik.values.name || ''}
|
||||||
|
/>
|
||||||
|
{formik.touched.name && formik.errors.name ? (
|
||||||
|
<div className="text-red-500">{formik.errors.name}</div>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</Modal>
|
||||||
|
</Portal>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default CreateEnvironment;
|
||||||
|
|
@ -0,0 +1,15 @@
|
|||||||
|
import styled from 'styled-components';
|
||||||
|
|
||||||
|
const Wrapper = styled.div`
|
||||||
|
button.submit {
|
||||||
|
color: white;
|
||||||
|
background-color: var(--color-background-danger) !important;
|
||||||
|
border: inherit !important;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
border: inherit !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export default Wrapper;
|
@ -0,0 +1,34 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import Portal from "components/Portal/index";
|
||||||
|
import Modal from "components/Modal/index";
|
||||||
|
// import { deleteWorkspace } from 'providers/ReduxStore/slices/workspaces';
|
||||||
|
import { useDispatch } from 'react-redux';
|
||||||
|
import StyledWrapper from './StyledWrapper';
|
||||||
|
|
||||||
|
const DeleteEnvironment = ({onClose, environment}) => {
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
const onConfirm = () =>{
|
||||||
|
// dispatch(deleteWorkspace({workspaceUid: workspace.uid}))
|
||||||
|
onClose();
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Portal>
|
||||||
|
<StyledWrapper>
|
||||||
|
<Modal
|
||||||
|
size="sm"
|
||||||
|
title={"Delete Environment"}
|
||||||
|
confirmText="Delete"
|
||||||
|
handleConfirm={onConfirm}
|
||||||
|
handleCancel={onClose}
|
||||||
|
>
|
||||||
|
Are you sure you want to delete <span className="font-semibold">{environment.name}</span> ?
|
||||||
|
</Modal>
|
||||||
|
</StyledWrapper>
|
||||||
|
</Portal>
|
||||||
|
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default DeleteEnvironment;
|
||||||
|
|
@ -0,0 +1,25 @@
|
|||||||
|
import React, { useEffect, useState } from "react";
|
||||||
|
import { IconEdit, IconTrash } from "@tabler/icons";
|
||||||
|
import RenameEnvironment from "../../RenameEnvironment";
|
||||||
|
import DeleteEnvironment from "../../DeleteEnvironment";
|
||||||
|
// import StyledWrapper from "./StyledWrapper";
|
||||||
|
|
||||||
|
const EnvironmentDetails = ({selected}) => {
|
||||||
|
const [ openEditModal, setOpenEditModal] = useState(false);
|
||||||
|
const [ openDeleteModal, setOpenDeleteModal] = useState(false);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="ml-10 flex-grow flex pt-4" style={{maxWidth: '700px'}}>
|
||||||
|
<span>{selected.name}</span>
|
||||||
|
<div className="flex gap-x-4 pl-4" >
|
||||||
|
<IconEdit className="cursor-pointer" size={20} strokeWidth={1.5} onClick={() => setOpenEditModal(true)}/>
|
||||||
|
<IconTrash className="cursor-pointer" size={20} strokeWidth={1.5} onClick={() => setOpenDeleteModal(true)}/>
|
||||||
|
</div>
|
||||||
|
{openEditModal && <RenameEnvironment onClose={() => setOpenEditModal(false)} environment={selected} />}
|
||||||
|
{openDeleteModal && <DeleteEnvironment onClose={() => setOpenDeleteModal(false)} environment={selected} />}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default EnvironmentDetails;
|
@ -0,0 +1,43 @@
|
|||||||
|
import styled from "styled-components";
|
||||||
|
|
||||||
|
const StyledWrapper = styled.div`
|
||||||
|
margin-left: -1rem;
|
||||||
|
margin-block: -1.5rem;
|
||||||
|
.environments-sidebar {
|
||||||
|
margin-bottom: 8px;
|
||||||
|
background-color: #ffffff;
|
||||||
|
min-height: 300px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.environment-item {
|
||||||
|
min-width: 150px;
|
||||||
|
display: block;
|
||||||
|
position: relative;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 8px 10px;
|
||||||
|
color: rgb(35, 35, 35);
|
||||||
|
border-bottom: 1px solid #eaecef;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
text-decoration: none;
|
||||||
|
background-color: #f6f8fa;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.active {
|
||||||
|
background-color: #e1e4e8;
|
||||||
|
&:hover {
|
||||||
|
text-decoration: none;
|
||||||
|
background-color: #e1e4e8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.create-env {
|
||||||
|
padding: 8px 10px;
|
||||||
|
cursor: pointer;
|
||||||
|
border-bottom: none;
|
||||||
|
color: var(--color-text-link);
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export default StyledWrapper;
|
@ -0,0 +1,40 @@
|
|||||||
|
import React, { useEffect, useState, forwardRef, useRef } from "react";
|
||||||
|
import EnvironmentDetails from "./EnvironmentDetails";
|
||||||
|
import CreateEnvironment from "../CreateEnvironment/index";
|
||||||
|
import StyledWrapper from "./StyledWrapper";
|
||||||
|
|
||||||
|
const environments = [
|
||||||
|
{name: "My env", uid: 123},
|
||||||
|
{name: "hjdgfh dj", uid: 3876},
|
||||||
|
{name: "hjdgfer dj", uid: 4678},
|
||||||
|
];
|
||||||
|
|
||||||
|
const Layout = () => {
|
||||||
|
const [selectedEnvironment, setSelectedEnvironment] = useState({});
|
||||||
|
const [openCreateModal, setOpenCreateModal] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setSelectedEnvironment(environments[0]);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<StyledWrapper>
|
||||||
|
<div className="flex">
|
||||||
|
<div style={{borderRight: "1px solid #ccc"}}>
|
||||||
|
<div className="environments-sidebar">
|
||||||
|
{environments && environments.length && environments.map((env) => (
|
||||||
|
<div className={selectedEnvironment.uid === env.uid ? "environment-item active": "environment-item"} onClick={() => setSelectedEnvironment(env)}>
|
||||||
|
<span>{env.name}</span>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
<p className="create-env" onClick={() => setOpenCreateModal(true)}> + Create</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<EnvironmentDetails selected={selectedEnvironment}/>
|
||||||
|
</div>
|
||||||
|
{openCreateModal && <CreateEnvironment onClose={() => setOpenCreateModal(false)}/>}
|
||||||
|
</StyledWrapper>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Layout;
|
@ -0,0 +1,70 @@
|
|||||||
|
import React, { useEffect, useRef } from 'react';
|
||||||
|
import Portal from "components/Portal/index";
|
||||||
|
import Modal from "components/Modal/index";
|
||||||
|
import { useFormik } from 'formik';
|
||||||
|
// import { rename } from 'providers/ReduxStore/slices/environments';
|
||||||
|
import * as Yup from 'yup';
|
||||||
|
import { useDispatch } from 'react-redux';
|
||||||
|
|
||||||
|
const RenameEnvironment = ({onClose, environment}) => {
|
||||||
|
// const dispatch = useDispatch();
|
||||||
|
const inputRef = useRef();
|
||||||
|
const formik = useFormik({
|
||||||
|
enableReinitialize: true,
|
||||||
|
initialValues: {
|
||||||
|
name: environment.name
|
||||||
|
},
|
||||||
|
validationSchema: Yup.object({
|
||||||
|
name: Yup.string()
|
||||||
|
.min(1, 'must be atleast 1 characters')
|
||||||
|
.max(30, 'must be 30 characters or less')
|
||||||
|
.required('name is required')
|
||||||
|
}),
|
||||||
|
onSubmit: (values) => {
|
||||||
|
// dispatch(rename({name: values.name, uid: environment.uid}));
|
||||||
|
onClose();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if(inputRef && inputRef.current) {
|
||||||
|
inputRef.current.focus();
|
||||||
|
}
|
||||||
|
}, [inputRef]);
|
||||||
|
|
||||||
|
const onSubmit = () => {
|
||||||
|
formik.handleSubmit();
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Portal>
|
||||||
|
<Modal
|
||||||
|
size="sm"
|
||||||
|
title={"Rename Environment"}
|
||||||
|
confirmText='Rename'
|
||||||
|
handleConfirm={onSubmit}
|
||||||
|
handleCancel={onClose}
|
||||||
|
>
|
||||||
|
<form className="bruno-form" onSubmit={formik.handleSubmit}>
|
||||||
|
<div>
|
||||||
|
<label htmlFor="name" className="block font-semibold">Environment Name</label>
|
||||||
|
<input
|
||||||
|
id="environment-name" type="text" name="name"
|
||||||
|
ref={inputRef}
|
||||||
|
className="block textbox mt-2 w-full"
|
||||||
|
autoComplete="off" autoCorrect="off" autoCapitalize="off" spellCheck="false"
|
||||||
|
onChange={formik.handleChange}
|
||||||
|
value={formik.values.name || ''}
|
||||||
|
/>
|
||||||
|
{formik.touched.name && formik.errors.name ? (
|
||||||
|
<div className="text-red-500">{formik.errors.name}</div>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</Modal>
|
||||||
|
</Portal>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default RenameEnvironment;
|
||||||
|
|
@ -0,0 +1,43 @@
|
|||||||
|
import Modal from "components/Modal/index";
|
||||||
|
import React, { useState } from "react";
|
||||||
|
import CreateEnvironment from "./CreateEnvironment";
|
||||||
|
import Layout from "./Layout";
|
||||||
|
|
||||||
|
const EnvironmentSettings = ({onClose}) => {
|
||||||
|
const environments = [
|
||||||
|
{name: "My env", uid: 123},
|
||||||
|
{name: "hjdgfh dj", uid: 3876},
|
||||||
|
];
|
||||||
|
const [openCreateModal, setOpenCreateModal] = useState(false)
|
||||||
|
|
||||||
|
if(!environments.length) {
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
size="lg"
|
||||||
|
title="Environment"
|
||||||
|
confirmText={"Close"}
|
||||||
|
handleConfirm={onClose}
|
||||||
|
hideCancel={true}
|
||||||
|
>
|
||||||
|
<p>No environment found!</p>
|
||||||
|
<button className="btn-add-param text-link pr-2 py-3 mt-2 select-none" onClick={() => setOpenCreateModal(true)}>+ Create Environment</button>
|
||||||
|
{openCreateModal && <CreateEnvironment onClose={() => setOpenCreateModal(false)}/>}
|
||||||
|
</Modal>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
size="lg"
|
||||||
|
title="Environment"
|
||||||
|
confirmText={"Close"}
|
||||||
|
handleCancel={onClose}
|
||||||
|
hideFooter={true}
|
||||||
|
>
|
||||||
|
<Layout />
|
||||||
|
</Modal>
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export default EnvironmentSettings;
|
@ -1,10 +1,12 @@
|
|||||||
import React, { useRef, forwardRef } from 'react';
|
import React, { useRef, forwardRef, useState } from 'react';
|
||||||
import Dropdown from 'components/Dropdown';
|
import Dropdown from 'components/Dropdown';
|
||||||
import { IconAdjustmentsHorizontal, IconCaretDown } from '@tabler/icons';
|
import { IconAdjustmentsHorizontal, IconCaretDown } from '@tabler/icons';
|
||||||
|
import EnvironmentSettings from "./EnvironmentSettings";
|
||||||
import StyledWrapper from './StyledWrapper';
|
import StyledWrapper from './StyledWrapper';
|
||||||
|
|
||||||
const EnvironmentSelector = () => {
|
const EnvironmentSelector = () => {
|
||||||
const dropdownTippyRef = useRef();
|
const dropdownTippyRef = useRef();
|
||||||
|
const [openSettingsModal, setOpenSettingsModal] = useState(false);
|
||||||
|
|
||||||
const Icon = forwardRef((props, ref) => {
|
const Icon = forwardRef((props, ref) => {
|
||||||
return (
|
return (
|
||||||
@ -36,9 +38,7 @@ const EnvironmentSelector = () => {
|
|||||||
}}>
|
}}>
|
||||||
<span>No Environment</span>
|
<span>No Environment</span>
|
||||||
</div>
|
</div>
|
||||||
<div className="dropdown-item" style={{borderTop: 'solid 1px #e7e7e7'}} onClick={() => {
|
<div className="dropdown-item" style={{borderTop: 'solid 1px #e7e7e7'}} onClick={() => setOpenSettingsModal(true)}>
|
||||||
dropdownTippyRef.current.hide();
|
|
||||||
}}>
|
|
||||||
<div className="pr-2 text-gray-600">
|
<div className="pr-2 text-gray-600">
|
||||||
<IconAdjustmentsHorizontal size={18} strokeWidth={1.5}/>
|
<IconAdjustmentsHorizontal size={18} strokeWidth={1.5}/>
|
||||||
</div>
|
</div>
|
||||||
@ -46,6 +46,7 @@ const EnvironmentSelector = () => {
|
|||||||
</div>
|
</div>
|
||||||
</Dropdown>
|
</Dropdown>
|
||||||
</div>
|
</div>
|
||||||
|
{openSettingsModal && <EnvironmentSettings onClose={() => setOpenSettingsModal(false)}/>}
|
||||||
</StyledWrapper>
|
</StyledWrapper>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -18,10 +18,14 @@ const ModalContent = ({children}) => (
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
const ModalFooter = ({confirmText, cancelText, handleSubmit, handleCancel, confirmDisabled, hideCancel}) => {
|
const ModalFooter = ({confirmText, cancelText, handleSubmit, handleCancel, confirmDisabled, hideCancel, hideFooter}) => {
|
||||||
confirmText = confirmText || 'Save';
|
confirmText = confirmText || 'Save';
|
||||||
cancelText = cancelText || 'Cancel';
|
cancelText = cancelText || 'Cancel';
|
||||||
|
|
||||||
|
if (hideFooter) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex justify-end p-4 bruno-modal-footer">
|
<div className="flex justify-end p-4 bruno-modal-footer">
|
||||||
<span className={hideCancel ? "hidden" : "mr-2"}>
|
<span className={hideCancel ? "hidden" : "mr-2"}>
|
||||||
@ -47,7 +51,8 @@ const Modal = ({
|
|||||||
handleConfirm,
|
handleConfirm,
|
||||||
children,
|
children,
|
||||||
confirmDisabled,
|
confirmDisabled,
|
||||||
hideCancel
|
hideCancel,
|
||||||
|
hideFooter
|
||||||
}) => {
|
}) => {
|
||||||
const [isClosing, setIsClosing] = useState(false);
|
const [isClosing, setIsClosing] = useState(false);
|
||||||
const escFunction = (event) => {
|
const escFunction = (event) => {
|
||||||
@ -86,6 +91,7 @@ const Modal = ({
|
|||||||
handleSubmit={handleConfirm}
|
handleSubmit={handleConfirm}
|
||||||
confirmDisabled={confirmDisabled}
|
confirmDisabled={confirmDisabled}
|
||||||
hideCancel={hideCancel}
|
hideCancel={hideCancel}
|
||||||
|
hideFooter={hideFooter}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="bruno-modal-backdrop" onClick={() => closeModal()} />
|
<div className="bruno-modal-backdrop" onClick={() => closeModal()} />
|
||||||
|
@ -47,7 +47,7 @@ const AddWorkspace = ({onClose}) => {
|
|||||||
>
|
>
|
||||||
<form className="bruno-form" onSubmit={formik.handleSubmit}>
|
<form className="bruno-form" onSubmit={formik.handleSubmit}>
|
||||||
<div>
|
<div>
|
||||||
<label htmlFor="name" className="block font-semibold">Workpsace Name</label>
|
<label htmlFor="name" className="block font-semibold">Workspace Name</label>
|
||||||
<input
|
<input
|
||||||
id="workspace-name" type="text" name="name"
|
id="workspace-name" type="text" name="name"
|
||||||
ref={inputRef}
|
ref={inputRef}
|
||||||
|
@ -47,7 +47,7 @@ const EditWorkspace = ({onClose, workspace}) => {
|
|||||||
>
|
>
|
||||||
<form className="bruno-form" onSubmit={formik.handleSubmit}>
|
<form className="bruno-form" onSubmit={formik.handleSubmit}>
|
||||||
<div>
|
<div>
|
||||||
<label htmlFor="name" className="block font-semibold">Workpsace Name</label>
|
<label htmlFor="name" className="block font-semibold">Workspace Name</label>
|
||||||
<input
|
<input
|
||||||
id="workspace-name" type="text" name="name"
|
id="workspace-name" type="text" name="name"
|
||||||
ref={inputRef}
|
ref={inputRef}
|
||||||
|
Loading…
Reference in New Issue
Block a user