config: 🔧 update prettier format

This commit is contained in:
Ravinou 2024-07-22 11:26:30 +02:00
commit 83fe9a5355
No known key found for this signature in database
GPG key ID: EEEE670C40F6A4D7
93 changed files with 6446 additions and 7274 deletions

View file

@ -5,73 +5,62 @@ import classes from './QuickCommands.module.css';
import { IconSettingsAutomation, IconCopy } from '@tabler/icons-react';
export default function QuickCommands(props) {
////Vars
const wizardEnv = props.wizardEnv;
//Needed to generate command for borg over LAN instead of WAN if env vars are set and option enabled.
let FQDN;
let SSH_SERVER_PORT;
if (
props.lanCommand &&
wizardEnv.FQDN_LAN &&
wizardEnv.SSH_SERVER_PORT_LAN
) {
FQDN = wizardEnv.FQDN_LAN;
SSH_SERVER_PORT =
wizardEnv.SSH_SERVER_PORT_LAN === 'false'
? ''
: ':' + wizardEnv.SSH_SERVER_PORT_LAN;
} else {
FQDN = wizardEnv.FQDN;
SSH_SERVER_PORT =
wizardEnv.SSH_SERVER_PORT === 'false'
? ''
: ':' + wizardEnv.SSH_SERVER_PORT;
}
////Vars
const wizardEnv = props.wizardEnv;
//Needed to generate command for borg over LAN instead of WAN if env vars are set and option enabled.
let FQDN;
let SSH_SERVER_PORT;
if (props.lanCommand && wizardEnv.FQDN_LAN && wizardEnv.SSH_SERVER_PORT_LAN) {
FQDN = wizardEnv.FQDN_LAN;
SSH_SERVER_PORT =
wizardEnv.SSH_SERVER_PORT_LAN === 'false' ? '' : ':' + wizardEnv.SSH_SERVER_PORT_LAN;
} else {
FQDN = wizardEnv.FQDN;
SSH_SERVER_PORT = wizardEnv.SSH_SERVER_PORT === 'false' ? '' : ':' + wizardEnv.SSH_SERVER_PORT;
}
//State
const [isCopied, setIsCopied] = useState(false);
//State
const [isCopied, setIsCopied] = useState(false);
//Functions
const handleCopy = async () => {
// Asynchronously call copy to clipboard
navigator.clipboard
.writeText(
`ssh://${wizardEnv.UNIX_USER}@${FQDN}${SSH_SERVER_PORT}/./${props.repositoryName}`
)
.then(() => {
// If successful, update the isCopied state value
setIsCopied(true);
setTimeout(() => {
setIsCopied(false);
}, 1500);
})
.catch((err) => {
console.log(err);
});
};
//Functions
const handleCopy = async () => {
// Asynchronously call copy to clipboard
navigator.clipboard
.writeText(`ssh://${wizardEnv.UNIX_USER}@${FQDN}${SSH_SERVER_PORT}/./${props.repositoryName}`)
.then(() => {
// If successful, update the isCopied state value
setIsCopied(true);
setTimeout(() => {
setIsCopied(false);
}, 1500);
})
.catch((err) => {
console.log(err);
});
};
return (
<div className={classes.container}>
{isCopied ? (
<div className={classes.copyValid}>Copied !</div>
) : (
<div className={classes.tooltip}>
ssh://{wizardEnv.UNIX_USER}@{FQDN}
{SSH_SERVER_PORT}/./
{props.repositoryName}
</div>
)}
{props.lanCommand && <div className={classes.lanBadge}>LAN</div>}
<div className={classes.icons}>
<button onClick={handleCopy} className={classes.copyButton}>
<IconCopy color='#65748b' stroke={1.25} />
</button>
<div className={classes.quickSetting}>
<IconSettingsAutomation color='#65748b' stroke={1.25} />
</div>
</div>
return (
<div className={classes.container}>
{isCopied ? (
<div className={classes.copyValid}>Copied !</div>
) : (
<div className={classes.tooltip}>
ssh://{wizardEnv.UNIX_USER}@{FQDN}
{SSH_SERVER_PORT}/./
{props.repositoryName}
</div>
);
)}
{props.lanCommand && <div className={classes.lanBadge}>LAN</div>}
<div className={classes.icons}>
<button onClick={handleCopy} className={classes.copyButton}>
<IconCopy color='#65748b' stroke={1.25} />
</button>
<div className={classes.quickSetting}>
<IconSettingsAutomation color='#65748b' stroke={1.25} />
</div>
</div>
</div>
);
}

View file

@ -1,116 +1,116 @@
.container {
display: flex;
align-items: center;
align-self: flex-start;
margin: auto 47px auto auto;
display: flex;
align-items: center;
align-self: flex-start;
margin: auto 47px auto auto;
}
.icons {
position: relative;
bottom: 13px;
position: relative;
bottom: 13px;
}
.quickSetting {
position: absolute;
visibility: visible;
opacity: 1;
position: absolute;
visibility: visible;
opacity: 1;
}
.lanBadge {
border-radius: 5px;
border: 1px solid #6d4aff;
color: #6d4aff;
font-size: 0.9em;
padding: 2px 5px;
margin-right: 8px;
border-radius: 5px;
border: 1px solid #6d4aff;
color: #6d4aff;
font-size: 0.9em;
padding: 2px 5px;
margin-right: 8px;
}
.tooltip {
visibility: hidden;
opacity: 0;
width: 100%;
height: 100%;
border: 1px solid #6d4aff21;
background-color: #f5f5f5;
border-radius: 5px;
box-shadow: 0 0px 1px rgba(0, 0, 0, 0.1) inset;
color: #65748b;
font-size: 0.95rem;
padding: 5px 5px;
transition: 0.5s opacity;
visibility: hidden;
opacity: 0;
width: 100%;
height: 100%;
border: 1px solid #6d4aff21;
background-color: #f5f5f5;
border-radius: 5px;
box-shadow: 0 0px 1px rgba(0, 0, 0, 0.1) inset;
color: #65748b;
font-size: 0.95rem;
padding: 5px 5px;
transition: 0.5s opacity;
}
.copyButton {
position: absolute;
visibility: hidden;
opacity: 0;
border: none;
background-color: none;
position: absolute;
visibility: hidden;
opacity: 0;
border: none;
background-color: none;
}
.copyValid {
margin: auto 8px auto auto;
font-size: 0.95rem;
color: #6d4aff;
animation: scale-in-center 0.5s cubic-bezier(0.25, 0.46, 0.45, 0.94) both;
margin: auto 8px auto auto;
font-size: 0.95rem;
color: #6d4aff;
animation: scale-in-center 0.5s cubic-bezier(0.25, 0.46, 0.45, 0.94) both;
}
@keyframes scale-in-center {
0% {
-webkit-transform: scale(0);
transform: scale(0);
opacity: 1;
}
100% {
-webkit-transform: scale(1);
transform: scale(1);
opacity: 1;
}
0% {
-webkit-transform: scale(0);
transform: scale(0);
opacity: 1;
}
100% {
-webkit-transform: scale(1);
transform: scale(1);
opacity: 1;
}
}
/* On Hover */
.container:hover .tooltip {
visibility: visible;
opacity: 1;
width: 100%;
height: 100%;
border: 1px solid #6d4aff21;
background-color: #f5f5f5;
border-radius: 5px;
box-shadow: 0 0px 1px rgba(0, 0, 0, 0.1) inset;
color: #65748b;
font-size: 0.95rem;
padding: 5px 5px;
transition: 0.5s opacity;
visibility: visible;
opacity: 1;
width: 100%;
height: 100%;
border: 1px solid #6d4aff21;
background-color: #f5f5f5;
border-radius: 5px;
box-shadow: 0 0px 1px rgba(0, 0, 0, 0.1) inset;
color: #65748b;
font-size: 0.95rem;
padding: 5px 5px;
transition: 0.5s opacity;
}
.container:hover .copyButton {
position: absolute;
visibility: visible;
opacity: 1;
border: none;
background-color: transparent;
cursor: pointer;
position: absolute;
visibility: visible;
opacity: 1;
border: none;
background-color: transparent;
cursor: pointer;
}
.container:hover .quickSetting {
position: absolute;
visibility: hidden;
opacity: 0;
position: absolute;
visibility: hidden;
opacity: 0;
}
.container:hover .lanBadge {
visibility: hidden;
opacity: 0;
width: 0;
height: 0;
margin: 0;
padding: 0;
visibility: hidden;
opacity: 0;
width: 0;
height: 0;
margin: 0;
padding: 0;
}
@media all and (max-width: 1000px) {
.container {
display: none;
}
.container {
display: none;
}
}

View file

@ -2,216 +2,179 @@
import { useState } from 'react';
import classes from './Repo.module.css';
import {
IconSettings,
IconInfoCircle,
IconChevronDown,
IconChevronUp,
IconBellOff,
IconLockPlus,
IconSettings,
IconInfoCircle,
IconChevronDown,
IconChevronUp,
IconBellOff,
IconLockPlus,
} from '@tabler/icons-react';
import timestampConverter from '../../helpers/functions/timestampConverter';
import StorageBar from '../UI/StorageBar/StorageBar';
import QuickCommands from './QuickCommands/QuickCommands';
export default function Repo(props) {
//Load displayDetails from LocalStorage
const displayDetailsFromLS = () => {
try {
if (
localStorage.getItem('displayDetailsRepo' + props.id) === null
) {
localStorage.setItem(
'displayDetailsRepo' + props.id,
JSON.stringify(true)
);
return true;
} else {
return JSON.parse(
localStorage.getItem('displayDetailsRepo' + props.id)
);
}
} catch (error) {
console.log(
'LocalStorage error, key',
'displayDetailsRepo' + props.id,
'will be removed. Try again.',
'Error message on this key : ',
error
);
localStorage.removeItem('displayDetailsRepo' + props.id);
}
};
//Load displayDetails from LocalStorage
const displayDetailsFromLS = () => {
try {
if (localStorage.getItem('displayDetailsRepo' + props.id) === null) {
localStorage.setItem('displayDetailsRepo' + props.id, JSON.stringify(true));
return true;
} else {
return JSON.parse(localStorage.getItem('displayDetailsRepo' + props.id));
}
} catch (error) {
console.log(
'LocalStorage error, key',
'displayDetailsRepo' + props.id,
'will be removed. Try again.',
'Error message on this key : ',
error
);
localStorage.removeItem('displayDetailsRepo' + props.id);
}
};
//States
const [displayDetails, setDisplayDetails] = useState(displayDetailsFromLS);
//States
const [displayDetails, setDisplayDetails] = useState(displayDetailsFromLS);
//BUTTON : Display or not repo details for ONE repo
const displayDetailsForOneHandler = (boolean) => {
//Update localStorage
localStorage.setItem(
'displayDetailsRepo' + props.id,
JSON.stringify(boolean)
);
setDisplayDetails(boolean);
};
//BUTTON : Display or not repo details for ONE repo
const displayDetailsForOneHandler = (boolean) => {
//Update localStorage
localStorage.setItem('displayDetailsRepo' + props.id, JSON.stringify(boolean));
setDisplayDetails(boolean);
};
//Status indicator
const statusIndicator = () => {
return props.status
? classes.statusIndicatorGreen
: classes.statusIndicatorRed;
};
//Status indicator
const statusIndicator = () => {
return props.status ? classes.statusIndicatorGreen : classes.statusIndicatorRed;
};
//Alert indicator
const alertIndicator = () => {
if (props.alert === 0) {
return (
<div className={classes.alertIcon}>
<IconBellOff size={16} color='grey' />
</div>
);
}
};
//Alert indicator
const alertIndicator = () => {
if (props.alert === 0) {
return (
<div className={classes.alertIcon}>
<IconBellOff size={16} color='grey' />
</div>
);
}
};
const appendOnlyModeIndicator = () => {
if (props.appendOnlyMode) {
return (
<div className={classes.appendOnlyModeIcon}>
<IconLockPlus size={16} color='grey' />
</div>
);
}
};
const appendOnlyModeIndicator = () => {
if (props.appendOnlyMode) {
return (
<div className={classes.appendOnlyModeIcon}>
<IconLockPlus size={16} color='grey' />
</div>
);
}
};
return (
return (
<>
{displayDetails ? (
<>
{displayDetails ? (
<>
<div className={classes.RepoOpen}>
<div className={classes.openFlex}>
<div className={statusIndicator()} />
<div className={classes.alias}>{props.alias}</div>
{appendOnlyModeIndicator()}
{alertIndicator()}
{props.comment && (
<div className={classes.comment}>
<IconInfoCircle size={16} color='grey' />
<div className={classes.toolTip}>
{props.comment}
</div>
</div>
)}
<QuickCommands
repositoryName={props.repositoryName}
lanCommand={props.lanCommand}
wizardEnv={props.wizardEnv}
/>
</div>
<div className={classes.RepoOpen}>
<div className={classes.openFlex}>
<div className={statusIndicator()} />
<div className={classes.alias}>{props.alias}</div>
{appendOnlyModeIndicator()}
{alertIndicator()}
{props.comment && (
<div className={classes.comment}>
<IconInfoCircle size={16} color='grey' />
<div className={classes.toolTip}>{props.comment}</div>
</div>
)}
<QuickCommands
repositoryName={props.repositoryName}
lanCommand={props.lanCommand}
wizardEnv={props.wizardEnv}
/>
</div>
<table className={classes.tabInfo}>
<thead>
<tr>
<th style={{ width: '15%' }}>Repository</th>
<th style={{ width: '10%' }}>
Storage Size
</th>
<th style={{ width: '30%' }}>
Storage Used
</th>
<th style={{ width: '15%' }}>
Last change
</th>
<th style={{ width: '5%' }}>ID</th>
<th style={{ width: '5%' }}>Edit</th>
</tr>
</thead>
<tbody>
<tr>
<th>{props.repositoryName}</th>
<th>{props.storageSize} GB</th>
<th style={{ padding: '0 4% 0 4%' }}>
<StorageBar
storageUsed={props.storageUsed}
storageSize={props.storageSize}
/>
</th>
<th>
<div className={classes.lastSave}>
{props.lastSave === 0
? '-'
: timestampConverter(
props.lastSave
)}
</div>
</th>
<th>#{props.id}</th>
<th>
<div className={classes.editButton}>
<IconSettings
width={24}
color='#6d4aff'
onClick={() =>
props.repoManageEditHandler()
}
/>
</div>
</th>
</tr>
</tbody>
</table>
<table className={classes.tabInfo}>
<thead>
<tr>
<th style={{ width: '15%' }}>Repository</th>
<th style={{ width: '10%' }}>Storage Size</th>
<th style={{ width: '30%' }}>Storage Used</th>
<th style={{ width: '15%' }}>Last change</th>
<th style={{ width: '5%' }}>ID</th>
<th style={{ width: '5%' }}>Edit</th>
</tr>
</thead>
<tbody>
<tr>
<th>{props.repositoryName}</th>
<th>{props.storageSize} GB</th>
<th style={{ padding: '0 4% 0 4%' }}>
<StorageBar storageUsed={props.storageUsed} storageSize={props.storageSize} />
</th>
<th>
<div className={classes.lastSave}>
{props.lastSave === 0 ? '-' : timestampConverter(props.lastSave)}
</div>
</>
) : (
<>
<div className={classes.RepoClose}>
<div className={classes.closeFlex}>
<div className={statusIndicator()} />
<div className={classes.alias}>{props.alias}</div>
{appendOnlyModeIndicator()}
{alertIndicator()}
{props.comment && (
<div className={classes.comment}>
<IconInfoCircle size={16} color='#637381' />
<div className={classes.toolTip}>
{props.comment}
</div>
</div>
)}
</div>
<div className={classes.lastSave}>
{props.lastSave === 0
? null
: timestampConverter(props.lastSave)}
<span
style={{ marginLeft: '20px', color: '#637381' }}
>
#{props.id}
</span>
</div>
</th>
<th>#{props.id}</th>
<th>
<div className={classes.editButton}>
<IconSettings
width={24}
color='#6d4aff'
onClick={() => props.repoManageEditHandler()}
/>
</div>
</>
)}
{displayDetails ? (
<div className={classes.chevron}>
<IconChevronUp
color='#494b7a'
size={28}
onClick={() => {
displayDetailsForOneHandler(false);
}}
/>
</div>
) : (
<div className={classes.chevron}>
<IconChevronDown
color='#494b7a'
size={28}
onClick={() => {
displayDetailsForOneHandler(true);
}}
/>
</div>
)}
</th>
</tr>
</tbody>
</table>
</div>
</>
);
) : (
<>
<div className={classes.RepoClose}>
<div className={classes.closeFlex}>
<div className={statusIndicator()} />
<div className={classes.alias}>{props.alias}</div>
{appendOnlyModeIndicator()}
{alertIndicator()}
{props.comment && (
<div className={classes.comment}>
<IconInfoCircle size={16} color='#637381' />
<div className={classes.toolTip}>{props.comment}</div>
</div>
)}
</div>
<div className={classes.lastSave}>
{props.lastSave === 0 ? null : timestampConverter(props.lastSave)}
<span style={{ marginLeft: '20px', color: '#637381' }}>#{props.id}</span>
</div>
</div>
</>
)}
{displayDetails ? (
<div className={classes.chevron}>
<IconChevronUp
color='#494b7a'
size={28}
onClick={() => {
displayDetailsForOneHandler(false);
}}
/>
</div>
) : (
<div className={classes.chevron}>
<IconChevronDown
color='#494b7a'
size={28}
onClick={() => {
displayDetailsForOneHandler(true);
}}
/>
</div>
)}
</>
);
}

View file

@ -1,250 +1,249 @@
/*Repo CLOSE*/
.RepoClose {
display: flex;
justify-content: space-between;
align-items: center;
box-shadow:
0 1px 3px rgba(0, 0, 0, 0.12),
0 1px 2px rgba(0, 0, 0, 0.24);
width: auto;
max-height: 65px;
margin: 20px 0px 0px 0px;
border-radius: 5px;
overflow: visible;
/* Need to display comment on hover (which is position : absolute) */
position: relative;
display: flex;
justify-content: space-between;
align-items: center;
box-shadow:
0 1px 3px rgba(0, 0, 0, 0.12),
0 1px 2px rgba(0, 0, 0, 0.24);
width: auto;
max-height: 65px;
margin: 20px 0px 0px 0px;
border-radius: 5px;
overflow: visible;
/* Need to display comment on hover (which is position : absolute) */
position: relative;
}
.closeFlex {
display: flex;
align-items: center;
padding: 15px;
display: flex;
align-items: center;
padding: 15px;
}
.RepoClose .lastSave {
padding: 15px;
padding: 15px;
}
/* REPO OPEN */
.RepoOpen {
display: flex;
flex-direction: column;
justify-content: space-between;
align-items: center;
box-shadow:
0 1px 3px rgba(0, 0, 0, 0.12),
0 1px 2px rgba(0, 0, 0, 0.24);
width: auto;
max-height: 200px;
margin: 20px 0px 0px 0px;
padding: 15px;
border-radius: 5px;
transition: max-height 0.1s linear;
overflow: visible;
/* Need to display comment on hover (which is position : absolute) */
position: relative;
display: flex;
flex-direction: column;
justify-content: space-between;
align-items: center;
box-shadow:
0 1px 3px rgba(0, 0, 0, 0.12),
0 1px 2px rgba(0, 0, 0, 0.24);
width: auto;
max-height: 200px;
margin: 20px 0px 0px 0px;
padding: 15px;
border-radius: 5px;
transition: max-height 0.1s linear;
overflow: visible;
/* Need to display comment on hover (which is position : absolute) */
position: relative;
}
.openFlex {
display: flex;
align-items: center;
align-self: flex-start;
width: 100%;
display: flex;
align-items: center;
align-self: flex-start;
width: 100%;
}
.tabInfo {
width: 100%;
overflow-wrap: break-word;
border-collapse: collapse;
background: #fff;
border-radius: 10px;
overflow: hidden;
margin: 25px auto;
table-layout: fixed;
width: 100%;
overflow-wrap: break-word;
border-collapse: collapse;
background: #fff;
border-radius: 10px;
overflow: hidden;
margin: 25px auto;
table-layout: fixed;
}
.tabInfo thead tr {
height: 50px;
background: #111827;
color: #fff;
height: 50px;
background: #111827;
color: #fff;
}
.tabInfo thead th {
font-size: 1em;
color: #fff;
line-height: 1.2;
font-weight: normal;
font-size: 1em;
color: #fff;
line-height: 1.2;
font-weight: normal;
}
.tabInfo tbody tr {
background-color: #f3f4f6;
height: 50px;
background-color: #f3f4f6;
height: 50px;
}
.tabInfo tbody tr th {
color: #65748b;
font-size: 0.95rem;
font-weight: 400;
color: #65748b;
font-size: 0.95rem;
font-weight: 400;
}
/*STATUS*/
.statusIndicatorGreen {
background: rgb(9, 255, 0);
border-radius: 50%;
margin: 10px;
height: 15px;
width: 15px;
box-shadow: 0 0 0 0 rgb(9, 255, 0);
transform: scale(1);
animation: pulseGreen 5s infinite;
animation-delay: 1s;
background: rgb(9, 255, 0);
border-radius: 50%;
margin: 10px;
height: 15px;
width: 15px;
box-shadow: 0 0 0 0 rgb(9, 255, 0);
transform: scale(1);
animation: pulseGreen 5s infinite;
animation-delay: 1s;
}
@keyframes pulseGreen {
0% {
transform: scale(0.95);
box-shadow: 0 0 0 0 rgba(17, 255, 0, 0.7);
}
0% {
transform: scale(0.95);
box-shadow: 0 0 0 0 rgba(17, 255, 0, 0.7);
}
10% {
transform: scale(1);
box-shadow: 0 0 0 10px rgba(17, 255, 0, 0);
}
10% {
transform: scale(1);
box-shadow: 0 0 0 10px rgba(17, 255, 0, 0);
}
90% {
transform: scale(0.95);
box-shadow: 0 0 0 0 rgba(17, 255, 0, 0);
}
90% {
transform: scale(0.95);
box-shadow: 0 0 0 0 rgba(17, 255, 0, 0);
}
}
.statusIndicatorRed {
background: rgb(255, 0, 0);
border-radius: 50%;
margin: 10px;
height: 15px;
width: 15px;
background: rgb(255, 0, 0);
border-radius: 50%;
margin: 10px;
height: 15px;
width: 15px;
box-shadow: 0 0 0 0 rgb(255, 0, 0);
transform: scale(1);
animation: pulseRed 5s infinite;
animation-delay: 0.5s;
box-shadow: 0 0 0 0 rgb(255, 0, 0);
transform: scale(1);
animation: pulseRed 5s infinite;
animation-delay: 0.5s;
}
@keyframes pulseRed {
0% {
transform: scale(0.95);
box-shadow: 0 0 0 0 rgba(255, 0, 0, 0.7);
}
0% {
transform: scale(0.95);
box-shadow: 0 0 0 0 rgba(255, 0, 0, 0.7);
}
10% {
transform: scale(1);
box-shadow: 0 0 0 10px rgba(255, 0, 0, 0);
}
10% {
transform: scale(1);
box-shadow: 0 0 0 10px rgba(255, 0, 0, 0);
}
90% {
transform: scale(0.95);
box-shadow: 0 0 0 0 rgba(255, 0, 0, 0);
}
90% {
transform: scale(0.95);
box-shadow: 0 0 0 0 rgba(255, 0, 0, 0);
}
}
/* Alert icon */
.alertIcon {
display: flex;
flex-direction: row;
align-items: center;
margin-left: 10px;
display: flex;
flex-direction: row;
align-items: center;
margin-left: 10px;
}
.appendOnlyModeIcon {
display: flex;
flex-direction: row;
align-items: center;
margin-left: 10px;
display: flex;
flex-direction: row;
align-items: center;
margin-left: 10px;
}
/* GENERAL */
.alias {
font-weight: bold;
color: #111827;
font-size: 1.05em;
font-weight: bold;
color: #111827;
font-size: 1.05em;
}
.lastSave {
color: #65748b;
color: #65748b;
}
.editButton {
cursor: pointer;
cursor: pointer;
}
/* Comment */
.comment {
display: flex;
flex-direction: row;
align-items: center;
margin-left: 10px;
display: flex;
flex-direction: row;
align-items: center;
margin-left: 10px;
}
.toolTip {
visibility: hidden;
width: auto;
height: auto;
max-width: 400px;
max-height: 250px;
background-color: #fff;
color: #637381;
text-align: center;
border-radius: 6px;
padding: 5px 5px;
position: absolute;
z-index: 1;
margin: 0px 0 0 20px;
opacity: 1;
transition: 0.5s opacity;
box-shadow:
0 3px 6px rgba(0, 0, 0, 0.16),
0 3px 6px rgba(0, 0, 0, 0.23);
overflow: auto;
visibility: hidden;
width: auto;
height: auto;
max-width: 400px;
max-height: 250px;
background-color: #fff;
color: #637381;
text-align: center;
border-radius: 6px;
padding: 5px 5px;
position: absolute;
z-index: 1;
margin: 0px 0 0 20px;
opacity: 1;
transition: 0.5s opacity;
box-shadow:
0 3px 6px rgba(0, 0, 0, 0.16),
0 3px 6px rgba(0, 0, 0, 0.23);
overflow: auto;
}
.comment:hover .toolTip,
.comment:active .toolTip {
visibility: visible;
opacity: 1;
visibility: visible;
opacity: 1;
}
.chevron {
margin: auto;
margin: auto;
}
.chevron :focus,
.chevron :hover {
cursor: pointer;
filter: invert(27%) sepia(82%) saturate(2209%) hue-rotate(240deg)
brightness(99%) contrast(105%);
cursor: pointer;
filter: invert(27%) sepia(82%) saturate(2209%) hue-rotate(240deg) brightness(99%) contrast(105%);
}
/* MOBILE */
@media all and (max-width: 1000px) {
.tabInfo {
display: none;
}
.toolTip {
display: none;
}
.comment {
display: none;
}
.lastSave {
display: none;
}
.closeFlex {
margin: auto;
}
.openFlex {
margin: auto;
width: auto;
}
.tabInfo {
display: none;
}
.toolTip {
display: none;
}
.comment {
display: none;
}
.lastSave {
display: none;
}
.closeFlex {
margin: auto;
}
.openFlex {
margin: auto;
width: auto;
}
}

