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