Skip to content
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 our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(ui): Added directory selector for snapshots and filesystem repository #229

Merged
merged 7 commits into from
Jan 25, 2024
Merged
Show file tree
Hide file tree
Changes from all 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
7 changes: 2 additions & 5 deletions src/components/SetupRepositoryFilesystem.jsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import React, { Component } from 'react';
import Row from 'react-bootstrap/Row';
import { handleChange, validateRequiredFields } from '../forms';
import { RequiredField } from '../forms/RequiredField';
import { RequiredDirectory } from '../forms/RequiredDirectory';

export class SetupRepositoryFilesystem extends Component {
constructor(props) {
Expand All @@ -19,9 +18,7 @@ export class SetupRepositoryFilesystem extends Component {

render() {
return <>
<Row>
{RequiredField(this, "Directory Path", "path", { autoFocus: true, placeholder: "enter directory path where you want to store repository files" })}
</Row>
{RequiredDirectory(this, "Directory Path", "path", { autoFocus: true, placeholder: "enter directory path where you want to store repository files"})}
</>;
}
}
2 changes: 0 additions & 2 deletions src/css/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@ body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
"Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}

code {
Expand Down
50 changes: 50 additions & 0 deletions src/forms/OptionalDirectory.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import React from 'react';
import Button from 'react-bootstrap/Button';
import Form from 'react-bootstrap/Form';
import { Col, FormGroup, FormControl, InputGroup } from 'react-bootstrap';
import { faFolderOpen } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { stateProperty } from '.';
import { setDeepStateProperty } from '../utils/deepstate';

/**
* This functions returns a directory selector that allows the user to select a directory.
* The selections is invoked using a button that calls a functions within the electron app.
* If the electron app is not present, the button is not visible.
*
* @param {*} component
* The component that this function is called from
* @param {string} label
* Label, that is added before the input field
* @param {string} name
* Name of the variable in which the directory path is stored
* @param {*} props
* Additional properties of the component
* @returns The form group with the components
*/
export function OptionalDirectory(component, label, name, props = {}) {
/**
* Saves the selected path as a deepstate variable within the component
* @param {The path that has been selected} path
*/
function onDirectorySelected(path) {
setDeepStateProperty(component, name, path)
}

return <FormGroup>
{label && <Form.Label htmlFor='directoryInput' className="required">{label}</Form.Label>}
<InputGroup as={Col}>
<FormControl
id='directoryInput'
size="sm"
name={name}
value={stateProperty(component, name)}
data-testid={'control-' + name}
onChange={component.handleChange}{...props}></FormControl>
{window.kopiaUI &&
<Button size="sm" onClick={() => window.kopiaUI.selectDirectory(onDirectorySelected)}>
<FontAwesomeIcon icon={faFolderOpen} />
</Button>}
</InputGroup>
</FormGroup>
}
3 changes: 1 addition & 2 deletions src/forms/OptionalField.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import Form from 'react-bootstrap/Form';
import Col from 'react-bootstrap/Col';
import { stateProperty } from '.';

export function OptionalField(component, label, name, props = {}, helpText = null, invalidFeedback = null) {
export function OptionalField(component, label, name, props = {}, helpText = null) {
return <Form.Group as={Col}>
<Form.Label>{label}</Form.Label>
<Form.Control
Expand All @@ -14,6 +14,5 @@ export function OptionalField(component, label, name, props = {}, helpText = nul
onChange={component.handleChange}
{...props} />
{helpText && <Form.Text className="text-muted">{helpText}</Form.Text>}
{invalidFeedback && <Form.Control.Feedback type="invalid">{invalidFeedback}</Form.Control.Feedback>}
</Form.Group>;
}
52 changes: 52 additions & 0 deletions src/forms/RequiredDirectory.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import React from 'react';
import Button from 'react-bootstrap/Button';
import Form from 'react-bootstrap/Form';
import { Col, FormGroup, FormControl, InputGroup } from 'react-bootstrap';
import { faFolderOpen } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { stateProperty } from '.';
import { setDeepStateProperty } from '../utils/deepstate';

/**
* This functions returns a directory selector that allows the user to select a directory.
* The selections is invoked using a button that calls a functions within the electron app.
* If the electron app is not present, the button is not visible. The path is required.
*
* @param {*} component
* The component that this function is called from
* @param {string} label
* Label, that is added before the input field
* @param {string} name
* Name of the variable in which the directory path is stored
* @param {*} props
* Additional properties of the component
* @returns The form group with the components
*/
export function RequiredDirectory(component, label, name, props = {}) {
/**
* Saves the selected path as a deepstate variable within the component
* @param {The path that has been selected} path
*/
function onDirectorySelected(path) {
setDeepStateProperty(component, name, path)
}

return <FormGroup>
{label && <Form.Label className="required">{label}</Form.Label>}
<InputGroup as={Col}>
<FormControl
id='directoryInput'
size="sm"
name={name}
isInvalid={stateProperty(component, name, null) === ''}
value={stateProperty(component, name)}
data-testid={'control-' + name}
onChange={component.handleChange}{...props}></FormControl>
{window.kopiaUI &&
<Button size="sm" onClick={() => window.kopiaUI.selectDirectory(onDirectorySelected)}>
<FontAwesomeIcon icon={faFolderOpen} />
</Button>}
<Form.Control.Feedback type="invalid">Required field</Form.Control.Feedback>
</InputGroup>
</FormGroup>
}
9 changes: 4 additions & 5 deletions src/pages/Policies.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ import Form from 'react-bootstrap/Form';
import Row from 'react-bootstrap/Row';
import { Link } from 'react-router-dom';
import { handleChange } from '../forms';
import { OptionalDirectory } from '../forms/OptionalDirectory'
import KopiaTable from '../utils/KopiaTable';
import { CLIEquivalent, compare, DirectorySelector, isAbsolutePath, ownerName, policyEditorURL, redirect } from '../utils/uiutil';
import { CLIEquivalent, compare, isAbsolutePath, ownerName, policyEditorURL, redirect } from '../utils/uiutil';

const applicablePolicies = "Applicable Policies"
const localPolicies = "Local Path Policies"
Expand Down Expand Up @@ -274,9 +275,7 @@ export class Policies extends Component {
</Col>
{(this.state.selectedOwner === localPolicies || this.state.selectedOwner === this.state.localSourceName || this.state.selectedOwner === applicablePolicies) ? <>
<Col>
<DirectorySelector autoFocus onDirectorySelected={p => this.setState({ policyPath: p })}
placeholder="enter directory to find or set policy"
name="policyPath" value={this.state.policyPath} onChange={this.handleChange} />
{OptionalDirectory(this, null, "policyPath", { autoFocus: true, placeholder: "enter directory to find or set policy" })}
</Col>
<Col xs="auto">
<Button disabled={!this.state.policyPath} size="sm" type="submit" onClick={this.editPolicyForPath}>Set Policy</Button>
Expand All @@ -295,4 +294,4 @@ export class Policies extends Component {
<CLIEquivalent command="policy list" />
</>;
}
}
}
27 changes: 10 additions & 17 deletions src/pages/SnapshotCreate.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ import Row from 'react-bootstrap/Row';
import { handleChange } from '../forms';
import { PolicyEditor } from '../components/policy-editor/PolicyEditor';
import { SnapshotEstimation } from '../components/SnapshotEstimation';
import { CLIEquivalent, DirectorySelector, errorAlert, GoBackButton, redirect } from '../utils/uiutil';
import { RequiredDirectory } from '../forms/RequiredDirectory';
import { CLIEquivalent, errorAlert, GoBackButton, redirect } from '../utils/uiutil';

export class SnapshotCreate extends Component {
constructor() {
Expand Down Expand Up @@ -59,7 +60,7 @@ export class SnapshotCreate extends Component {
} else {
this.setState({
lastResolvedPath: currentPath,
resolvedSource: null,
resolvedSource: "",
});

this.maybeResolveCurrentPath(currentPath);
Expand Down Expand Up @@ -147,18 +148,15 @@ export class SnapshotCreate extends Component {

render() {
return <>
<Row>
<Form.Group>
<GoBackButton onClick={this.props.history.goBack} />
</Form.Group>
&nbsp;&nbsp;&nbsp;<h4>New Snapshot</h4>
</Row>
<Form.Group>
<GoBackButton onClick={this.props.history.goBack} />
</Form.Group>
<br />
<h4>New Snapshot</h4>
<br />
<Row>
<Col>
<Form.Group>
<DirectorySelector onDirectorySelected={p => this.setState({ path: p })} autoFocus placeholder="enter path to snapshot" name="path" value={this.state.path} onChange={this.handleChange} />
</Form.Group>
{RequiredDirectory(this, null, "path", { autoFocus: true, placeholder: "enter path to snapshot" })}
</Col>
<Col xs="auto">
<Button
Expand All @@ -168,7 +166,6 @@ export class SnapshotCreate extends Component {
title="Estimate"
variant="secondary"
onClick={this.estimate}>Estimate</Button>
&nbsp;
<Button
data-testid='snapshot-now'
size="sm"
Expand All @@ -182,21 +179,17 @@ export class SnapshotCreate extends Component {
<SnapshotEstimation taskID={this.state.estimateTaskID} hideDescription={true} showZeroCounters={true} />
}
<br />

{this.state.resolvedSource && <Row><Col xs={12}>
<Form.Text>
{this.state.resolvedSource ? this.state.resolvedSource.path : this.state.path}
</Form.Text>

<PolicyEditor ref={this.policyEditorRef}
embedded
host={this.state.resolvedSource.host}
userName={this.state.resolvedSource.userName}
path={this.state.resolvedSource.path} />
</Col></Row>}

<Row><Col>&nbsp;</Col></Row>

<br />
<CLIEquivalent command={`snapshot create ${this.state.resolvedSource ? this.state.resolvedSource.path : this.state.path}`} />
</>;
}
Expand Down
18 changes: 1 addition & 17 deletions src/utils/uiutil.jsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
import { faBan, faCheck, faChevronLeft, faCopy, faExclamationCircle, faExclamationTriangle, faFolderOpen, faTerminal, faXmark } from '@fortawesome/free-solid-svg-icons';
import { faBan, faCheck, faChevronLeft, faCopy, faExclamationCircle, faExclamationTriangle, faTerminal, faXmark } from '@fortawesome/free-solid-svg-icons';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import axios from 'axios';
import React, { useState } from 'react';
import Button from 'react-bootstrap/Button';
import Form from 'react-bootstrap/Form';
import FormControl from 'react-bootstrap/FormControl';
import InputGroup from 'react-bootstrap/InputGroup';
import Spinner from 'react-bootstrap/Spinner';
Expand Down Expand Up @@ -367,21 +366,6 @@ export function errorAlert(err, prefix) {
}
}

export function DirectorySelector(props) {
let { onDirectorySelected, ...inputProps } = props;

if (!window.kopiaUI) {
return <Form.Control size="sm" {...inputProps} />
}

return <InputGroup>
<FormControl size="sm" {...inputProps} />
<Button size="sm" onClick={() => window.kopiaUI.selectDirectory(onDirectorySelected)}>
<FontAwesomeIcon icon={faFolderOpen} />
</Button>
</InputGroup>;
}

export function CLIEquivalent(props) {
let [visible, setVisible] = useState(false);
let [cliInfo, setCLIInfo] = useState({});
Expand Down
Loading