From 580467a2c2a22994c70bcd85b8e86e8a632e02d5 Mon Sep 17 00:00:00 2001 From: Jason Ho Date: Thu, 28 Mar 2024 18:01:45 -0700 Subject: [PATCH 01/21] Define width of provider selectors to be 20% of the screen, consistent padding --- src/css/App.css | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/css/App.css b/src/css/App.css index d75bd892..7c45fb83 100644 --- a/src/css/App.css +++ b/src/css/App.css @@ -240,9 +240,10 @@ body { } .providerIcon { + padding: 10px; min-width: 120px; - max-width: 240px; - min-height: 110px; + max-width: calc(20% - calc(2 * 10px)); + min-height: 120px; margin: 10px; vertical-align: middle; } From e064d3f95022e71eeebd2519d9fda916e1ae5d12 Mon Sep 17 00:00:00 2001 From: Jason Ho Date: Thu, 28 Mar 2024 18:02:01 -0700 Subject: [PATCH 02/21] Forms should be spaced to look nice --- src/css/App.css | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/css/App.css b/src/css/App.css index 7c45fb83..ea1cc75f 100644 --- a/src/css/App.css +++ b/src/css/App.css @@ -124,6 +124,10 @@ body { filter: brightness(80%); } +#kopia .form-label { + margin-top: 0.5rem; +} + #kopia .form-control { color: var(--color-text-body); background-color: var(--background-color); From 7fc0091c48f83a010bc2b131ea9f12f3470f740f Mon Sep 17 00:00:00 2001 From: Jason Ho Date: Thu, 28 Mar 2024 18:02:27 -0700 Subject: [PATCH 03/21] Move connected repository to navbar --- src/App.jsx | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/App.jsx b/src/App.jsx index 6669ff91..44079e18 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -122,14 +122,24 @@ export default class App extends Component { Repository Preferences + - -
{this.state.repoDescription}
-
- From 946923e93ea3b9062f8c0610f2f6e9b958be754a Mon Sep 17 00:00:00 2001 From: Jason Ho Date: Thu, 28 Mar 2024 18:03:17 -0700 Subject: [PATCH 04/21] Don't stripe tables, remove dark constraint on table making the table look out of place in light mode --- src/utils/KopiaTable.jsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/utils/KopiaTable.jsx b/src/utils/KopiaTable.jsx index 0ce6574d..fd3a8941 100644 --- a/src/utils/KopiaTable.jsx +++ b/src/utils/KopiaTable.jsx @@ -110,8 +110,8 @@ export default function KopiaTable({ columns, data }) { return ( <> - - +
+ {headerGroups.map(headerGroup => ( {headerGroup.headers.map(column => ( @@ -120,6 +120,7 @@ export default function KopiaTable({ columns, data }) { width: column.width, } })}>{column.render('Header')} +   {column.isSorted ? (column.isSortedDesc ? '🔽' : '🔼') : ''} ))} From 3c88635d4e6f5b7d0d6b8c71f1afcdfa920a21cb Mon Sep 17 00:00:00 2001 From: Jason Ho Date: Thu, 28 Mar 2024 18:05:23 -0700 Subject: [PATCH 05/21] Hide dividers on some dropdowns when no options under them --- src/pages/Policies.jsx | 4 +++- src/pages/Snapshots.jsx | 4 +++- src/pages/Tasks.jsx | 4 +++- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/pages/Policies.jsx b/src/pages/Policies.jsx index 56676d56..fecb8d96 100644 --- a/src/pages/Policies.jsx +++ b/src/pages/Policies.jsx @@ -268,7 +268,9 @@ export class Policies extends Component { this.selectOwner(globalPolicy)}>{globalPolicy} this.selectOwner(perUserPolicies)}>{perUserPolicies} this.selectOwner(perHostPolicies)}>{perHostPolicies} - + 0 ? "" : "none" + }}/> {uniqueOwners.map(v => this.selectOwner(v)}>{v})} diff --git a/src/pages/Snapshots.jsx b/src/pages/Snapshots.jsx index ffabeaab..2bc86535 100644 --- a/src/pages/Snapshots.jsx +++ b/src/pages/Snapshots.jsx @@ -305,7 +305,9 @@ export class Snapshots extends Component { this.selectOwner(localSnapshots)}>{localSnapshots} this.selectOwner(allSnapshots)}>{allSnapshots} - + 0 ? "" : "none", + }}/> {uniqueOwners.map(v => this.selectOwner(v)}>{v})} diff --git a/src/pages/Tasks.jsx b/src/pages/Tasks.jsx index 427df65a..9d9a1a16 100644 --- a/src/pages/Tasks.jsx +++ b/src/pages/Tasks.jsx @@ -145,7 +145,9 @@ export class Tasks extends Component { Kind: {this.state.showKind} this.setState({ showKind: "All" })}>All - + 0 ? "" : "none" + }}/> {this.state.uniqueKinds.map(k => this.setState({ showKind: k })}>{k})} From 4870dbfcb4d186c9b7fad9e87f6fa61246f87fd5 Mon Sep 17 00:00:00 2001 From: Jason Ho Date: Thu, 28 Mar 2024 18:06:00 -0700 Subject: [PATCH 06/21] consistency in status display for task statuses --- src/utils/uiutil.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/uiutil.jsx b/src/utils/uiutil.jsx index 0f230498..5450efc3 100644 --- a/src/utils/uiutil.jsx +++ b/src/utils/uiutil.jsx @@ -284,7 +284,7 @@ export function taskStatusSymbol(task) { return

Failed after {dur}

; case "CANCELED": - return

Canceled after {dur}

; + return

Canceled after {dur}

; default: return st; From d31b783ecb40b1a5b1f03b4d6932fa7d9038a837 Mon Sep 17 00:00:00 2001 From: Jason Ho Date: Thu, 28 Mar 2024 18:06:57 -0700 Subject: [PATCH 07/21] Remove link for viewing task details from the time, and add a dedicated icon for viewing details --- src/pages/Tasks.jsx | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/src/pages/Tasks.jsx b/src/pages/Tasks.jsx index 9d9a1a16..4716d38d 100644 --- a/src/pages/Tasks.jsx +++ b/src/pages/Tasks.jsx @@ -13,6 +13,7 @@ import { Link } from 'react-router-dom'; import { handleChange } from '../forms'; import KopiaTable from '../utils/KopiaTable'; import { redirect, taskStatusSymbol } from '../utils/uiutil'; +import {faEye} from "@fortawesome/free-regular-svg-icons/faEye"; export class Tasks extends Component { constructor() { @@ -103,24 +104,29 @@ export class Tasks extends Component { return

Loading ...

; } - const columns = [{ + const columns = [ + { Header: 'Start Time', width: 160, - accessor: x => - {moment(x.startTime).fromNow()} - + accessor: x => {moment(x.startTime).fromNow()} }, { Header: 'Status', width: 240, - accessor: x => taskStatusSymbol(x), + accessor: x => {taskStatusSymbol(x)}, }, { Header: 'Kind', width: "", - accessor: x =>

{x.kind}

, + accessor: x => {x.kind}, }, { Header: 'Description', width: "", - accessor: x =>

{x.description}

, + accessor: x => {x.description}, + }, { + Header: "View", + width: 50, + accessor: x => + + }] const filteredItems = this.filterItems(items) @@ -135,8 +141,10 @@ export class Tasks extends Component { this.setState({ showStatus: "All" })}>All + this.setState({ showStatus: "Success" })}>Success this.setState({ showStatus: "Running" })}>Running this.setState({ showStatus: "Failed" })}>Failed + this.setState({ showStatus: "Canceled" })}>Cancelled From ee87b133b250d3ec192d1eafbb736f0a9eb535f6 Mon Sep 17 00:00:00 2001 From: Jason Ho Date: Thu, 28 Mar 2024 18:08:32 -0700 Subject: [PATCH 08/21] Make policyeditor have accordions in columns for groups of settings, rather than one giant accordion --- src/components/policy-editor/PolicyEditor.jsx | 644 +++++++++--------- 1 file changed, 329 insertions(+), 315 deletions(-) diff --git a/src/components/policy-editor/PolicyEditor.jsx b/src/components/policy-editor/PolicyEditor.jsx index b61554ee..58f42dca 100644 --- a/src/components/policy-editor/PolicyEditor.jsx +++ b/src/components/policy-editor/PolicyEditor.jsx @@ -29,6 +29,7 @@ import { SectionHeaderRow } from './SectionHeaderRow'; import { ActionRowScript } from './ActionRowScript'; import { ActionRowTimeout } from './ActionRowTimeout'; import { ActionRowMode } from './ActionRowMode'; +import Col from "react-bootstrap/Col"; export class PolicyEditor extends Component { constructor() { @@ -253,321 +254,334 @@ export class PolicyEditor extends Component { return <>
- - -  Snapshot Retention - - - - - {OptionalNumberField(this, null, "policy.retention.keepLatest", { placeholder: "# of latest snapshots" })} - {EffectiveValue(this, "retention.keepLatest")} - - - - {OptionalNumberField(this, null, "policy.retention.keepHourly", { placeholder: "# of hourly snapshots" })} - {EffectiveValue(this, "retention.keepHourly")} - - - - {OptionalNumberField(this, null, "policy.retention.keepDaily", { placeholder: "# of daily snapshots" })} - {EffectiveValue(this, "retention.keepDaily")} - - - - {OptionalNumberField(this, null, "policy.retention.keepWeekly", { placeholder: "# of weekly snapshots" })} - {EffectiveValue(this, "retention.keepWeekly")} - - - - {OptionalNumberField(this, null, "policy.retention.keepMonthly", { placeholder: "# of monthly snapshots" })} - {EffectiveValue(this, "retention.keepMonthly")} - - - - {OptionalNumberField(this, null, "policy.retention.keepAnnual", { placeholder: "# of annual snapshots" })} - {EffectiveValue(this, "retention.keepAnnual")} - - - - {OptionalBoolean(this, null, "policy.retention.ignoreIdenticalSnapshots", "inherit from parent")} - {EffectiveValue(this, "retention.ignoreIdenticalSnapshots")} - - - - -  Files - - - - List of file and directory names to ignore.
(See documentation on ignoring files).} /> - {StringList(this, "policy.files.ignore", { placeholder: "e.g. /file.txt" })} - {EffectiveTextAreaValue(this, "files.ignore")} -
- - - {RequiredBoolean(this, "", "policy.files.noParentIgnore")} - - - - - {StringList(this, "policy.files.ignoreDotFiles", { placeholder: "e.g. .kopiaignore" })} - {EffectiveTextAreaValue(this, "files.ignoreDotFiles")} - - - - {RequiredBoolean(this, "", "policy.files.noParentDotFiles")} - - - - - {OptionalBoolean(this, null, "policy.files.ignoreCacheDirs", "inherit from parent")} - {EffectiveBooleanValue(this, "files.ignoreCacheDirs")} - - - - {OptionalBoolean(this, null, "policy.files.oneFileSystem", "inherit from parent")} - {EffectiveBooleanValue(this, "files.oneFileSystem")} - -
-
- -  Error Handling - - - - - {OptionalBoolean(this, null, "policy.errorHandling.ignoreDirectoryErrors", "inherit from parent")} - {EffectiveBooleanValue(this, "errorHandling.ignoreDirectoryErrors")} - - - - {OptionalBoolean(this, null, "policy.errorHandling.ignoreFileErrors", "inherit from parent")} - {EffectiveBooleanValue(this, "errorHandling.ignoreFileErrors")} - - - - {OptionalBoolean(this, null, "policy.errorHandling.ignoreUnknownTypes", "inherit from parent")} - {EffectiveBooleanValue(this, "errorHandling.ignoreUnknownTypes")} - - - - -  Compression - - - - - - - - {this.state.algorithms && this.state.algorithms.compression.map(x => toAlgorithmOption(x, ""))} - - - {EffectiveValue(this, "compression.compressorName")} - - - - {OptionalNumberField(this, "", "policy.compression.minSize", { placeholder: "minimum file size in bytes" })} - {EffectiveValue(this, "compression.minSize")} - - - - {OptionalNumberField(this, "", "policy.compression.maxSize", { placeholder: "maximum file size in bytes" })} - {EffectiveValue(this, "compression.maxSize")} - - - - - {StringList(this, "policy.compression.onlyCompress", { placeholder: "e.g. *.txt" })} - - {EffectiveTextAreaValue(this, "compression.onlyCompress")} - - - - - {StringList(this, "policy.compression.neverCompress", { placeholder: "e.g. *.mp4" })} - - {EffectiveTextAreaValue(this, "compression.neverCompress")} - - - - -  Scheduling - - - - - - this.handleChange(e, valueToNumber)} - value={stateProperty(this, "policy.scheduling.intervalSeconds")}> - - - - - - - - - - - - {EffectiveValue(this, "scheduling.intervalSeconds")} - - - - - {TimesOfDayList(this, "policy.scheduling.timeOfDay", { placeholder: "e.g. 17:00" })} - - {EffectiveTimesOfDayValue(this, "scheduling.timeOfDay")} - - - Snapshot schedules using UNIX crontab syntax (one per line): -
See supported format details.} /> - - {StringList(this, "policy.scheduling.cron", { placeholder: "minute hour day month weekday #comment" })} - - {EffectiveListValue(this, "scheduling.cron")} -
- - - - {OptionalBoolean(this, "", "policy.scheduling.runMissed", "inherit from parent")} - - {EffectiveBooleanValue(this, "scheduling.runMissed")} - - - - - {OptionalBoolean(this, "", "policy.scheduling.manual", "inherit from parent")} - - {EffectiveBooleanValue(this, "scheduling.manual")} - - - - - - - {UpcomingSnapshotTimes(this.state?.resolved)} - - -
-
- -  Upload - - - - - {OptionalNumberField(this, "", "policy.upload.maxParallelSnapshots", { placeholder: !this.props.path ? "max number of parallel snapshots" : "must be specified using global, user, or host policy", disabled: !!this.props.path })} - {EffectiveValue(this, "upload.maxParallelSnapshots")} - - - - {OptionalNumberField(this, "", "policy.upload.maxParallelFileReads", { placeholder: "max number of parallel file reads" })} - {EffectiveValue(this, "upload.maxParallelFileReads")} - - - - -  Snapshot Actions - - - {ActionRowScript(this, "actions.beforeSnapshotRoot.script", "Before Snapshot", "Script to run before snapshot")} - {ActionRowTimeout(this, "actions.beforeSnapshotRoot.timeout")} - {ActionRowMode(this, "actions.beforeSnapshotRoot.mode")} -
- {ActionRowScript(this, "actions.afterSnapshotRoot.script", "After Snapshot", "Script to run after snapshot")} - {ActionRowTimeout(this, "actions.afterSnapshotRoot.timeout")} - {ActionRowMode(this, "actions.afterSnapshotRoot.mode")} -
-
- -  Folder Actions - - - {ActionRowScript(this, "actions.beforeFolder.script", "Before Folder", "Script to run before folder")} - {ActionRowTimeout(this, "actions.beforeFolder.timeout")} - {ActionRowMode(this, "actions.beforeFolder.mode")} -
- {ActionRowScript(this, "actions.afterFolder.script", "After Folder", "Script to run after folder")} - {ActionRowTimeout(this, "actions.afterFolder.timeout")} - {ActionRowMode(this, "actions.afterFolder.mode")} -
-
- -  Logging - - - - - - {LogDetailSelector(this, "policy.logging.directories.snapshotted")} - - {EffectiveValue(this, "logging.directories.snapshotted")} - - - - - {LogDetailSelector(this, "policy.logging.directories.ignored")} - - {EffectiveValue(this, "logging.directories.ignored")} - - - - - {LogDetailSelector(this, "policy.logging.entries.snapshotted")} - - {EffectiveValue(this, "logging.entries.snapshotted")} - - - - - {LogDetailSelector(this, "policy.logging.entries.ignored")} - - {EffectiveValue(this, "logging.entries.ignored")} - - - - - {LogDetailSelector(this, "policy.logging.entries.cacheHit")} - - {EffectiveValue(this, "logging.entries.cacheHit")} - - - - - {LogDetailSelector(this, "policy.logging.entries.cacheMiss")} - - {EffectiveValue(this, "logging.entries.cacheMiss")} - - - - -  Other - - - - - {RequiredBoolean(this, "", "policy.noParent")} - - - - - -
{JSON.stringify(this.state.policy, null, 4)}
-                                    
-
-
-
-
-
+ +
+ + +  Files + + + + List of file and directory names to ignore.
(See documentation on ignoring files).} /> + {StringList(this, "policy.files.ignore", { placeholder: "e.g. /file.txt" })} + {EffectiveTextAreaValue(this, "files.ignore")} +
+ + + {RequiredBoolean(this, "", "policy.files.noParentIgnore")} + + + + + {StringList(this, "policy.files.ignoreDotFiles", { placeholder: "e.g. .kopiaignore" })} + {EffectiveTextAreaValue(this, "files.ignoreDotFiles")} + + + + {RequiredBoolean(this, "", "policy.files.noParentDotFiles")} + + + + + {OptionalBoolean(this, null, "policy.files.ignoreCacheDirs", "inherit from parent")} + {EffectiveBooleanValue(this, "files.ignoreCacheDirs")} + + + + {OptionalBoolean(this, null, "policy.files.oneFileSystem", "inherit from parent")} + {EffectiveBooleanValue(this, "files.oneFileSystem")} + +
+
+ +  Folder Actions + + + {ActionRowScript(this, "actions.beforeFolder.script", "Before Folder", "Script to run before folder")} + {ActionRowTimeout(this, "actions.beforeFolder.timeout")} + {ActionRowMode(this, "actions.beforeFolder.mode")} +
+ {ActionRowScript(this, "actions.afterFolder.script", "After Folder", "Script to run after folder")} + {ActionRowTimeout(this, "actions.afterFolder.timeout")} + {ActionRowMode(this, "actions.afterFolder.mode")} +
+
+
+ + +  Error Handling + + + + + {OptionalBoolean(this, null, "policy.errorHandling.ignoreDirectoryErrors", "inherit from parent")} + {EffectiveBooleanValue(this, "errorHandling.ignoreDirectoryErrors")} + + + + {OptionalBoolean(this, null, "policy.errorHandling.ignoreFileErrors", "inherit from parent")} + {EffectiveBooleanValue(this, "errorHandling.ignoreFileErrors")} + + + + {OptionalBoolean(this, null, "policy.errorHandling.ignoreUnknownTypes", "inherit from parent")} + {EffectiveBooleanValue(this, "errorHandling.ignoreUnknownTypes")} + + + + +  Compression + + + + + + + + {this.state.algorithms && this.state.algorithms.compression.map(x => toAlgorithmOption(x, ""))} + + + {EffectiveValue(this, "compression.compressorName")} + + + + {OptionalNumberField(this, "", "policy.compression.minSize", { placeholder: "minimum file size in bytes" })} + {EffectiveValue(this, "compression.minSize")} + + + + {OptionalNumberField(this, "", "policy.compression.maxSize", { placeholder: "maximum file size in bytes" })} + {EffectiveValue(this, "compression.maxSize")} + + + + + {StringList(this, "policy.compression.onlyCompress", { placeholder: "e.g. *.txt" })} + + {EffectiveTextAreaValue(this, "compression.onlyCompress")} + + + + + {StringList(this, "policy.compression.neverCompress", { placeholder: "e.g. *.mp4" })} + + {EffectiveTextAreaValue(this, "compression.neverCompress")} + + + + +  Upload + + + + + {OptionalNumberField(this, "", "policy.upload.maxParallelSnapshots", { placeholder: !this.props.path ? "max number of parallel snapshots" : "must be specified using global, user, or host policy", disabled: !!this.props.path })} + {EffectiveValue(this, "upload.maxParallelSnapshots")} + + + + {OptionalNumberField(this, "", "policy.upload.maxParallelFileReads", { placeholder: "max number of parallel file reads" })} + {EffectiveValue(this, "upload.maxParallelFileReads")} + + + + + + + + +  Scheduling + + + + + + this.handleChange(e, valueToNumber)} + value={stateProperty(this, "policy.scheduling.intervalSeconds")}> + + + + + + + + + + + + {EffectiveValue(this, "scheduling.intervalSeconds")} + + + + + {TimesOfDayList(this, "policy.scheduling.timeOfDay", { placeholder: "e.g. 17:00" })} + + {EffectiveTimesOfDayValue(this, "scheduling.timeOfDay")} + + + Snapshot schedules using UNIX crontab syntax (one per line): +
See supported format details.} /> + + {StringList(this, "policy.scheduling.cron", { placeholder: "minute hour day month weekday #comment" })} + + {EffectiveListValue(this, "scheduling.cron")} +
+ + + + {OptionalBoolean(this, "", "policy.scheduling.runMissed", "inherit from parent")} + + {EffectiveBooleanValue(this, "scheduling.runMissed")} + + + + + {OptionalBoolean(this, "", "policy.scheduling.manual", "inherit from parent")} + + {EffectiveBooleanValue(this, "scheduling.manual")} + + + + + + + {UpcomingSnapshotTimes(this.state?.resolved)} + + +
+
+ +  Snapshot Retention + + + + + {OptionalNumberField(this, null, "policy.retention.keepLatest", { placeholder: "# of latest snapshots" })} + {EffectiveValue(this, "retention.keepLatest")} + + + + {OptionalNumberField(this, null, "policy.retention.keepHourly", { placeholder: "# of hourly snapshots" })} + {EffectiveValue(this, "retention.keepHourly")} + + + + {OptionalNumberField(this, null, "policy.retention.keepDaily", { placeholder: "# of daily snapshots" })} + {EffectiveValue(this, "retention.keepDaily")} + + + + {OptionalNumberField(this, null, "policy.retention.keepWeekly", { placeholder: "# of weekly snapshots" })} + {EffectiveValue(this, "retention.keepWeekly")} + + + + {OptionalNumberField(this, null, "policy.retention.keepMonthly", { placeholder: "# of monthly snapshots" })} + {EffectiveValue(this, "retention.keepMonthly")} + + + + {OptionalNumberField(this, null, "policy.retention.keepAnnual", { placeholder: "# of annual snapshots" })} + {EffectiveValue(this, "retention.keepAnnual")} + + + + {OptionalBoolean(this, null, "policy.retention.ignoreIdenticalSnapshots", "inherit from parent")} + {EffectiveValue(this, "retention.ignoreIdenticalSnapshots")} + + + + +  Snapshot Actions + + + {ActionRowScript(this, "actions.beforeSnapshotRoot.script", "Before Snapshot", "Script to run before snapshot")} + {ActionRowTimeout(this, "actions.beforeSnapshotRoot.timeout")} + {ActionRowMode(this, "actions.beforeSnapshotRoot.mode")} +
+ {ActionRowScript(this, "actions.afterSnapshotRoot.script", "After Snapshot", "Script to run after snapshot")} + {ActionRowTimeout(this, "actions.afterSnapshotRoot.timeout")} + {ActionRowMode(this, "actions.afterSnapshotRoot.mode")} +
+
+
+ + +  Logging + + + + + + {LogDetailSelector(this, "policy.logging.directories.snapshotted")} + + {EffectiveValue(this, "logging.directories.snapshotted")} + + + + + {LogDetailSelector(this, "policy.logging.directories.ignored")} + + {EffectiveValue(this, "logging.directories.ignored")} + + + + + {LogDetailSelector(this, "policy.logging.entries.snapshotted")} + + {EffectiveValue(this, "logging.entries.snapshotted")} + + + + + {LogDetailSelector(this, "policy.logging.entries.ignored")} + + {EffectiveValue(this, "logging.entries.ignored")} + + + + + {LogDetailSelector(this, "policy.logging.entries.cacheHit")} + + {EffectiveValue(this, "logging.entries.cacheHit")} + + + + + {LogDetailSelector(this, "policy.logging.entries.cacheMiss")} + + {EffectiveValue(this, "logging.entries.cacheMiss")} + + + + +  Other + + + + + {RequiredBoolean(this, "", "policy.noParent")} + + + + + +
+                                                {JSON.stringify(this.state.policy, null, 4)}
+                                            
+ + + + + + + {!this.props.embedded && } {!this.state.isNew && !this.props.embedded && <>  From 9dc1bb5c4dfafae148bc5513e5710df6bafdd0d2 Mon Sep 17 00:00:00 2001 From: Jason Ho Date: Thu, 28 Mar 2024 18:09:17 -0700 Subject: [PATCH 09/21] unshorten some words (Config -> Configuration), and add margins to space things out appropriately --- src/pages/Repository.jsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/pages/Repository.jsx b/src/pages/Repository.jsx index 5e646cb3..e34ed972 100644 --- a/src/pages/Repository.jsx +++ b/src/pages/Repository.jsx @@ -174,13 +174,13 @@ export class Repository extends Component { : <> - + - Config File + Configuration File - + Provider @@ -198,7 +198,7 @@ export class Repository extends Component { - + Repository Format From fd114ada8ae746c42119bda9c02e2a11d3a2e410 Mon Sep 17 00:00:00 2001 From: Jason Ho Date: Thu, 28 Mar 2024 18:10:03 -0700 Subject: [PATCH 10/21] perhaps a more clearer name for the policy headers (Defined -> This Policy; Effective -> Global Policy) --- src/components/policy-editor/SectionHeaderRow.jsx | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/components/policy-editor/SectionHeaderRow.jsx b/src/components/policy-editor/SectionHeaderRow.jsx index 568a14ad..74bab81c 100644 --- a/src/components/policy-editor/SectionHeaderRow.jsx +++ b/src/components/policy-editor/SectionHeaderRow.jsx @@ -7,7 +7,13 @@ import { EffectiveValueColumn } from './EffectiveValueColumn'; export function SectionHeaderRow() { return -
Defined
-
Effective
+ +
This Policy
+
+
+ +
Global Policy
+
+
; } From 5204eb04b0ef3d13f0189843e0bce78387219cff Mon Sep 17 00:00:00 2001 From: Jason Ho Date: Thu, 28 Mar 2024 18:12:05 -0700 Subject: [PATCH 11/21] Remove unnecessary special titles for certain providers, and make everything use a generic format --- src/components/SetupRepository.jsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/SetupRepository.jsx b/src/components/SetupRepository.jsx index 485c1530..20f461d4 100644 --- a/src/components/SetupRepository.jsx +++ b/src/components/SetupRepository.jsx @@ -304,9 +304,9 @@ export class SetupRepository extends Component { } return - {!this.state.provider.startsWith("_") &&

Storage Configuration

} - {this.state.provider === "_token" &&

Enter Repository Token

} - {this.state.provider === "_server" &&

Kopia Server Parameters

} +

{supportedProviders.find(x => x.provider == this.state.provider).description}

+
Configuring a Storage Repository
+
To configure a storage repository, please fill out the following.
From e062a89f224bdb039988e969323b072c29656c8a Mon Sep 17 00:00:00 2001 From: Jason Ho Date: Thu, 28 Mar 2024 18:14:05 -0700 Subject: [PATCH 12/21] stylistic spacings here and there to make things les crammed in certain views --- src/components/SetupRepository.jsx | 5 +++-- src/components/policy-editor/LabelColumn.jsx | 2 +- src/pages/Task.jsx | 1 + 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/components/SetupRepository.jsx b/src/components/SetupRepository.jsx index 20f461d4..cf3d2f9d 100644 --- a/src/components/SetupRepository.jsx +++ b/src/components/SetupRepository.jsx @@ -325,7 +325,7 @@ export class SetupRepository extends Component { const icon = this.state.showAdvanced ? faAngleDoubleUp : faAngleDoubleDown; const text = this.state.showAdvanced ? "Hide Advanced Options" : "Show Advanced Options"; - return
{props.name} - {props.help && <>

{props.help}

} + {props.help && <>

{props.help}

} ; } diff --git a/src/pages/Task.jsx b/src/pages/Task.jsx index df4bbf90..fbf34c4d 100644 --- a/src/pages/Task.jsx +++ b/src/pages/Task.jsx @@ -186,6 +186,7 @@ export class Task extends Component { {task.counters && +

Statistics

From a0bcffc865b8d3607416cbdc17262de5306c23c2 Mon Sep 17 00:00:00 2001 From: Jason Ho Date: Thu, 28 Mar 2024 18:15:24 -0700 Subject: [PATCH 13/21] badgification of some statuses to distinguish them from regular data --- src/pages/Snapshots.jsx | 8 ++++---- src/utils/uiutil.jsx | 11 ++++++----- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/src/pages/Snapshots.jsx b/src/pages/Snapshots.jsx index 2bc86535..0e0768b0 100644 --- a/src/pages/Snapshots.jsx +++ b/src/pages/Snapshots.jsx @@ -196,20 +196,20 @@ export class Snapshots extends Component { nextSnapshotTimeCell(x, parent) { if (!x.cell.value) { if (x.row.original.status === "PAUSED") { - return "paused"; + return Paused; } - return ""; + return Not Available; } if (x.row.original.status === "UPLOADING") { - return ""; + return Uploading; } return

{moment(x.cell.value).fromNow()} {moment(x.cell.value).isBefore(moment()) && <>   - overdue + Overdue }

; } diff --git a/src/utils/uiutil.jsx b/src/utils/uiutil.jsx index 5450efc3..725194e3 100644 --- a/src/utils/uiutil.jsx +++ b/src/utils/uiutil.jsx @@ -7,6 +7,7 @@ import FormControl from 'react-bootstrap/FormControl'; import InputGroup from 'react-bootstrap/InputGroup'; import Spinner from 'react-bootstrap/Spinner'; import { Link } from 'react-router-dom'; +import Badge from "react-bootstrap/Badge"; // locale to use for number formatting (undefined would use default locale, but we stick to EN for now) const locale = "en-US" @@ -30,7 +31,7 @@ function toDecimalUnitString(f, thousand, prefixes, suffix) { export function sizeWithFailures(size, summ, bytesStringBase2) { if (size === undefined) { - return ""; + return Unknown;; } if (!summ || !summ.errors || !summ.numFailed) { @@ -303,18 +304,18 @@ export function GoBackButton(props) { export function PolicyTypeName(s) { if (!s.host && !s.userName) { - return "Global Policy" + return Global Global Policy } if (!s.userName) { - return "Host: " + s.host; + return Host {s.host}; } if (!s.path) { - return "User: " + s.userName + "@" + s.host; + return User s.userName + "@" + s.host; } - return "Directory: " + s.userName + "@" + s.host + ":" + s.path; + return Directory {s.userName + "@" + s.host + ":" + s.path}; } export function policyEditorURL(s) { From da8812725e4b4b953a3e450b7391f417239db8a4 Mon Sep 17 00:00:00 2001 From: Jason Ho Date: Thu, 28 Mar 2024 18:15:55 -0700 Subject: [PATCH 14/21] Move task start and finish date to the top, keeping less detailed data at the top, and detailed data at the bottom --- src/pages/Task.jsx | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/src/pages/Task.jsx b/src/pages/Task.jsx index fbf34c4d..d1aa48fb 100644 --- a/src/pages/Task.jsx +++ b/src/pages/Task.jsx @@ -184,6 +184,20 @@ export class Task extends Component { {this.summaryControl(task)} + +
+ + Started + + + + + + Finished + + + + {task.counters &&

Statistics

@@ -200,20 +214,6 @@ export class Task extends Component {
} - - - - Started - - - - - - Finished - - - - Logs From 7076bb8536e08293346c02cad9fab6a4be840649 Mon Sep 17 00:00:00 2001 From: Jason Ho Date: Thu, 28 Mar 2024 18:16:30 -0700 Subject: [PATCH 15/21] change snapshot list to say "in progress" when snapshots are running --- src/pages/Snapshots.jsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/pages/Snapshots.jsx b/src/pages/Snapshots.jsx index 0e0768b0..e675e497 100644 --- a/src/pages/Snapshots.jsx +++ b/src/pages/Snapshots.jsx @@ -167,9 +167,10 @@ export class Snapshots extends Component { } return <> -  {totals} +   - {x.row.original.currentTask && Details} + {x.row.original.currentTask && In progress} +  {totals} ; default: From ca97bbca997cc06745c620c5ef2f9fc45670b714 Mon Sep 17 00:00:00 2001 From: Jason Ho Date: Thu, 28 Mar 2024 18:17:11 -0700 Subject: [PATCH 16/21] Make the new snapshot button look different from the other buttons of similar color on the same page --- src/pages/Snapshots.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/Snapshots.jsx b/src/pages/Snapshots.jsx index e675e497..9b3712d7 100644 --- a/src/pages/Snapshots.jsx +++ b/src/pages/Snapshots.jsx @@ -314,7 +314,7 @@ export class Snapshots extends Component { } - + From 95ccb02d41c166cfdd0659926b6206af4eebb0c5 Mon Sep 17 00:00:00 2001 From: Jason Ho Date: Thu, 28 Mar 2024 18:17:55 -0700 Subject: [PATCH 17/21] Make the current page purpose clear when editing policy by putting the name up top, and the policy name under it --- src/pages/Policy.jsx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/pages/Policy.jsx b/src/pages/Policy.jsx index e759e786..ea5b29d4 100644 --- a/src/pages/Policy.jsx +++ b/src/pages/Policy.jsx @@ -16,9 +16,13 @@ export class Policy extends Component { const { userName, host, path } = source; return <> +

- -   {PolicyTypeName(source)}

+ Editing a Policy + +

+ {PolicyTypeName(source)} +

  From ea6f164847d74aeea11a1b5993bd5cff714571ed Mon Sep 17 00:00:00 2001 From: Jason Ho Date: Thu, 28 Mar 2024 18:18:12 -0700 Subject: [PATCH 18/21] make repository selector BOLD --- src/components/SetupRepository.jsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/SetupRepository.jsx b/src/components/SetupRepository.jsx index cf3d2f9d..2d33f77b 100644 --- a/src/components/SetupRepository.jsx +++ b/src/components/SetupRepository.jsx @@ -230,7 +230,7 @@ export class SetupRepository extends Component { data-testid={'provider-' + x.provider} onClick={() => this.setState({ provider: x.provider, providerSettings: {} })} variant={x.provider.startsWith("_") ? "secondary" : "primary"} - className="providerIcon" >{x.description} + className="providerIcon" >{x.description} )} ; @@ -465,6 +465,7 @@ export class SetupRepository extends Component { renderConfirmConnect() { return

Connect To Repository

+
Finish connecting to this repository by configuring these options.
Connect As From ff8883c974b6a1af576de628cc10af16aca0c9dc Mon Sep 17 00:00:00 2001 From: Jason Ho Date: Sat, 30 Mar 2024 09:58:05 -0700 Subject: [PATCH 19/21] Adjust repository in navbar size --- src/App.jsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/App.jsx b/src/App.jsx index 44079e18..fc4f7f3d 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -126,12 +126,12 @@ export default class App extends Component {

{this.state.isRepositoryConnected ? "Connected Repository" : "Repository Disconnected"}

{this.state.isRepositoryConnected ? this.state.repoDescription : "Please connect to a repository first!"}
From 8ca76b369bc814f7165fad4d2b1b0dec5d198b61 Mon Sep 17 00:00:00 2001 From: Jason Ho Date: Sat, 30 Mar 2024 09:58:46 -0700 Subject: [PATCH 20/21] Dock KopiaUI version number to bottom right --- public/index.html | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/public/index.html b/public/index.html index 32f1d803..78dfab4d 100644 --- a/public/index.html +++ b/public/index.html @@ -15,9 +15,18 @@
-