View file

@ -4,51 +4,38 @@ import { useState } from 'react';
import { IconChecks, IconCopy } from '@tabler/icons-react';
export default function CopyButton(props) {
//State
const [isCopied, setIsCopied] = useState(false);
//State
const [isCopied, setIsCopied] = useState(false);
//Function
const handleCopy = async (data) => {
navigator.clipboard
.writeText(data)
.then(() => {
// If successful, update the isCopied state value
setIsCopied(true);
setTimeout(() => {
setIsCopied(false);
}, 1500);
})
.catch((err) => {
console.log(err);
});
};
//Function
const handleCopy = async (data) => {
navigator.clipboard
.writeText(data)
.then(() => {
// If successful, update the isCopied state value
setIsCopied(true);
setTimeout(() => {
setIsCopied(false);
}, 1500);
})
.catch((err) => {
console.log(err);
});
};
return (
<>
<button
className={classes.copyButton}
onClick={() => handleCopy(props.dataToCopy)}
>
{props.children}
{isCopied && props.displayIconConfirmation ? (
<IconChecks
color='#07bc0c'
stroke={props.stroke || 1.25}
size={props.size}
/>
) : (
<IconCopy
color='#65748b'
stroke={props.stroke || 1.25}
size={props.size}
/>
)}
</button>
{isCopied
? !props.displayIconConfirmation && (
<span className={classes.copyValid}>Copied !</span>
)
: null}
</>
);
return (
<>
<button className={classes.copyButton} onClick={() => handleCopy(props.dataToCopy)}>
{props.children}
{isCopied && props.displayIconConfirmation ? (
<IconChecks color='#07bc0c' stroke={props.stroke || 1.25} size={props.size} />
) : (
<IconCopy color='#65748b' stroke={props.stroke || 1.25} size={props.size} />
)}
</button>
{isCopied
? !props.displayIconConfirmation && <span className={classes.copyValid}>Copied !</span>
: null}
</>
);
}

View file

@ -1,35 +1,35 @@
.copyButton {
visibility: visible;
opacity: 1;
border: none;
background-color: transparent;
cursor: pointer;
display: flex;
align-items: center;
visibility: visible;
opacity: 1;
border: none;
background-color: transparent;
cursor: pointer;
display: flex;
align-items: center;
}
.copyButton span {
font-size: 0.95rem;
color: #6d4aff;
margin-right: 5px;
user-select: text;
font-size: 0.95rem;
color: #6d4aff;
margin-right: 5px;
user-select: text;
}
.copyValid {
font-size: 0.95rem;
color: #6d4aff;
animation: scale-in-center 0.5s cubic-bezier(0.25, 0.46, 0.45, 0.94) both;
font-size: 0.95rem;
color: #6d4aff;
animation: scale-in-center 0.5s cubic-bezier(0.25, 0.46, 0.45, 0.94) both;
}
@keyframes scale-in-center {
0% {
-webkit-transform: scale(0);
transform: scale(0);
opacity: 1;
}
100% {
-webkit-transform: scale(1);
transform: scale(1);
opacity: 1;
}
0% {
-webkit-transform: scale(0);
transform: scale(0);
opacity: 1;
}
100% {
-webkit-transform: scale(1);
transform: scale(1);
opacity: 1;
}
}

View file

@ -2,5 +2,5 @@
import classes from './Error.module.css';
export default function Error(props) {
return <div className={classes.errorMessage}>{props.message}</div>;
return <div className={classes.errorMessage}>{props.message}</div>;
}

View file

@ -1,18 +1,18 @@
.errorMessage {
margin: 15px 0px;
background-color: red;
color: white;
padding: 15px;
border-radius: 5px;
animation: myAnim 1s ease 0s 1 normal forwards;
margin: 15px 0px;
background-color: red;
color: white;
padding: 15px;
border-radius: 5px;
animation: myAnim 1s ease 0s 1 normal forwards;
}
@keyframes myAnim {
0% {
opacity: 0;
}
0% {
opacity: 0;
}
100% {
opacity: 1;
}
100% {
opacity: 1;
}
}

View file

@ -2,13 +2,10 @@
import classes from './Info.module.css';
export default function Info(props) {
return (
<div
className={classes.infoMessage}
style={{ backgroundColor: props.color }}
>
{props.message}
{props.children}
</div>
);
return (
<div className={classes.infoMessage} style={{ backgroundColor: props.color }}>
{props.message}
{props.children}
</div>
);
}

View file

@ -1,18 +1,18 @@
.infoMessage {
margin: 15px 0px;
background-color: rgb(17, 147, 0);
color: white;
padding: 15px;
border-radius: 5px;
animation: myAnim 1s ease 0s 1 normal forwards;
margin: 15px 0px;
background-color: rgb(17, 147, 0);
color: white;
padding: 15px;
border-radius: 5px;
animation: myAnim 1s ease 0s 1 normal forwards;
}
@keyframes myAnim {
0% {
opacity: 0;
}
0% {
opacity: 0;
}
100% {
opacity: 1;
}
100% {
opacity: 1;
}
}

View file

@ -3,22 +3,22 @@ import classes from './Footer.module.css';
import packageInfo from '../../../../package.json';
function Footer() {
return (
<div className={classes.footer}>
<p>
About{' '}
<a
className={classes.site}
target='_blank'
href='https://borgwarehouse.com/'
rel='noreferrer'
>
BorgWarehouse
</a>{' '}
- v{packageInfo.version}
</p>
</div>
);
return (
<div className={classes.footer}>
<p>
About{' '}
<a
className={classes.site}
target='_blank'
href='https://borgwarehouse.com/'
rel='noreferrer'
>
BorgWarehouse
</a>{' '}
- v{packageInfo.version}
</p>
</div>
);
}
export default Footer;

View file

@ -1,26 +1,26 @@
.footer {
color: #494b7a;
text-align: center;
position: absolute;
bottom: 0;
width: 100%;
height: 50px;
color: #494b7a;
text-align: center;
position: absolute;
bottom: 0;
width: 100%;
height: 50px;
}
.footer p {
padding-left: 70px;
padding-left: 70px;
}
a.site {
color: #6d4aff;
text-decoration: none;
color: #6d4aff;
text-decoration: none;
}
@media all and (max-width: 1000px) {
.footer {
width: 100%;
}
.footer p {
padding-left: 0;
}
.footer {
width: 100%;
}
.footer p {
padding-left: 0;
}
}

View file

@ -5,17 +5,17 @@ import classes from './Header.module.css';
import Nav from './Nav/Nav';
function Header() {
return (
<header className={classes.Header}>
<div className={[classes.flex, 'container'].join(' ')}>
<div className={classes.logo}>BorgWarehouse</div>
return (
<header className={classes.Header}>
<div className={[classes.flex, 'container'].join(' ')}>
<div className={classes.logo}>BorgWarehouse</div>
<nav>
<Nav />
</nav>
</div>
</header>
);
<nav>
<Nav />
</nav>
</div>
</header>
);
}
export default Header;

View file

@ -1,31 +1,31 @@
.Header {
width: 100%;
background: #111827;
box-shadow:
0 3px 6px rgba(0, 0, 0, 0.16),
0 3px 6px rgba(0, 0, 0, 0.23);
height: 50px;
color: white;
display: flex;
align-items: center;
position: static;
top: 0;
z-index: 1000;
width: 100%;
background: #111827;
box-shadow:
0 3px 6px rgba(0, 0, 0, 0.16),
0 3px 6px rgba(0, 0, 0, 0.23);
height: 50px;
color: white;
display: flex;
align-items: center;
position: static;
top: 0;
z-index: 1000;
}
.flex {
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
max-width: 1500px;
margin: auto;
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
max-width: 1500px;
margin: auto;
}
.logo {
font-size: 1.5em;
font-weight: bold;
color: #6d4aff;
text-shadow: #6d4aff 0px 0px 18px;
margin-left: 20px;
font-size: 1.5em;
font-weight: bold;
color: #6d4aff;
text-shadow: #6d4aff 0px 0px 18px;
margin-left: 20px;
}

View file

