mirror of
https://github.com/Ravinou/borgwarehouse
synced 2026-03-14 14:25:46 +01:00
config: 🔧 update prettier format
This commit is contained in:
parent
e4d9484759
commit
83fe9a5355
93 changed files with 6446 additions and 7274 deletions
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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%;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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' }}>
|
||||
(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' }}> (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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
<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
|
||||
<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
|
||||
<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
|
||||
<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
|
||||
<a
|
||||
href='https://torsion.org/borgmatic/docs/how-to/set-up-backups/#autopilot'
|
||||
rel='noreferrer'
|
||||
target='_blank'
|
||||
>
|
||||
this documentation
|
||||
</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
|
||||
<a
|
||||
href='https://torsion.org/borgmatic/docs/how-to/set-up-backups/#autopilot'
|
||||
rel='noreferrer'
|
||||
target='_blank'
|
||||
>
|
||||
this documentation
|
||||
</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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue