mirror of
https://github.com/Dictionarry-Hub/profilarr.git
synced 2026-01-30 06:10:56 +01:00
feat: Add button visibility depending on various factors - dev mode hide stage / commit - buttons show tooltips / are greyed out when they cant be clicked
This commit is contained in:
@@ -2,6 +2,37 @@ import React from 'react';
|
|||||||
import {Loader, RotateCcw, Download, CheckCircle, Plus} from 'lucide-react';
|
import {Loader, RotateCcw, Download, CheckCircle, Plus} from 'lucide-react';
|
||||||
import Tooltip from '../../ui/Tooltip';
|
import Tooltip from '../../ui/Tooltip';
|
||||||
|
|
||||||
|
const ActionButton = ({
|
||||||
|
onClick,
|
||||||
|
disabled,
|
||||||
|
loading,
|
||||||
|
icon,
|
||||||
|
text,
|
||||||
|
className,
|
||||||
|
disabledTooltip
|
||||||
|
}) => {
|
||||||
|
const baseClassName =
|
||||||
|
'flex items-center px-4 py-2 text-white rounded-md transition-all duration-200 ease-in-out text-xs';
|
||||||
|
const enabledClassName = `${baseClassName} ${className} hover:opacity-80`;
|
||||||
|
const disabledClassName = `${baseClassName} ${className} opacity-50 cursor-not-allowed`;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Tooltip content={disabled ? disabledTooltip : text}>
|
||||||
|
<button
|
||||||
|
onClick={onClick}
|
||||||
|
className={disabled ? disabledClassName : enabledClassName}
|
||||||
|
disabled={disabled || loading}>
|
||||||
|
{loading ? (
|
||||||
|
<Loader size={12} className='animate-spin mr-1' />
|
||||||
|
) : (
|
||||||
|
React.cloneElement(icon, {className: 'mr-1', size: 12})
|
||||||
|
)}
|
||||||
|
{text}
|
||||||
|
</button>
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
const ActionButtons = ({
|
const ActionButtons = ({
|
||||||
isDevMode,
|
isDevMode,
|
||||||
selectedOutgoingChanges,
|
selectedOutgoingChanges,
|
||||||
@@ -12,106 +43,67 @@ const ActionButtons = ({
|
|||||||
onStageSelected,
|
onStageSelected,
|
||||||
onCommitSelected,
|
onCommitSelected,
|
||||||
onRevertSelected,
|
onRevertSelected,
|
||||||
onPullSelected,
|
onPullSelected
|
||||||
getStageButtonTooltip,
|
|
||||||
getCommitButtonTooltip,
|
|
||||||
getRevertButtonTooltip
|
|
||||||
}) => {
|
}) => {
|
||||||
|
const canStage =
|
||||||
|
isDevMode &&
|
||||||
|
selectedOutgoingChanges.length > 0 &&
|
||||||
|
selectionType !== 'staged';
|
||||||
|
const canCommit =
|
||||||
|
isDevMode &&
|
||||||
|
selectedOutgoingChanges.length > 0 &&
|
||||||
|
commitMessage.trim() &&
|
||||||
|
selectionType !== 'unstaged';
|
||||||
|
const canRevert = selectedOutgoingChanges.length > 0;
|
||||||
|
const canPull = selectedIncomingChanges.length > 0;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='mt-4 flex justify-end space-x-2'>
|
<div className='mt-4 flex justify-start space-x-2'>
|
||||||
{isDevMode && (
|
{isDevMode && (
|
||||||
<>
|
<>
|
||||||
{/* Stage */}
|
<ActionButton
|
||||||
{selectedOutgoingChanges.length > 0 &&
|
onClick={() => onStageSelected(selectedOutgoingChanges)}
|
||||||
selectionType !== 'staged' && (
|
disabled={!canStage}
|
||||||
<Tooltip content={getStageButtonTooltip()}>
|
loading={loadingAction === 'stage_selected'}
|
||||||
<button
|
icon={<Plus />}
|
||||||
onClick={() =>
|
text='Stage'
|
||||||
onStageSelected(selectedOutgoingChanges)
|
className='bg-green-600'
|
||||||
}
|
disabledTooltip='Select unstaged files to enable staging'
|
||||||
className='flex items-center px-4 py-2 bg-green-600 text-white rounded-md hover:bg-green-700 transition-colors duration-200 ease-in-out text-xs'
|
/>
|
||||||
disabled={
|
<ActionButton
|
||||||
loadingAction === 'stage_selected'
|
onClick={() =>
|
||||||
}>
|
onCommitSelected(
|
||||||
{loadingAction === 'stage_selected' ? (
|
selectedOutgoingChanges,
|
||||||
<Loader
|
commitMessage
|
||||||
size={12}
|
)
|
||||||
className='animate-spin'
|
}
|
||||||
/>
|
disabled={!canCommit}
|
||||||
) : (
|
loading={loadingAction === 'commit_selected'}
|
||||||
<Plus className='mr-1' size={12} />
|
icon={<CheckCircle />}
|
||||||
)}
|
text='Commit'
|
||||||
Stage Selected
|
className='bg-blue-600'
|
||||||
</button>
|
disabledTooltip='Select staged files and enter a commit message to enable committing'
|
||||||
</Tooltip>
|
/>
|
||||||
)}
|
|
||||||
{/* Commit */}
|
|
||||||
{selectedOutgoingChanges.length > 0 &&
|
|
||||||
commitMessage.trim() &&
|
|
||||||
selectionType !== 'unstaged' && (
|
|
||||||
<Tooltip content={getCommitButtonTooltip()}>
|
|
||||||
<button
|
|
||||||
onClick={() =>
|
|
||||||
onCommitSelected(
|
|
||||||
selectedOutgoingChanges,
|
|
||||||
commitMessage
|
|
||||||
)
|
|
||||||
}
|
|
||||||
className='flex items-center px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 transition-colors duration-200 ease-in-out text-xs'
|
|
||||||
disabled={
|
|
||||||
loadingAction === 'commit_selected'
|
|
||||||
}>
|
|
||||||
{loadingAction === 'commit_selected' ? (
|
|
||||||
<Loader
|
|
||||||
size={12}
|
|
||||||
className='animate-spin'
|
|
||||||
/>
|
|
||||||
) : (
|
|
||||||
<CheckCircle
|
|
||||||
className='mr-1'
|
|
||||||
size={12}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
Commit Selected
|
|
||||||
</button>
|
|
||||||
</Tooltip>
|
|
||||||
)}
|
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{/* Revert */}
|
<ActionButton
|
||||||
{selectedOutgoingChanges.length > 0 && (
|
onClick={() => onRevertSelected(selectedOutgoingChanges)}
|
||||||
<Tooltip content={getRevertButtonTooltip()}>
|
disabled={!canRevert}
|
||||||
<button
|
loading={loadingAction === 'revert_selected'}
|
||||||
onClick={() =>
|
icon={<RotateCcw />}
|
||||||
onRevertSelected(selectedOutgoingChanges)
|
text='Revert'
|
||||||
}
|
className='bg-red-600'
|
||||||
className='flex items-center px-4 py-2 bg-red-600 text-white rounded-md hover:bg-red-700 transition-colors duration-200 ease-in-out text-xs'
|
disabledTooltip='Select files to revert'
|
||||||
disabled={loadingAction === 'revert_selected'}>
|
/>
|
||||||
{loadingAction === 'revert_selected' ? (
|
<ActionButton
|
||||||
<Loader size={12} className='animate-spin' />
|
onClick={() => onPullSelected(selectedIncomingChanges)}
|
||||||
) : (
|
disabled={!canPull}
|
||||||
<RotateCcw className='mr-1' size={12} />
|
loading={loadingAction === 'pull_changes'}
|
||||||
)}
|
icon={<Download />}
|
||||||
Revert Selected
|
text='Pull'
|
||||||
</button>
|
className='bg-yellow-600'
|
||||||
</Tooltip>
|
disabledTooltip='Select incoming changes to pull'
|
||||||
)}
|
/>
|
||||||
{/* Pull */}
|
|
||||||
{selectedIncomingChanges.length > 0 && (
|
|
||||||
<Tooltip content='Pull selected changes'>
|
|
||||||
<button
|
|
||||||
onClick={() => onPullSelected(selectedIncomingChanges)}
|
|
||||||
className='flex items-center px-4 py-2 bg-yellow-600 text-white rounded-md hover:bg-yellow-700 transition-colors duration-200 ease-in-out text-xs'
|
|
||||||
disabled={loadingAction === 'pull_changes'}>
|
|
||||||
{loadingAction === 'pull_changes' ? (
|
|
||||||
<Loader size={12} className='animate-spin' />
|
|
||||||
) : (
|
|
||||||
<Download className='mr-1' size={12} />
|
|
||||||
)}
|
|
||||||
Pull Selected
|
|
||||||
</button>
|
|
||||||
</Tooltip>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -109,22 +109,46 @@ const StatusContainer = ({
|
|||||||
}
|
}
|
||||||
}, [status]);
|
}, [status]);
|
||||||
|
|
||||||
|
const hasChanges =
|
||||||
|
status.incoming_changes.length > 0 ||
|
||||||
|
status.outgoing_changes.length > 0;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='dark:bg-gray-800 border border-gray-200 dark:border-gray-700 p-4 rounded-md'>
|
<div className='dark:bg-gray-800 border border-gray-200 dark:border-gray-700 p-4 rounded-md'>
|
||||||
<div className='flex items-center'>
|
<div className='flex items-center justify-between'>
|
||||||
<GitMerge className='mr-2 text-green-400' size={14} />
|
<div className='flex items-center'>
|
||||||
<h3 className='text-m font-semibold text-gray-100 mr-2'>
|
<GitMerge className='mr-2 text-green-400' size={14} />
|
||||||
Sync Status:
|
<h3 className='text-m font-semibold text-gray-100 mr-2'>
|
||||||
</h3>
|
Sync Status:
|
||||||
{status.incoming_changes.length === 0 &&
|
</h3>
|
||||||
status.outgoing_changes.length === 0 ? (
|
{!hasChanges ? (
|
||||||
<span className='text-m font-medium'>
|
<span className='text-m font-medium'>
|
||||||
{noChangesMessage}
|
{noChangesMessage}
|
||||||
</span>
|
</span>
|
||||||
) : (
|
) : (
|
||||||
<span className='text-gray-400 text-m flex items-center'>
|
<span className='text-gray-400 text-m flex items-center'>
|
||||||
Out of Date!
|
Out of Date!
|
||||||
</span>
|
</span>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
{!hasChanges && (
|
||||||
|
<div className='flex-shrink-0'>
|
||||||
|
<ActionButtons
|
||||||
|
isDevMode={isDevMode}
|
||||||
|
selectedOutgoingChanges={selectedOutgoingChanges}
|
||||||
|
selectedIncomingChanges={selectedIncomingChanges}
|
||||||
|
selectionType={selectionType}
|
||||||
|
commitMessage={commitMessage}
|
||||||
|
loadingAction={loadingAction}
|
||||||
|
onStageSelected={onStageSelected}
|
||||||
|
onCommitSelected={onCommitSelected}
|
||||||
|
onRevertSelected={onRevertSelected}
|
||||||
|
onPullSelected={onPullSelected}
|
||||||
|
getStageButtonTooltip={getStageButtonTooltip}
|
||||||
|
getCommitButtonTooltip={getCommitButtonTooltip}
|
||||||
|
getRevertButtonTooltip={getRevertButtonTooltip}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -160,30 +184,36 @@ const StatusContainer = ({
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<CommitSection
|
{hasChanges && (
|
||||||
status={status}
|
<>
|
||||||
commitMessage={commitMessage}
|
<CommitSection
|
||||||
setCommitMessage={setCommitMessage}
|
status={status}
|
||||||
loadingAction={loadingAction}
|
commitMessage={commitMessage}
|
||||||
hasIncomingChanges={status.incoming_changes.length > 0}
|
setCommitMessage={setCommitMessage}
|
||||||
isDevMode={isDevMode}
|
loadingAction={loadingAction}
|
||||||
/>
|
hasIncomingChanges={status.incoming_changes.length > 0}
|
||||||
|
isDevMode={isDevMode}
|
||||||
|
/>
|
||||||
|
|
||||||
<ActionButtons
|
<div className='mt-4 flex justify-end'>
|
||||||
isDevMode={isDevMode}
|
<ActionButtons
|
||||||
selectedOutgoingChanges={selectedOutgoingChanges}
|
isDevMode={isDevMode}
|
||||||
selectedIncomingChanges={selectedIncomingChanges}
|
selectedOutgoingChanges={selectedOutgoingChanges}
|
||||||
selectionType={selectionType}
|
selectedIncomingChanges={selectedIncomingChanges}
|
||||||
commitMessage={commitMessage}
|
selectionType={selectionType}
|
||||||
loadingAction={loadingAction}
|
commitMessage={commitMessage}
|
||||||
onStageSelected={onStageSelected}
|
loadingAction={loadingAction}
|
||||||
onCommitSelected={onCommitSelected}
|
onStageSelected={onStageSelected}
|
||||||
onRevertSelected={onRevertSelected}
|
onCommitSelected={onCommitSelected}
|
||||||
onPullSelected={onPullSelected}
|
onRevertSelected={onRevertSelected}
|
||||||
getStageButtonTooltip={getStageButtonTooltip}
|
onPullSelected={onPullSelected}
|
||||||
getCommitButtonTooltip={getCommitButtonTooltip}
|
getStageButtonTooltip={getStageButtonTooltip}
|
||||||
getRevertButtonTooltip={getRevertButtonTooltip}
|
getCommitButtonTooltip={getCommitButtonTooltip}
|
||||||
/>
|
getRevertButtonTooltip={getRevertButtonTooltip}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -14,18 +14,18 @@ export const statusLoadingMessages = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
export const noChangesMessages = [
|
export const noChangesMessages = [
|
||||||
'No changes detected. Your regex is so precise, it could find a needle in a haystack... made of needles. 🧵🔍',
|
'All synced up! Smooth sailing ahead.',
|
||||||
'All quiet on the commit front. Your custom formats are so perfect, even perfectionists are jealous. 🏆',
|
"No changes detected. We're all good here.",
|
||||||
"No updates needed. Your media automation is running so smoothly, it's making butter jealous. 🧈",
|
"Everything's in order. Carry on!",
|
||||||
'Zero modifications. Your torrent setup is seeding so efficiently, farmers are asking for advice. 🌾',
|
'Up to date and ready to go.',
|
||||||
"No edits required. Your regex fu is so strong, it's bench-pressing parentheses for fun. 💪()",
|
'All quiet on the regex front!',
|
||||||
'Unchanged status. Your Plex library is so well-organized, librarians are taking notes. 📚🤓',
|
'Perfectly synced! Nice work.',
|
||||||
"No alterations found. Your file naming scheme is so consistent, it's bringing tears to OCD eyes. 😢👀",
|
"No updates needed. You're all set.",
|
||||||
"All systems nominal. Your download queue is so orderly, it's making Marie Kondo question her career. 🧹✨",
|
"Everything's hunky-dory in your repository.",
|
||||||
"No revisions necessary. Your automation scripts are so smart, they're solving captchas for fun. 🤖🧩",
|
'All good in the code neighborhood.',
|
||||||
'Steady as she goes. Your media collection is so complete, Netflix is asking you for recommendations. 🎬👑'
|
'Sync complete! Ship-Shape.'
|
||||||
];
|
];
|
||||||
|
|
||||||
export const getRandomMessage = (messages) => {
|
export const getRandomMessage = messages => {
|
||||||
return messages[Math.floor(Math.random() * messages.length)];
|
return messages[Math.floor(Math.random() * messages.length)];
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user