@ -6,52 +6,42 @@ import { useRouter } from 'next/router';
import { useSession, signOut } from 'next-auth/react';
export default function Nav() {
////Var
//Get the current route to light the right Item
const router = useRouter();
const currentRoute = router.pathname;
const { status, data } = useSession();
////Var
//Get the current route to light the right Item
const router = useRouter();
const currentRoute = router.pathname;
const { status, data } = useSession();
//Function
const onLogoutClickedHandler = async () => {
//This bug is open : https://github.com/nextauthjs/next-auth/issues/1542
//I put redirect to false and redirect with router.
//The result on logout click is an ugly piece of page for a few milliseconds before returning to the login page.
//It's ugly if you are perfectionist but functional and invisible for most of users while waiting for a next-auth fix.
await signOut({ redirect: false });
router.replace('/login');
};
//Function
const onLogoutClickedHandler = async () => {
//This bug is open : https://github.com/nextauthjs/next-auth/issues/1542
//I put redirect to false and redirect with router.
//The result on logout click is an ugly piece of page for a few milliseconds before returning to the login page.
//It's ugly if you are perfectionist but functional and invisible for most of users while waiting for a next-auth fix.
await signOut({ redirect: false });
router.replace('/login');
};
return (
<ul className={classes.Nav}>
<li
style={{ margin: '0px 15px 0px 0px' }}
className={classes.account}
>
<Link
href='/account'
className={
currentRoute === '/account' ? classes.active : null
}
>
<div className={classes.user}>
<div>
<IconUser size={28} />
</div>
<div className={classes.username}>
{status === 'authenticated' && data.user.name}
</div>
</div>
</Link>
</li>
return (
<ul className={classes.Nav}>
<li style={{ margin: '0px 15px 0px 0px' }} className={classes.account}>
<Link href='/account' className={currentRoute === '/account' ? classes.active : null}>
<div className={classes.user}>
<div>
<IconUser size={28} />
</div>
<div className={classes.username}>{status === 'authenticated' && data.user.name}</div>
</div>
</Link>
</li>
<li>
<div className={classes.logout}>
<a onClick={onLogoutClickedHandler}>
<IconLogout size={28} />
</a>
</div>
</li>
</ul>
);
<li>
<div className={classes.logout}>
<a onClick={onLogoutClickedHandler}>
<IconLogout size={28} />
</a>
</div>
</li>
</ul>
);
}

View file

@ -1,54 +1,54 @@
.Nav {
list-style-type: none;
margin: 0px 15px 0px 0px;
padding: 0;
display: flex;
list-style-type: none;
margin: 0px 15px 0px 0px;
padding: 0;
display: flex;
}
.user {
display: flex;
align-items: center;
display: flex;
align-items: center;
}
.username::first-letter {
text-transform: capitalize;
text-transform: capitalize;
}
.account {
background: none;
border: none;
cursor: pointer;
color: #494b7a;
background: none;
border: none;
cursor: pointer;
color: #494b7a;
}
.account a {
color: #494b7a;
text-decoration: none;
color: #494b7a;
text-decoration: none;
}
.account :focus,
.account .active,
.account :hover {
color: #6d4aff;
text-shadow: #6d4aff 0px 0px 18px;
color: #6d4aff;
text-shadow: #6d4aff 0px 0px 18px;
}
.logout {
background: none;
border: none;
cursor: pointer;
color: #494b7a;
background: none;
border: none;
cursor: pointer;
color: #494b7a;
}
.logout :focus,
.logout .active,
.logout :hover {
color: #6d4aff;
text-shadow: #6d4aff 0px 0px 18px;
color: #6d4aff;
text-shadow: #6d4aff 0px 0px 18px;
}
@media all and (max-width: 1000px) {
.account {
display: none;
}
.account {
display: none;
}
}

View file

@ -6,25 +6,25 @@ import classes from './Layout.module.css';
import { useSession } from 'next-auth/react';
function Layout(props) {
//Var
const { status } = useSession();
//Var
const { status } = useSession();
if (status === 'authenticated') {
return (
<>
<Header />
<NavSide />
<div className={classes.mainWrapper}>{props.children}</div>
<Footer />
</>
);
} else if (status === 'unauthenticated') {
return (
<>
<div className={classes.login}>{props.children}</div>
</>
);
}
if (status === 'authenticated') {
return (
<>
<Header />
<NavSide />
<div className={classes.mainWrapper}>{props.children}</div>
<Footer />
</>
);
} else if (status === 'unauthenticated') {
return (
<>
<div className={classes.login}>{props.children}</div>
</>
);
}
}
export default Layout;

View file

@ -1,23 +1,23 @@
.mainWrapper {
margin: auto;
max-width: 1400px;
height: calc(100vh - 100px);
display: flex;
justify-content: center;
align-items: flex-start;
overflow: auto;
/* to prevent main content under navside on little screen */
padding-left: 90px;
/* Disable scrollbar */
scrollbar-width: none;
margin: auto;
max-width: 1400px;
height: calc(100vh - 100px);
display: flex;
justify-content: center;
align-items: flex-start;
overflow: auto;
/* to prevent main content under navside on little screen */
padding-left: 90px;
/* Disable scrollbar */
scrollbar-width: none;
}
.login {
background-color: #111827;
background-color: #111827;
}
@media all and (max-width: 1000px) {
.mainWrapper {
padding-left: 0px;
}
.mainWrapper {
padding-left: 0px;
}
}

View file

@ -1,56 +1,40 @@
//Lib
import classes from './NavSide.module.css';
import {
IconServer,
IconSettingsAutomation,
IconActivityHeartbeat,
} from '@tabler/icons-react';
import { IconServer, IconSettingsAutomation, IconActivityHeartbeat } from '@tabler/icons-react';
import Link from 'next/link';
import { useRouter } from 'next/router';
//Composants
export default function NavSide() {
////Var
//Get the current route to light the right Item
const router = useRouter();
const currentRoute = router.pathname;
////Var
//Get the current route to light the right Item
const router = useRouter();
const currentRoute = router.pathname;
return (
<ul className={classes.NavSide}>
<li className={classes.NavSideItem}>
<Link
href='/'
className={currentRoute === '/' ? classes.active : null}
>
<IconServer size={40} />
</Link>
<span className={classes.tooltip}>Repositories</span>
</li>
<li className={classes.NavSideItem}>
<Link
href='/setup-wizard/1'
className={
currentRoute === '/setup-wizard/[slug]'
? classes.active
: null
}
>
<IconSettingsAutomation size={40} />
</Link>
<span className={classes.tooltip}>Setup Wizard</span>
</li>
<li className={classes.NavSideItem}>
<Link
href='/monitoring'
className={
currentRoute === '/monitoring' ? classes.active : null
}
>
<IconActivityHeartbeat size={40} />
</Link>
<span className={classes.tooltip}>Monitoring</span>
</li>
</ul>
);
return (
<ul className={classes.NavSide}>
<li className={classes.NavSideItem}>
<Link href='/' className={currentRoute === '/' ? classes.active : null}>
<IconServer size={40} />
</Link>
<span className={classes.tooltip}>Repositories</span>
</li>
<li className={classes.NavSideItem}>
<Link
href='/setup-wizard/1'
className={currentRoute === '/setup-wizard/[slug]' ? classes.active : null}
>
<IconSettingsAutomation size={40} />
</Link>
<span className={classes.tooltip}>Setup Wizard</span>
</li>
<li className={classes.NavSideItem}>
<Link href='/monitoring' className={currentRoute === '/monitoring' ? classes.active : null}>
<IconActivityHeartbeat size={40} />
</Link>
<span className={classes.tooltip}>Monitoring</span>
</li>
</ul>
);
}

View file

@ -1,74 +1,74 @@
/* NAVSIDE */
.NavSide {
display: flex;
flex-direction: column;
justify-content: flex-start;
position: absolute;
top: 50px;
left: 0;
bottom: 0;
text-align: center;
/* border-right: 2px solid #e5e7eb; */
height: calc(100% - 50px);
width: 70px;
list-style-type: none;
/* background: #1b1340; */
background: #111827;
/* box-shadow: 0 3px 6px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23); */
display: flex;
flex-direction: column;
justify-content: flex-start;
position: absolute;
top: 50px;
left: 0;
bottom: 0;
text-align: center;
/* border-right: 2px solid #e5e7eb; */
height: calc(100% - 50px);
width: 70px;
list-style-type: none;
/* background: #1b1340; */
background: #111827;
/* box-shadow: 0 3px 6px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23); */
}
ul.NavSide {
padding-top: 10px;
margin: 0;
padding-inline-start: 0px;
padding-top: 10px;
margin: 0;
padding-inline-start: 0px;
}
/* NAV SIDE ITEMS */
.NavSideItem {
margin: 0px 0px 30px 0px;
margin: 0px 0px 30px 0px;
}
.NavSideItem a {
text-decoration: none;
color: #494b7a;
font-weight: bold;
text-decoration: none;
color: #494b7a;
font-weight: bold;
}
.NavSideItem a:hover,
.NavSideItem a:focus,
.NavSideItem a.active {
color: #6d4aff;
font-weight: bold;
/* border-bottom: 2px solid #6d4aff; */
/* padding-bottom: 15px; */
text-shadow: #6d4aff 0px 0px 18px;
color: #6d4aff;
font-weight: bold;
/* border-bottom: 2px solid #6d4aff; */
/* padding-bottom: 15px; */
text-shadow: #6d4aff 0px 0px 18px;
}
.tooltip {
visibility: hidden;
width: 120px;
/* background-color: #1b1340; */
background-color: #111827;
color: #d1d5db;
text-align: center;
border-radius: 6px;
padding: 5px 0;
position: absolute;
z-index: 1;
margin: 5px 0 0 20px;
opacity: 0;
transition: 0.5s opacity;
box-shadow: 0 1px 8px rgba(0, 0, 0, 0.251);
visibility: hidden;
width: 120px;
/* background-color: #1b1340; */
background-color: #111827;
color: #d1d5db;
text-align: center;
border-radius: 6px;
padding: 5px 0;
position: absolute;
z-index: 1;
margin: 5px 0 0 20px;
opacity: 0;
transition: 0.5s opacity;
box-shadow: 0 1px 8px rgba(0, 0, 0, 0.251);
}
.NavSideItem:hover .tooltip {
visibility: visible;
opacity: 1;
visibility: visible;
opacity: 1;
}
@media all and (max-width: 1000px) {
.NavSide {
display: none;
}
.NavSide {
display: none;
}
}

View file

@ -2,18 +2,18 @@
import classes from './ShimmerRepoList.module.css';
export default function ShimmerRepoList() {
return (
<div className={classes.container}>
<div className={classes.loadingButtonContainer}>
<div className={classes.buttonIsLoading} />
</div>
<div className={classes.loadingRepoContainer}>
<div className={classes.repoIsLoading} />
<div className={classes.repoIsLoading} />
<div className={classes.repoIsLoading} />
<div className={classes.repoIsLoading} />
<div className={classes.repoIsLoading} />
</div>
</div>
);
return (
<div className={classes.container}>
<div className={classes.loadingButtonContainer}>
<div className={classes.buttonIsLoading} />
</div>
<div className={classes.loadingRepoContainer}>
<div className={classes.repoIsLoading} />
<div className={classes.repoIsLoading} />
<div className={classes.repoIsLoading} />
<div className={classes.repoIsLoading} />
<div className={classes.repoIsLoading} />
</div>
</div>
);
}

View file

@ -1,50 +1,50 @@
.container {
display: flex;
width: 90%;
justify-content: center;
flex-direction: column;
display: flex;
width: 90%;
justify-content: center;
flex-direction: column;
}
.loadingButtonContainer {
display: flex;
flex-direction: column;
margin: 20px auto;
width: 90%;
justify-content: center;
display: flex;
flex-direction: column;
margin: 20px auto;
width: 90%;
justify-content: center;
}
.buttonIsLoading {
height: 62px;
width: 211px;
margin: auto;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.05);
background: linear-gradient(110deg, #ececec 8%, #f5f5f5 18%, #ececec 33%);
border-radius: 5px;
background-size: 200% 100%;
animation: 1.5s shine linear infinite;
height: 62px;
width: 211px;
margin: auto;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.05);
background: linear-gradient(110deg, #ececec 8%, #f5f5f5 18%, #ececec 33%);
border-radius: 5px;
background-size: 200% 100%;
animation: 1.5s shine linear infinite;
}
.loadingRepoContainer {
display: flex;
justify-content: center;
flex-direction: column;
width: 100%;
margin: auto;
display: flex;
justify-content: center;
flex-direction: column;
width: 100%;
margin: auto;
}
.repoIsLoading {
width: 100%;
height: 65px;
margin: 20px auto;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.05);
background: linear-gradient(110deg, #ececec 8%, #f5f5f5 18%, #ececec 33%);
border-radius: 5px;
background-size: 200% 100%;
animation: 1.5s shine linear infinite;
width: 100%;
height: 65px;
margin: 20px auto;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.05);
background: linear-gradient(110deg, #ececec 8%, #f5f5f5 18%, #ececec 33%);
border-radius: 5px;
background-size: 200% 100%;
animation: 1.5s shine linear infinite;
}
@keyframes shine {
to {
background-position-x: -200%;
}
to {
background-position-x: -200%;
}
}

View file

@ -2,31 +2,27 @@
import classes from './StorageBar.module.css';
export default function StorageBar(props) {
//Var
//storageUsed is in octet, storageSize is in GB. Round to 1 decimal for %.
const storageUsedPercent = (
((props.storageUsed / 1000000) * 100) /
props.storageSize
).toFixed(1);
//Var
//storageUsed is in octet, storageSize is in GB. Round to 1 decimal for %.
const storageUsedPercent = (((props.storageUsed / 1000000) * 100) / props.storageSize).toFixed(1);
return (
<div className={classes.barContainer}>
<div className={classes.barBackground}>
<div
style={{
maxWidth: '100%',
width: storageUsedPercent + '%',
transition: 'width 0.5s 0s ease',
}}
>
<div className={classes.progressionStyle} />
</div>
<div className={classes.tooltip}>
{storageUsedPercent}% (
{(props.storageUsed / 1000000).toFixed(1)} GB /{' '}
{props.storageSize} GB)
</div>
</div>
return (
<div className={classes.barContainer}>
<div className={classes.barBackground}>
<div
style={{
maxWidth: '100%',
width: storageUsedPercent + '%',
transition: 'width 0.5s 0s ease',
}}
>
<div className={classes.progressionStyle} />
</div>
);
<div className={classes.tooltip}>
{storageUsedPercent}% ({(props.storageUsed / 1000000).toFixed(1)} GB / {props.storageSize}{' '}
GB)
</div>
</div>
</div>
);
}

View file

@ -1,36 +1,36 @@
.barContainer {
margin: auto;
margin: auto;
}
.barBackground {
background-color: #704dff5e;
border-radius: 3px;
height: 19px;
width: 100%;
position: relative;
background-color: #704dff5e;
border-radius: 3px;
height: 19px;
width: 100%;
position: relative;
}
.progressionStyle {
background-color: #704dff;
border-radius: 3px;
height: 19px;
width: 100%;
background-color: #704dff;
border-radius: 3px;
height: 19px;
width: 100%;
}
.tooltip {
visibility: hidden;
width: auto;
height: auto;
color: #fff;
text-align: center;
opacity: 0;
transition: 0.5s opacity;
position: absolute;
left: calc(30%);
top: 0px;
visibility: hidden;
width: auto;
height: auto;
color: #fff;
text-align: center;
opacity: 0;
transition: 0.5s opacity;
position: absolute;
left: calc(30%);
top: 0px;
}
.barBackground:hover .tooltip {
visibility: visible;
opacity: 1;
visibility: visible;
opacity: 1;
}

View file

@ -2,25 +2,25 @@
import classes from './Switch.module.css';
export default function Switch(props) {
return (
<>
<div className={classes.switchWrapper}>
<div className={classes.switch}>
<label className={classes.pureMaterialSwitch}>
<input
checked={props.checked}
disabled={props.disabled}
type='checkbox'
onChange={(e) => props.onChange(e.target.checked)}
/>
return (
<>
<div className={classes.switchWrapper}>
<div className={classes.switch}>
<label className={classes.pureMaterialSwitch}>
<input
checked={props.checked}
disabled={props.disabled}
type='checkbox'
onChange={(e) => props.onChange(e.target.checked)}
/>
<span>{props.switchName}</span>
</label>
</div>
<div className={classes.switchDescription}>
<span>{props.switchDescription}</span>
</div>
</div>
</>
);
<span>{props.switchName}</span>
</label>
</div>
<div className={classes.switchDescription}>
<span>{props.switchDescription}</span>
</div>
</div>
</>
);
}

View file

@ -1,141 +1,141 @@
.switchWrapper {
display: flex;
flex-direction: column;
margin-bottom: 20px;
display: flex;
flex-direction: column;
margin-bottom: 20px;
}
.switch {
display: flex;
display: flex;
}
.switchDescription {
display: flex;
margin: 8px 0px 0px 0px;
color: #6c737f;
font-size: 0.875rem;
display: flex;
margin: 8px 0px 0px 0px;
color: #6c737f;
font-size: 0.875rem;
}
.pureMaterialSwitch {
z-index: 0;
position: relative;
display: inline-block;
color: rgba(var(--pure-material-onsurface-rgb, 0, 0, 0), 0.87);
font-family: var(
--pure-material-font,
'Roboto',
'Segoe UI',
BlinkMacSystemFont,
system-ui,
-apple-system
);
font-size: 16px;
line-height: 1.5;
z-index: 0;
position: relative;
display: inline-block;
color: rgba(var(--pure-material-onsurface-rgb, 0, 0, 0), 0.87);
font-family: var(
--pure-material-font,
'Roboto',
'Segoe UI',
BlinkMacSystemFont,
system-ui,
-apple-system
);
font-size: 16px;
line-height: 1.5;
}
/* Input */
.pureMaterialSwitch > input {
appearance: none;
-moz-appearance: none;
-webkit-appearance: none;
z-index: -1;
position: absolute;
right: 6px;
top: -8px;
display: block;
margin: 0;
border-radius: 50%;
width: 40px;
height: 40px;
background-color: rgba(var(--pure-material-onsurface-rgb, 0, 0, 0), 0.38);
outline: none;
opacity: 0;
transform: scale(1);
pointer-events: none;
transition:
opacity 0.3s 0.1s,
transform 0.2s 0.1s;
appearance: none;
-moz-appearance: none;
-webkit-appearance: none;
z-index: -1;
position: absolute;
right: 6px;
top: -8px;
display: block;
margin: 0;
border-radius: 50%;
width: 40px;
height: 40px;
background-color: rgba(var(--pure-material-onsurface-rgb, 0, 0, 0), 0.38);
outline: none;
opacity: 0;
transform: scale(1);
pointer-events: none;
transition:
opacity 0.3s 0.1s,
transform 0.2s 0.1s;
}
/* Span */
.pureMaterialSwitch > span {
display: inline-block;
width: 100%;
cursor: pointer;
font-size: 1rem;
font-weight: 500;
color: #494b7a;
display: inline-block;
width: 100%;
cursor: pointer;
font-size: 1rem;
font-weight: 500;
color: #494b7a;
}
/* Track */
.pureMaterialSwitch > span::before {
content: '';
float: right;
display: inline-block;
margin: 5px 0 5px 30px;
border-radius: 7px;
width: 36px;
height: 14px;
background-color: rgba(var(--pure-material-onsurface-rgb, 0, 0, 0), 0.38);
vertical-align: top;
transition:
background-color 0.2s,
opacity 0.2s;
content: '';
float: right;
display: inline-block;
margin: 5px 0 5px 30px;
border-radius: 7px;
width: 36px;
height: 14px;
background-color: rgba(var(--pure-material-onsurface-rgb, 0, 0, 0), 0.38);
vertical-align: top;
transition:
background-color 0.2s,
opacity 0.2s;
}
/* Thumb */
.pureMaterialSwitch > span::after {
content: '';
position: absolute;
top: 2px;
right: 16px;
border-radius: 50%;
width: 20px;
height: 20px;
background-color: rgb(var(--pure-material-onprimary-rgb, 255, 255, 255));
box-shadow:
0 3px 1px -2px rgba(0, 0, 0, 0.2),
0 2px 2px 0 rgba(0, 0, 0, 0.14),
0 1px 5px 0 rgba(0, 0, 0, 0.12);
transition:
background-color 0.2s,
transform 0.2s;
content: '';
position: absolute;
top: 2px;
right: 16px;
border-radius: 50%;
width: 20px;
height: 20px;
background-color: rgb(var(--pure-material-onprimary-rgb, 255, 255, 255));
box-shadow:
0 3px 1px -2px rgba(0, 0, 0, 0.2),
0 2px 2px 0 rgba(0, 0, 0, 0.14),
0 1px 5px 0 rgba(0, 0, 0, 0.12);
transition:
background-color 0.2s,
transform 0.2s;
}
/* Checked */
.pureMaterialSwitch > input:checked {
right: -10px;
background-color: rgb(var(--pure-material-primary-rgb, 109, 74, 255));
right: -10px;
background-color: rgb(var(--pure-material-primary-rgb, 109, 74, 255));
}
.pureMaterialSwitch > input:checked + span::before {
background-color: rgba(var(--pure-material-primary-rgb, 109, 74, 255), 0.6);
background-color: rgba(var(--pure-material-primary-rgb, 109, 74, 255), 0.6);
}
.pureMaterialSwitch > input:checked + span::after {
background-color: rgb(var(--pure-material-primary-rgb, 109, 74, 255));
transform: translateX(16px);
background-color: rgb(var(--pure-material-primary-rgb, 109, 74, 255));
transform: translateX(16px);
}
/* Active */
.pureMaterialSwitch > input:active {
opacity: 1;
transform: scale(0);
transition:
transform 0s,
opacity 0s;
opacity: 1;
transform: scale(0);
transition:
transform 0s,
opacity 0s;
}
.pureMaterialSwitch > input:active + span::before {
background-color: rgba(var(--pure-material-primary-rgb, 109, 74, 255), 0.6);
background-color: rgba(var(--pure-material-primary-rgb, 109, 74, 255), 0.6);
}
.pureMaterialSwitch > input:checked:active + span::before {
background-color: rgba(var(--pure-material-onsurface-rgb, 0, 0, 0), 0.38);
background-color: rgba(var(--pure-material-onsurface-rgb, 0, 0, 0), 0.38);
}
/* Disabled */
.pureMaterialSwitch > input:disabled + span {
cursor: wait;
cursor: wait;
}
/* .pureMaterialSwitch > input:disabled {

View file

@ -4,66 +4,53 @@ import classes from '../WizardStep1/WizardStep1.module.css';
import { IconDeviceDesktopAnalytics, IconTerminal2 } from '@tabler/icons-react';
function WizardStep1() {
return (
<div className={classes.container}>
<h1>
<IconTerminal2 className={classes.icon} />
Command Line Interface
</h1>
<div className={classes.description}>
We recommend using the official <b>BorgBackup</b> client which
is supported by most Linux distributions.
<br />
More information about installation can be{' '}
<a
href='https://borgbackup.readthedocs.io/en/stable/installation.html'
target='_blank'
rel='noreferrer'
>
found here
</a>
.<br />
To <b>automate your backup</b>, you can also use{' '}
<a
href='https://torsion.org/borgmatic/'
target='_blank'
rel='noreferrer'
>
Borgmatic
</a>{' '}
which is a{' '}
<a
href='https://packages.debian.org/buster/borgmatic'
target='_blank'
rel='noreferrer'
>
Debian package
</a>
. On the step 4, you will find a pattern of default config.
</div>
<div className={classes.separator}></div>
<h1>
<IconDeviceDesktopAnalytics className={classes.icon} />
Graphical User Interface
</h1>
<div className={classes.description}>
<b>Vorta</b> is an opensource (GPLv3) backup client for Borg
Backup.
<br />
It runs on Linux, MacOS and Windows (via Windows Linux
Subsystem (WSL)). Find the right way to install it{' '}
<a
href='https://vorta.borgbase.com/'
target='_blank'
rel='noreferrer'
>
just here
</a>
.
</div>
<img src='/vorta-demo.gif' alt='Vorta GIF' />
</div>
);
return (
<div className={classes.container}>
<h1>
<IconTerminal2 className={classes.icon} />
Command Line Interface
</h1>
<div className={classes.description}>
We recommend using the official <b>BorgBackup</b> client which is supported by most Linux
distributions.
<br />
More information about installation can be{' '}
<a
href='https://borgbackup.readthedocs.io/en/stable/installation.html'
target='_blank'
rel='noreferrer'
>
found here
</a>
.<br />
To <b>automate your backup</b>, you can also use{' '}
<a href='https://torsion.org/borgmatic/' target='_blank' rel='noreferrer'>
Borgmatic
</a>{' '}
which is a{' '}
<a href='https://packages.debian.org/buster/borgmatic' target='_blank' rel='noreferrer'>
Debian package
</a>
. On the step 4, you will find a pattern of default config.
</div>
<div className={classes.separator}></div>
<h1>
<IconDeviceDesktopAnalytics className={classes.icon} />
Graphical User Interface
</h1>
<div className={classes.description}>
<b>Vorta</b> is an opensource (GPLv3) backup client for Borg Backup.
<br />
It runs on Linux, MacOS and Windows (via Windows Linux Subsystem (WSL)). Find the right way
to install it{' '}
<a href='https://vorta.borgbase.com/' target='_blank' rel='noreferrer'>
just here
</a>
.
</div>
<img src='/vorta-demo.gif' alt='Vorta GIF' />
</div>
);
}
export default WizardStep1;

View file

@ -1,137 +1,137 @@
.container {
margin: 40px 20px 20px 5px;
box-shadow:
0 1px 3px rgba(0, 0, 0, 0.12),
0 1px 2px rgba(0, 0, 0, 0.24);
border-radius: 5px;
text-align: left;
padding: 30px 70px;
animation: animStep ease-in 0.3s 1 normal none;
margin: 40px 20px 20px 5px;
box-shadow:
0 1px 3px rgba(0, 0, 0, 0.12),
0 1px 2px rgba(0, 0, 0, 0.24);
border-radius: 5px;
text-align: left;
padding: 30px 70px;
animation: animStep ease-in 0.3s 1 normal none;
}
@keyframes animStep {
0% {
opacity: 0;
}
0% {
opacity: 0;
}
50% {
opacity: 0.5;
}
50% {
opacity: 0.5;
}
100% {
opacity: 1;
}
100% {
opacity: 1;
}
}
.container h1 {
text-align: center;
font-size: 1.7em;
padding-bottom: 20px;
display: flex;
align-items: center;
text-align: center;
font-size: 1.7em;
padding-bottom: 20px;
display: flex;
align-items: center;
}
.container a {
color: #6d4aff;
color: #6d4aff;
}
.separator {
height: 15px;
height: 15px;
}
.container {
line-height: 1.8em;
font-size: 1.05em;
line-height: 1.8em;
font-size: 1.05em;
}
h1 .icon {
color: #6d4aff;
margin-right: 5px;
color: #6d4aff;
margin-right: 5px;
}
.container img {
max-width: 650px;
max-width: 650px;
}
.code {
background-color: #111827;
color: #f8f8f2;
font-family:
ui-monospace,
SFMono-Regular,
Menlo,
Monaco,
Consolas,
liberation mono,
courier new,
monospace;
padding: 5px 15px;
border-radius: 5px;
display: inline-block;
margin: 5px 0px 10px 0px;
font-size: 0.8em;
white-space: pre;
line-height: 1em;
background-color: #111827;
color: #f8f8f2;
font-family:
ui-monospace,
SFMono-Regular,
Menlo,
Monaco,
Consolas,
liberation mono,
courier new,
monospace;
padding: 5px 15px;
border-radius: 5px;
display: inline-block;
margin: 5px 0px 10px 0px;
font-size: 0.8em;
white-space: pre;
line-height: 1em;
}
.verifyOrange {
background-color: #ff7a1b;
color: #fff;
font-weight: 500;
border-radius: 5px;
padding: 15px;
background-color: #ff7a1b;
color: #fff;
font-weight: 500;
border-radius: 5px;
padding: 15px;
}
.verifyRed {
background-color: #ea1313;
color: #fff;
font-weight: 500;
border-radius: 5px;
padding: 15px;
margin-top: 10px;
background-color: #ea1313;
color: #fff;
font-weight: 500;
border-radius: 5px;
padding: 15px;
margin-top: 10px;
}
.verifyOrange li,
.container li {
list-style: disc;
margin-top: 1px;
margin-left: 30px;
list-style: disc;
margin-top: 1px;
margin-left: 30px;
}
.verifyOrange li .sshPublicKey {
background-color: #282a36;
font-family:
ui-monospace,
SFMono-Regular,
Menlo,
Monaco,
Consolas,
liberation mono,
courier new,
monospace;
border-radius: 5px;
padding: 5px;
background-color: #282a36;
font-family:
ui-monospace,
SFMono-Regular,
Menlo,
Monaco,
Consolas,
liberation mono,
courier new,
monospace;
border-radius: 5px;
padding: 5px;
}
.verifyRed .alert,
.verifyOrange .alert {
text-align: center;
font-size: 1.1em;
padding-bottom: 10px;
display: flex;
align-items: center;
text-align: center;
font-size: 1.1em;
padding-bottom: 10px;
display: flex;
align-items: center;
}
.iconAlert {
margin-right: 5px;
margin-right: 5px;
}
.note {
font-style: italic;
color: #494b7a4d;
font-size: 0.8em;
font-style: italic;
color: #494b7a4d;
font-size: 0.8em;
}
.note a {
color: #6d4aff73;
color: #6d4aff73;
}

View file

@ -6,146 +6,132 @@ import CopyButton from '../../UI/CopyButton/CopyButton';
import lanCommandOption from '../../../helpers/functions/lanCommandOption';
function WizardStep2(props) {
////Vars
const wizardEnv = props.wizardEnv;
const UNIX_USER = wizardEnv.UNIX_USER;
//Needed to generate command for borg over LAN instead of WAN if env vars are set and option enabled.
const { FQDN, SSH_SERVER_PORT } = lanCommandOption(
wizardEnv,
props.selectedOption.lanCommand
);
////Vars
const wizardEnv = props.wizardEnv;
const UNIX_USER = wizardEnv.UNIX_USER;
//Needed to generate command for borg over LAN instead of WAN if env vars are set and option enabled.
const { FQDN, SSH_SERVER_PORT } = lanCommandOption(wizardEnv, props.selectedOption.lanCommand);
return (
<div className={classes.container}>
<h1>
<IconTool className={classes.icon} />
Initialize a repository
</h1>
<div className={classes.description}>
To initialize your repository with borgbackup :
<br />
<div
style={{
display: 'flex',
flexDirection: 'row',
justifyContent: 'flex-start',
}}
>
<div className={classes.code}>
borg init -e repokey-blake2 ssh://
{UNIX_USER}@{FQDN}
{SSH_SERVER_PORT}/./
{props.selectedOption.repositoryName}
</div>
<CopyButton
dataToCopy={`borg init -e repokey-blake2 ssh://${UNIX_USER}@${FQDN}${SSH_SERVER_PORT}/./${props.selectedOption.repositoryName}`}
/>
</div>
<div className={classes.note}>
The encryption mode is the one recommended by BorgBackup.
For more information,{' '}
<a
href='https://borgbackup.readthedocs.io/en/stable/usage/init.html?highlight=init#more-encryption-modes'
rel='noreferrer'
target='_blank'
>
click here
</a>
.
</div>
</div>
<div className={classes.separator}></div>
<h2>Borgmatic</h2>
<div className={classes.description}>
If you are using Borgmatic and have <b>already edited</b> the
configuration file (find a sample on the step 4) :
<br />
<div
style={{
display: 'flex',
flexDirection: 'row',
justifyContent: 'flex-start',
}}
>
<div className={classes.code}>
borgmatic init -e repokey-blake2
</div>
<CopyButton dataToCopy='borgmatic init -e repokey-blake2' />
</div>
</div>
<h2>Vorta</h2>
<div className={classes.description}>
To "Initialize a new repository" or "Add existing repository",
copy this into the field "Repository URL" of Vorta :
<br />
<div
style={{
display: 'flex',
flexDirection: 'row',
justifyContent: 'flex-start',
}}
>
<div className={classes.code}>
ssh://
{UNIX_USER}@{FQDN}
{SSH_SERVER_PORT}/./
{props.selectedOption.repositoryName}
</div>
<CopyButton
dataToCopy={`ssh://${UNIX_USER}@${FQDN}${SSH_SERVER_PORT}/./${props.selectedOption.repositoryName}`}
/>
</div>
For more information about the Vorta graphical client, please
refer to{' '}
<a
href='https://vorta.borgbase.com/usage/remote/'
rel='noreferrer'
target='_blank'
>
this documentation
</a>
.
</div>
<div className={classes.separator} />
<div className={classes.verifyOrange}>
<div className={classes.alert}>
<IconAlertCircle className={classes.iconAlert} />
<b>Check the fingerprint of server</b>
</div>
To check that you are talking to the right server, please make
sure to validate one of the following key's fingerprint when you
first connect :
<li>
<span className={classes.sshPublicKey}>
ECDSA : {wizardEnv.SSH_SERVER_FINGERPRINT_ECDSA}
</span>
</li>
<li>
<span className={classes.sshPublicKey}>
ED25519 : {wizardEnv.SSH_SERVER_FINGERPRINT_ED25519}
</span>
</li>
<li>
<span className={classes.sshPublicKey}>
RSA : {wizardEnv.SSH_SERVER_FINGERPRINT_RSA}
</span>
</li>
</div>
<div className={classes.verifyRed}>
<div className={classes.alert}>
<IconAlertCircle className={classes.iconAlert} />
<b>Save your passphrase</b>
</div>
Once again, the server cannot access your encrypted backup data
or the encryption passphrase. Remember to put your passphrase in
your password manager when you initialise your repository.
</div>
return (
<div className={classes.container}>
<h1>
<IconTool className={classes.icon} />
Initialize a repository
</h1>
<div className={classes.description}>
To initialize your repository with borgbackup :
<br />
<div
style={{
display: 'flex',
flexDirection: 'row',
justifyContent: 'flex-start',
}}
>
<div className={classes.code}>
borg init -e repokey-blake2 ssh://
{UNIX_USER}@{FQDN}
{SSH_SERVER_PORT}/./
{props.selectedOption.repositoryName}
</div>
<CopyButton
dataToCopy={`borg init -e repokey-blake2 ssh://${UNIX_USER}@${FQDN}${SSH_SERVER_PORT}/./${props.selectedOption.repositoryName}`}
/>
</div>
);
<div className={classes.note}>
The encryption mode is the one recommended by BorgBackup. For more information,{' '}
<a
href='https://borgbackup.readthedocs.io/en/stable/usage/init.html?highlight=init#more-encryption-modes'
rel='noreferrer'
target='_blank'
>
click here
</a>
.
</div>
</div>
<div className={classes.separator}></div>
<h2>Borgmatic</h2>
<div className={classes.description}>
If you are using Borgmatic and have <b>already edited</b> the configuration file (find a
sample on the step 4) :
<br />
<div
style={{
display: 'flex',
flexDirection: 'row',
justifyContent: 'flex-start',
}}
>
<div className={classes.code}>borgmatic init -e repokey-blake2</div>
<CopyButton dataToCopy='borgmatic init -e repokey-blake2' />
</div>
</div>
<h2>Vorta</h2>
<div className={classes.description}>
To "Initialize a new repository" or "Add existing repository", copy this into the field
"Repository URL" of Vorta :
<br />
<div
style={{
display: 'flex',
flexDirection: 'row',
justifyContent: 'flex-start',
}}
>
<div className={classes.code}>
ssh://
{UNIX_USER}@{FQDN}
{SSH_SERVER_PORT}/./
{props.selectedOption.repositoryName}
</div>
<CopyButton
dataToCopy={`ssh://${UNIX_USER}@${FQDN}${SSH_SERVER_PORT}/./${props.selectedOption.repositoryName}`}
/>
</div>
For more information about the Vorta graphical client, please refer to{' '}
<a href='https://vorta.borgbase.com/usage/remote/' rel='noreferrer' target='_blank'>
this documentation
</a>
.
</div>
<div className={classes.separator} />
<div className={classes.verifyOrange}>
<div className={classes.alert}>
<IconAlertCircle className={classes.iconAlert} />
<b>Check the fingerprint of server</b>
</div>
To check that you are talking to the right server, please make sure to validate one of the
following key's fingerprint when you first connect :
<li>
<span className={classes.sshPublicKey}>
ECDSA : {wizardEnv.SSH_SERVER_FINGERPRINT_ECDSA}
</span>
</li>
<li>
<span className={classes.sshPublicKey}>
ED25519 : {wizardEnv.SSH_SERVER_FINGERPRINT_ED25519}
</span>
</li>
<li>
<span className={classes.sshPublicKey}>RSA : {wizardEnv.SSH_SERVER_FINGERPRINT_RSA}</span>
</li>
</div>
<div className={classes.verifyRed}>
<div className={classes.alert}>
<IconAlertCircle className={classes.iconAlert} />
<b>Save your passphrase</b>
</div>
Once again, the server cannot access your encrypted backup data or the encryption
passphrase. Remember to put your passphrase in your password manager when you initialise
your repository.
</div>
</div>
);
}
export default WizardStep2;

View file

@ -6,184 +6,165 @@ import CopyButton from '../../UI/CopyButton/CopyButton';
import lanCommandOption from '../../../helpers/functions/lanCommandOption';
function WizardStep3(props) {
////Vars
const wizardEnv = props.wizardEnv;
const UNIX_USER = wizardEnv.UNIX_USER;
//Needed to generate command for borg over LAN instead of WAN if env vars are set and option enabled.
const { FQDN, SSH_SERVER_PORT } = lanCommandOption(
wizardEnv,
props.selectedOption.lanCommand
);
////Vars
const wizardEnv = props.wizardEnv;
const UNIX_USER = wizardEnv.UNIX_USER;
//Needed to generate command for borg over LAN instead of WAN if env vars are set and option enabled.
const { FQDN, SSH_SERVER_PORT } = lanCommandOption(wizardEnv, props.selectedOption.lanCommand);
return (
<div className={classes.container}>
<h1>
<IconPlayerPlay className={classes.icon} />
Launch a backup
</h1>
<div className={classes.description}>
To launch a backup with borgbackup :
<div
style={{
display: 'flex',
flexDirection: 'row',
justifyContent: 'flex-start',
}}
>
<div className={classes.code}>
borg create ssh://
{UNIX_USER}@{FQDN}
{SSH_SERVER_PORT}/./
{props.selectedOption.repositoryName}
::archive1 /your/pathToBackup
</div>
<CopyButton
dataToCopy={`borg create ssh://${UNIX_USER}@${FQDN}${SSH_SERVER_PORT}/./${props.selectedOption.repositoryName}::archive1 /your/pathToBackup`}
/>
</div>
</div>
<div className={classes.separator}></div>
<h1>
<IconChecks className={classes.icon} />
Check your backup{' '}
<span style={{ color: '#494b7a4d', fontWeight: 'normal' }}>
&nbsp;(always)
</span>
</h1>
<div className={classes.description}>
BorgWarehouse <b>only stores</b> your backups. They are
encrypted and <b>there is no way</b> for BorgWarehouse to know
if the backup is intact.
<br />
You should regularly test your backups and check that the data
is recoverable.{' '}
<b>
BorgWarehouse cannot do this for you and does not guarantee
anything.
</b>
<br />
</div>
<span className={classes.description}>
Based on the Borg documentation, you have multiple ways to check
that your backups are correct with your tools (tar, rsync, diff
or other tools).
<br />
<li>Check the integrity of a repository with :</li>
<div
style={{
display: 'flex',
flexDirection: 'row',
justifyContent: 'flex-start',
}}
>
<div className={classes.code}>
borg check -v --progress ssh://
{UNIX_USER}@{FQDN}
{SSH_SERVER_PORT}/./
{props.selectedOption.repositoryName}
</div>
<CopyButton
dataToCopy={`borg check -v --progress ssh://${UNIX_USER}@${FQDN}${SSH_SERVER_PORT}/./${props.selectedOption.repositoryName}`}
/>
</div>
<li>List the remote archives with :</li>
<div
style={{
display: 'flex',
flexDirection: 'row',
justifyContent: 'flex-start',
}}
>
<div className={classes.code}>
borg list ssh://
{UNIX_USER}@{FQDN}
{SSH_SERVER_PORT}/./
{props.selectedOption.repositoryName}
</div>
<CopyButton
dataToCopy={`borg list ssh://${UNIX_USER}@${FQDN}${SSH_SERVER_PORT}/./${props.selectedOption.repositoryName}`}
/>
</div>
<li>Download a remote archive with the following command :</li>
<div
style={{
display: 'flex',
flexDirection: 'row',
justifyContent: 'flex-start',
}}
>
<div className={classes.code}>
borg export-tar --tar-filter="gzip -9" ssh://
{UNIX_USER}@{FQDN}
{SSH_SERVER_PORT}/./
{props.selectedOption.repositoryName}
::archive1 archive1.tar.gz
</div>
<CopyButton
dataToCopy={`borg export-tar --tar-filter="gzip -9" ssh://${UNIX_USER}@${FQDN}${SSH_SERVER_PORT}/./${props.selectedOption.repositoryName}::archive1 archive1.tar.gz`}
/>
</div>
<li>
Mount an archive to compare or backup some files without
download all the archive :
</li>
<div
style={{
display: 'flex',
flexDirection: 'row',
justifyContent: 'flex-start',
}}
>
<div className={classes.code}>
borg mount ssh://
{UNIX_USER}@{FQDN}
{SSH_SERVER_PORT}/./
{props.selectedOption.repositoryName}
::archive1 /tmp/yourMountPoint
</div>
<CopyButton
dataToCopy={`borg mount ssh://${UNIX_USER}@${FQDN}${SSH_SERVER_PORT}/./${props.selectedOption.repositoryName}::archive1 /tmp/yourMountPoint`}
/>
</div>
<br />
To verify the consistency of a repository and the corresponding
archives, please refer to{' '}
<a
href='https://borgbackup.readthedocs.io/en/stable/usage/check.html'
rel='noreferrer'
target='_blank'
>
this documentation
</a>
</span>
<div className={classes.separator}></div>
<h2>Borgmatic</h2>
<div className={classes.description}>
If you are using Borgmatic, please refer to{' '}
<a
href='https://torsion.org/borgmatic/docs/how-to/deal-with-very-large-backups/#consistency-check-configuration'
rel='noreferrer'
target='_blank'
>
this documentation
</a>{' '}
for a consistency check.
</div>
<h2>Vorta</h2>
<div className={classes.description}>
If you are using the Vorta graphical client, please refer to{' '}
<a
href='https://vorta.borgbase.com/usage/'
rel='noreferrer'
target='_blank'
>
this documentation
</a>
.
</div>
return (
<div className={classes.container}>
<h1>
<IconPlayerPlay className={classes.icon} />
Launch a backup
</h1>
<div className={classes.description}>
To launch a backup with borgbackup :
<div
style={{
display: 'flex',
flexDirection: 'row',
justifyContent: 'flex-start',
}}
>
<div className={classes.code}>
borg create ssh://
{UNIX_USER}@{FQDN}
{SSH_SERVER_PORT}/./
{props.selectedOption.repositoryName}
::archive1 /your/pathToBackup
</div>
<CopyButton
dataToCopy={`borg create ssh://${UNIX_USER}@${FQDN}${SSH_SERVER_PORT}/./${props.selectedOption.repositoryName}::archive1 /your/pathToBackup`}
/>
</div>
);
</div>
<div className={classes.separator}></div>
<h1>
<IconChecks className={classes.icon} />
Check your backup{' '}
<span style={{ color: '#494b7a4d', fontWeight: 'normal' }}>&nbsp;(always)</span>
</h1>
<div className={classes.description}>
BorgWarehouse <b>only stores</b> your backups. They are encrypted and <b>there is no way</b>{' '}
for BorgWarehouse to know if the backup is intact.
<br />
You should regularly test your backups and check that the data is recoverable.{' '}
<b>BorgWarehouse cannot do this for you and does not guarantee anything.</b>
<br />
</div>
<span className={classes.description}>
Based on the Borg documentation, you have multiple ways to check that your backups are
correct with your tools (tar, rsync, diff or other tools).
<br />
<li>Check the integrity of a repository with :</li>
<div
style={{
display: 'flex',
flexDirection: 'row',
justifyContent: 'flex-start',
}}
>
<div className={classes.code}>
borg check -v --progress ssh://
{UNIX_USER}@{FQDN}
{SSH_SERVER_PORT}/./
{props.selectedOption.repositoryName}
</div>
<CopyButton
dataToCopy={`borg check -v --progress ssh://${UNIX_USER}@${FQDN}${SSH_SERVER_PORT}/./${props.selectedOption.repositoryName}`}
/>
</div>
<li>List the remote archives with :</li>
<div
style={{
display: 'flex',
flexDirection: 'row',
justifyContent: 'flex-start',
}}
>
<div className={classes.code}>
borg list ssh://
{UNIX_USER}@{FQDN}
{SSH_SERVER_PORT}/./
{props.selectedOption.repositoryName}
</div>
<CopyButton
dataToCopy={`borg list ssh://${UNIX_USER}@${FQDN}${SSH_SERVER_PORT}/./${props.selectedOption.repositoryName}`}
/>
</div>
<li>Download a remote archive with the following command :</li>
<div
style={{
display: 'flex',
flexDirection: 'row',
justifyContent: 'flex-start',
}}
>
<div className={classes.code}>
borg export-tar --tar-filter="gzip -9" ssh://
{UNIX_USER}@{FQDN}
{SSH_SERVER_PORT}/./
{props.selectedOption.repositoryName}
::archive1 archive1.tar.gz
</div>
<CopyButton
dataToCopy={`borg export-tar --tar-filter="gzip -9" ssh://${UNIX_USER}@${FQDN}${SSH_SERVER_PORT}/./${props.selectedOption.repositoryName}::archive1 archive1.tar.gz`}
/>
</div>
<li>Mount an archive to compare or backup some files without download all the archive :</li>
<div
style={{
display: 'flex',
flexDirection: 'row',
justifyContent: 'flex-start',
}}
>
<div className={classes.code}>
borg mount ssh://
{UNIX_USER}@{FQDN}
{SSH_SERVER_PORT}/./
{props.selectedOption.repositoryName}
::archive1 /tmp/yourMountPoint
</div>
<CopyButton
dataToCopy={`borg mount ssh://${UNIX_USER}@${FQDN}${SSH_SERVER_PORT}/./${props.selectedOption.repositoryName}::archive1 /tmp/yourMountPoint`}
/>
</div>
<br />
To verify the consistency of a repository and the corresponding archives, please refer to{' '}
<a
href='https://borgbackup.readthedocs.io/en/stable/usage/check.html'
rel='noreferrer'
target='_blank'
>
this documentation
</a>
</span>
<div className={classes.separator}></div>
<h2>Borgmatic</h2>
<div className={classes.description}>
If you are using Borgmatic, please refer to{' '}
<a
href='https://torsion.org/borgmatic/docs/how-to/deal-with-very-large-backups/#consistency-check-configuration'
rel='noreferrer'
target='_blank'
>
this documentation
</a>{' '}
for a consistency check.
</div>
<h2>Vorta</h2>
<div className={classes.description}>
If you are using the Vorta graphical client, please refer to{' '}
<a href='https://vorta.borgbase.com/usage/' rel='noreferrer' target='_blank'>
this documentation
</a>
.
</div>
</div>
);
}
export default WizardStep3;

View file

@ -6,16 +6,13 @@ import CopyButton from '../../UI/CopyButton/CopyButton';
import lanCommandOption from '../../../helpers/functions/lanCommandOption';
function WizardStep4(props) {
////Vars
const wizardEnv = props.wizardEnv;
const UNIX_USER = wizardEnv.UNIX_USER;
//Needed to generate command for borg over LAN instead of WAN if env vars are set and option enabled.
const { FQDN, SSH_SERVER_PORT } = lanCommandOption(
wizardEnv,
props.selectedOption.lanCommand
);
////Vars
const wizardEnv = props.wizardEnv;
const UNIX_USER = wizardEnv.UNIX_USER;
//Needed to generate command for borg over LAN instead of WAN if env vars are set and option enabled.
const { FQDN, SSH_SERVER_PORT } = lanCommandOption(wizardEnv, props.selectedOption.lanCommand);
const configBorgmatic = `location:
const configBorgmatic = `location:
# List of source directories to backup.
source_directories:
- /your-repo-to-backup
@ -54,72 +51,70 @@ consistency:
# Third-party services to notify you if backups aren't happening.
#healthchecks: https://hc-ping.com/be067061-cf96-4412-8eae-62b0c50d6a8c`;
return (
<div className={classes.container}>
<h1>
<IconWand className={classes.icon} />
Automate your backup
</h1>
<div className={classes.description}>
The official borgbackup project provides a script in its
documentation&nbsp;
<a
href='https://borgbackup.readthedocs.io/en/stable/quickstart.html#automating-backups'
rel='noreferrer'
target='_blank'
>
right here
</a>
.
</div>
return (
<div className={classes.container}>
<h1>
<IconWand className={classes.icon} />
Automate your backup
</h1>
<div className={classes.description}>
The official borgbackup project provides a script in its documentation&nbsp;
<a
href='https://borgbackup.readthedocs.io/en/stable/quickstart.html#automating-backups'
rel='noreferrer'
target='_blank'
>
right here
</a>
.
</div>
<div className={classes.separator} />
<h2>Vorta</h2>
<div className={classes.description}>
If you are using the Vorta graphical client, please refer
to&nbsp;
<a
href='https://vorta.borgbase.com/usage/#scheduling-automatic-backups'
rel='noreferrer'
target='_blank'
>
this documentation
</a>
.
</div>
<div className={classes.separator} />
<h2>Vorta</h2>
<div className={classes.description}>
If you are using the Vorta graphical client, please refer to&nbsp;
<a
href='https://vorta.borgbase.com/usage/#scheduling-automatic-backups'
rel='noreferrer'
target='_blank'
>
this documentation
</a>
.
</div>
<h2>Borgmatic</h2>
<div className={classes.description}>
If you are using Borgmatic, you can check&nbsp;
<a
href='https://torsion.org/borgmatic/docs/how-to/set-up-backups/#autopilot'
rel='noreferrer'
target='_blank'
>
this documentation&nbsp;
</a>
and <b>adapt</b> and use the following script :
</div>
<div
style={{
display: 'flex',
flexDirection: 'row',
justifyContent: 'flex-start',
}}
>
<div className={classes.code}>{configBorgmatic}</div>
<div
style={{
margin: '15px 0 auto 0',
display: 'flex',
alignContent: 'center',
}}
>
<CopyButton dataToCopy={configBorgmatic} size={32} />
</div>
</div>
<h2>Borgmatic</h2>
<div className={classes.description}>
If you are using Borgmatic, you can check&nbsp;
<a
href='https://torsion.org/borgmatic/docs/how-to/set-up-backups/#autopilot'
rel='noreferrer'
target='_blank'
>
this documentation&nbsp;
</a>
and <b>adapt</b> and use the following script :
</div>
<div
style={{
display: 'flex',
flexDirection: 'row',
justifyContent: 'flex-start',
}}
>
<div className={classes.code}>{configBorgmatic}</div>
<div
style={{
margin: '15px 0 auto 0',
display: 'flex',
alignContent: 'center',
}}
>
<CopyButton dataToCopy={configBorgmatic} size={32} />
</div>
);
</div>
</div>
);
}
export default WizardStep4;

View file

@ -4,100 +4,67 @@ import classes from './WizardStepBar.module.css';
import { IconChevronLeft, IconChevronRight } from '@tabler/icons-react';
function WizardStepBar(props) {
////Functions
//Color onClick on a step
const colorHandler = (step) => {
if (step <= props.step) {
return classes.active;
} else {
return classes.inactive;
}
};
//Color onClick on next step button
const colorChevronNextStep = () => {
if (props.step < 4) {
return classes.activeChevron;
} else {
return classes.inactiveChevron;
}
};
//Color onClick on previous step button
const colorChevronPreviousStep = () => {
if (props.step > 1) {
return classes.activeChevron;
} else {
return classes.inactiveChevron;
}
};
////Functions
//Color onClick on a step
const colorHandler = (step) => {
if (step <= props.step) {
return classes.active;
} else {
return classes.inactive;
}
};
//Color onClick on next step button
const colorChevronNextStep = () => {
if (props.step < 4) {
return classes.activeChevron;
} else {
return classes.inactiveChevron;
}
};
//Color onClick on previous step button
const colorChevronPreviousStep = () => {
if (props.step > 1) {
return classes.activeChevron;
} else {
return classes.inactiveChevron;
}
};
return (
<div className={classes.stepBarContainer}>
<IconChevronLeft
size={32}
className={colorChevronPreviousStep()}
onClick={props.previousStepHandler}
/>
<ul>
<li
className={colorHandler(2)}
onClick={() => props.setStep(1)}
>
<div
className={[classes.number, colorHandler(1)].join(' ')}
>
1
</div>
<div className={[classes.text, colorHandler(1)].join(' ')}>
Client Setup
</div>
<div className={classes.line}></div>
</li>
<li
className={colorHandler(3)}
onClick={() => props.setStep(2)}
>
<div
className={[classes.number, colorHandler(2)].join(' ')}
>
2
</div>
<div className={[classes.text, colorHandler(2)].join(' ')}>
Init. repository
</div>
<div className={classes.line}></div>
</li>
<li
className={colorHandler(4)}
onClick={() => props.setStep(3)}
>
<div
className={[classes.number, colorHandler(3)].join(' ')}
>
3
</div>
<div className={[classes.text, colorHandler(3)].join(' ')}>
Launch & Verify
</div>
<div className={classes.line}></div>
</li>
<li onClick={() => props.setStep(4)}>
<div
className={[classes.number, colorHandler(4)].join(' ')}
>
4
</div>
<div className={[classes.text, colorHandler(4)].join(' ')}>
Automation
</div>
</li>
</ul>
<IconChevronRight
size={32}
className={colorChevronNextStep()}
onClick={props.nextStepHandler}
/>
</div>
);
return (
<div className={classes.stepBarContainer}>
<IconChevronLeft
size={32}
className={colorChevronPreviousStep()}
onClick={props.previousStepHandler}
/>
<ul>
<li className={colorHandler(2)} onClick={() => props.setStep(1)}>
<div className={[classes.number, colorHandler(1)].join(' ')}>1</div>
<div className={[classes.text, colorHandler(1)].join(' ')}>Client Setup</div>
<div className={classes.line}></div>
</li>
<li className={colorHandler(3)} onClick={() => props.setStep(2)}>
<div className={[classes.number, colorHandler(2)].join(' ')}>2</div>
<div className={[classes.text, colorHandler(2)].join(' ')}>Init. repository</div>
<div className={classes.line}></div>
</li>
<li className={colorHandler(4)} onClick={() => props.setStep(3)}>
<div className={[classes.number, colorHandler(3)].join(' ')}>3</div>
<div className={[classes.text, colorHandler(3)].join(' ')}>Launch & Verify</div>
<div className={classes.line}></div>
</li>
<li onClick={() => props.setStep(4)}>
<div className={[classes.number, colorHandler(4)].join(' ')}>4</div>
<div className={[classes.text, colorHandler(4)].join(' ')}>Automation</div>
</li>
</ul>
<IconChevronRight
size={32}
className={colorChevronNextStep()}
onClick={props.nextStepHandler}
/>
</div>
);
}
export default WizardStepBar;

View file

@ -1,94 +1,94 @@
/* General */
.stepBarContainer {
text-transform: uppercase;
color: #494b7a;
font-size: 1em;
margin-top: 40px;
display: flex;
justify-content: center;
align-items: center;
text-align: center;
text-transform: uppercase;
color: #494b7a;
font-size: 1em;
margin-top: 40px;
display: flex;
justify-content: center;
align-items: center;
text-align: center;
}
.stepBarContainer li {
display: inline-block;
position: relative;
margin: 0px 30px;
width: 180px;
cursor: pointer;
display: inline-block;
position: relative;
margin: 0px 30px;
width: 180px;
cursor: pointer;
}
.stepBarContainer ul {
padding-top: 10px;
margin: 0;
padding-inline-start: 0px;
padding-top: 10px;
margin: 0;
padding-inline-start: 0px;
}
/* Transition Active / Inactive */
.number {
display: inline-block;
position: relative;
color: #494b7a4d;
background: #fff;
border: 1px solid #494b7a4d;
border-radius: 50%;
width: 37px;
padding: 8px 5px;
margin-bottom: 4px;
font-weight: 700;
z-index: 1;
display: inline-block;
position: relative;
color: #494b7a4d;
background: #fff;
border: 1px solid #494b7a4d;
border-radius: 50%;
width: 37px;
padding: 8px 5px;
margin-bottom: 4px;
font-weight: 700;
z-index: 1;
}
.active.number {
box-shadow: 0 0 15px 5px rgba(110, 74, 255, 0.405);
color: #fff;
background: #6d4aff;
transition: all 1000ms cubic-bezier(0.25, 0.25, 0.75, 0.75);
transition-delay: 500ms;
box-shadow: 0 0 15px 5px rgba(110, 74, 255, 0.405);
color: #fff;
background: #6d4aff;
transition: all 1000ms cubic-bezier(0.25, 0.25, 0.75, 0.75);
transition-delay: 500ms;
}
.text {
padding: 8px 0 0 0;
color: #494b7a4d;
padding: 8px 0 0 0;
color: #494b7a4d;
}
.active.text {
transition: all 1000ms cubic-bezier(0.25, 0.25, 0.75, 0.75);
transition-delay: 500ms;
color: #494b7a;
transition: all 1000ms cubic-bezier(0.25, 0.25, 0.75, 0.75);
transition-delay: 500ms;
color: #494b7a;
}
.line {
background: #6d4aff;
width: 0;
height: 2px;
position: absolute;
top: 17px;
left: 108px;
z-index: 0;
transition: all 500ms cubic-bezier(0.25, 0.25, 0.75, 0.75);
background: #6d4aff;
width: 0;
height: 2px;
position: absolute;
top: 17px;
left: 108px;
z-index: 0;
transition: all 500ms cubic-bezier(0.25, 0.25, 0.75, 0.75);
}
.active .line {
background: #6d4aff;
width: 204px;
height: 2px;
position: absolute;
top: 17px;
left: 108px;
z-index: 0;
background: #6d4aff;
width: 204px;
height: 2px;
position: absolute;
top: 17px;
left: 108px;
z-index: 0;
}
.activeChevron {
cursor: pointer;
cursor: pointer;
}
.activeChevron:hover {
color: #6d4aff;
color: #6d4aff;
}
.inactiveChevron {
color: #494b7a4d;
color: #494b7a4d;
}