feat(branch): Improve styling

This commit is contained in:
santiagosayshey
2024-08-24 17:56:09 +09:30
committed by Sam Chau
parent 650cfa2dc7
commit 06a230eeba

View File

@@ -1,17 +1,34 @@
import React, { useState, useEffect } from 'react';
import Modal from '../ui/Modal';
import { getBranches, checkoutBranch, createBranch, deleteBranch } from '../../api/api';
import { ExternalLink, Trash2, GitBranchPlus, ArrowRightCircle, Loader } from 'lucide-react';
import Tooltip from '../ui/Tooltip';
import Alert from '../ui/Alert';
import React, { useState, useEffect } from "react";
import Modal from "../ui/Modal";
import {
getBranches,
checkoutBranch,
createBranch,
deleteBranch,
} from "../../api/api";
import {
ExternalLink,
Trash2,
GitBranchPlus,
ArrowRightCircle,
Loader,
} from "lucide-react";
import Tooltip from "../ui/Tooltip";
import Alert from "../ui/Alert";
const SettingsBranchModal = ({ isOpen, onClose, repoUrl, currentBranch, onBranchChange }) => {
const SettingsBranchModal = ({
isOpen,
onClose,
repoUrl,
currentBranch,
onBranchChange,
}) => {
const [branches, setBranches] = useState([]);
const [branchOffMode, setBranchOffMode] = useState(null);
const [newBranchName, setNewBranchName] = useState('');
const [newBranchName, setNewBranchName] = useState("");
const [validBranchName, setValidBranchName] = useState(true);
const [branchToDelete, setBranchToDelete] = useState(null);
const [loadingAction, setLoadingAction] = useState('');
const [loadingAction, setLoadingAction] = useState("");
useEffect(() => {
if (isOpen) {
@@ -26,19 +43,19 @@ const SettingsBranchModal = ({ isOpen, onClose, repoUrl, currentBranch, onBranch
if (response.success && response.data.branches) {
setBranches(response.data.branches);
} else {
console.error('Error fetching branches:', response.data.error);
console.error("Error fetching branches:", response.data.error);
}
} catch (error) {
console.error('Error fetching branches:', error);
console.error("Error fetching branches:", error);
}
};
const resetForm = () => {
setBranchOffMode(null);
setNewBranchName('');
setNewBranchName("");
setValidBranchName(true);
setBranchToDelete(null);
setLoadingAction('');
setLoadingAction("");
};
const handleCheckout = async (branchName, isNewBranch = false) => {
@@ -49,56 +66,68 @@ const SettingsBranchModal = ({ isOpen, onClose, repoUrl, currentBranch, onBranch
// Refresh branches after successful checkout
await fetchBranches();
// Notify parent component to update the status
onBranchChange(); // <-- Call the callback to update status in the parent component
onBranchChange(); // <-- Call the callback to update status in the parent component
if (!isNewBranch) {
Alert.success('Branch checked out successfully');
Alert.success("Branch checked out successfully");
}
onClose(); // Close the modal
} else {
Alert.error(response.error); // Use the Alert component for error
}
} catch (error) {
if (error.response && error.response.status === 400 && error.response.data.error) {
if (
error.response &&
error.response.status === 400 &&
error.response.data.error
) {
Alert.error(error.response.data.error); // Show alert with specific backend error message
} else {
Alert.error('An unexpected error occurred while checking out the branch.');
console.error('Error checking out branch:', error);
Alert.error(
"An unexpected error occurred while checking out the branch."
);
console.error("Error checking out branch:", error);
}
} finally {
setLoadingAction('');
setLoadingAction("");
}
};
const handleBranchOff = async () => {
setLoadingAction('branchOff');
setLoadingAction("branchOff");
if (newBranchName && validBranchName) {
try {
const response = await createBranch(newBranchName, branchOffMode);
if (response.success) {
// Checkout the new branch without showing the checkout alert
await handleCheckout(newBranchName, true);
Alert.success('Branch created and checked out successfully');
Alert.success("Branch created and checked out successfully");
} else {
Alert.error(response.error); // Handle known errors from the backend
}
} catch (error) {
if (error.response && error.response.status === 400 && error.response.data.error) {
if (
error.response &&
error.response.status === 400 &&
error.response.data.error
) {
Alert.error(error.response.data.error); // Specific error from the backend
} else {
console.error('Error branching off:', error); // Log unexpected errors
Alert.error('An unexpected error occurred while creating the branch. Please try again.');
console.error("Error branching off:", error); // Log unexpected errors
Alert.error(
"An unexpected error occurred while creating the branch. Please try again."
);
}
} finally {
setLoadingAction('');
setLoadingAction("");
}
} else {
Alert.error('Please enter a valid branch name.');
Alert.error("Please enter a valid branch name.");
}
};
const handleBranchOffClick = (branchName) => {
setBranchOffMode(branchName);
setNewBranchName('');
setNewBranchName("");
setValidBranchName(true);
};
@@ -110,7 +139,7 @@ const SettingsBranchModal = ({ isOpen, onClose, repoUrl, currentBranch, onBranch
const handleOpenInGitHub = (branchName) => {
const branchUrl = `${repoUrl}/tree/${encodeURIComponent(branchName)}`;
window.open(branchUrl, '_blank');
window.open(branchUrl, "_blank");
};
const confirmDeleteBranch = (branchName) => {
@@ -118,7 +147,7 @@ const SettingsBranchModal = ({ isOpen, onClose, repoUrl, currentBranch, onBranch
};
const handleDeleteBranch = async () => {
if (branchToDelete && branchToDelete.toLowerCase() === 'main') {
if (branchToDelete && branchToDelete.toLowerCase() === "main") {
Alert.warning("The 'main' branch cannot be deleted.");
return;
}
@@ -126,7 +155,7 @@ const SettingsBranchModal = ({ isOpen, onClose, repoUrl, currentBranch, onBranch
try {
const response = await deleteBranch(branchToDelete);
if (response.success) {
onBranchChange(); // <-- Call the callback to update status in the parent component
onBranchChange(); // <-- Call the callback to update status in the parent component
await fetchBranches(); // Refresh the list after deletion
Alert.success(`Branch '${branchToDelete}' deleted successfully`);
setBranchToDelete(null);
@@ -134,43 +163,60 @@ const SettingsBranchModal = ({ isOpen, onClose, repoUrl, currentBranch, onBranch
Alert.error(response.error); // Use the Alert component for error
}
} catch (error) {
Alert.error('An unexpected error occurred while deleting the branch.');
console.error('Error deleting branch:', error);
Alert.error("An unexpected error occurred while deleting the branch.");
console.error("Error deleting branch:", error);
} finally {
setLoadingAction('');
setLoadingAction("");
}
};
return (
<Modal isOpen={isOpen} onClose={onClose} title="Manage Git Branches">
<div className="space-y-4 text-xs">
<div>
<h3 className="text-lg font-medium mb-2 text-gray-100">Branches:</h3>
<ul className="space-y-2">
<div className="space-y-6 text-sm">
<div className="bg-gray-800 rounded-lg p-4 shadow-inner">
<h3 className="text-lg font-semibold mb-3 text-gray-100 border-b border-gray-700 pb-2">
Branches
</h3>
<ul className="space-y-3">
{branches.map((branch, index) => (
<li
key={index} // Ensure you use a unique key
className={`flex items-center justify-between p-2 rounded ${
branch.name === currentBranch ? 'border border-blue-400 bg-gray-800' : 'bg-gray-700'
key={index}
className={`flex items-center justify-between p-3 rounded-md transition-all duration-200 ${
branch.name === currentBranch
? "bg-blue-900/30 border border-blue-500 shadow-md"
: "bg-gray-700 hover:bg-gray-650"
}`}
>
<div className="flex items-center space-x-2">
<span className={branch.name === currentBranch ? 'text-white' : 'text-gray-300'}>
{branch.name ? branch.name : 'Unknown Branch'} {/* Fallback if branch.name is undefined */}
<div className="flex items-center space-x-3">
<div
className={`w-2 h-2 rounded-full ${
branch.name === currentBranch
? "bg-blue-400"
: "bg-gray-400"
}`}
></div>
<span
className={`font-medium ${
branch.name === currentBranch
? "text-blue-200"
: "text-gray-200"
}`}
>
{branch.name || "Unknown Branch"}
</span>
</div>
<div className="flex items-center space-x-1">
<div className="flex items-center space-x-2">
{branch.name !== currentBranch && (
<Tooltip content="Checkout">
<button
onClick={() => handleCheckout(branch.name)}
className="p-1 bg-blue-500 text-white rounded hover:bg-blue-600 hover:scale-105 transition-transform duration-200"
className="p-1.5 bg-blue-500 text-white rounded-md hover:bg-blue-600 hover:scale-105 transition-all duration-200 shadow-sm"
disabled={loadingAction === `checkout-${branch.name}`}
>
{loadingAction === `checkout-${branch.name}` ? (
<Loader size={14} className="animate-spin" />
<Loader size={16} className="animate-spin" />
) : (
<ArrowRightCircle size={14} />
<ArrowRightCircle size={16} />
)}
</button>
</Tooltip>
@@ -178,37 +224,38 @@ const SettingsBranchModal = ({ isOpen, onClose, repoUrl, currentBranch, onBranch
<Tooltip content="Branch Off">
<button
onClick={() => handleBranchOffClick(branch.name)}
className="p-1 bg-green-500 text-white rounded hover:bg-green-600 hover:scale-105 transition-transform duration-200"
disabled={loadingAction === 'branchOff'}
className="p-1.5 bg-green-500 text-white rounded-md hover:bg-green-600 hover:scale-105 transition-all duration-200 shadow-sm"
disabled={loadingAction === "branchOff"}
>
{loadingAction === 'branchOff' ? (
<Loader size={14} className="animate-spin" />
{loadingAction === "branchOff" ? (
<Loader size={16} className="animate-spin" />
) : (
<GitBranchPlus size={14} />
<GitBranchPlus size={16} />
)}
</button>
</Tooltip>
{branch.name !== currentBranch && branch.name.toLowerCase() !== 'main' && (
<Tooltip content="Delete">
<button
onClick={() => confirmDeleteBranch(branch.name)}
className="p-1 bg-red-500 text-white rounded hover:bg-red-600 hover:scale-105 transition-transform duration-200"
disabled={loadingAction === `delete-${branch.name}`}
>
{loadingAction === `delete-${branch.name}` ? (
<Loader size={14} className="animate-spin" />
) : (
<Trash2 size={14} />
)}
</button>
</Tooltip>
)}
{branch.name !== currentBranch &&
branch.name.toLowerCase() !== "main" && (
<Tooltip content="Delete">
<button
onClick={() => confirmDeleteBranch(branch.name)}
className="p-1.5 bg-red-500 text-white rounded-md hover:bg-red-600 hover:scale-105 transition-all duration-200 shadow-sm"
disabled={loadingAction === `delete-${branch.name}`}
>
{loadingAction === `delete-${branch.name}` ? (
<Loader size={16} className="animate-spin" />
) : (
<Trash2 size={16} />
)}
</button>
</Tooltip>
)}
<Tooltip content="View on GitHub">
<button
onClick={() => handleOpenInGitHub(branch.name)}
className="p-1 bg-gray-600 text-white rounded hover:bg-gray-700 hover:scale-105 transition-transform duration-200"
className="p-1.5 bg-gray-600 text-white rounded-md hover:bg-gray-500 hover:scale-105 transition-all duration-200 shadow-sm"
>
<ExternalLink size={14} />
<ExternalLink size={16} />
</button>
</Tooltip>
</div>
@@ -217,37 +264,54 @@ const SettingsBranchModal = ({ isOpen, onClose, repoUrl, currentBranch, onBranch
</ul>
</div>
{branchOffMode && (
<div className="flex items-center space-x-2 mt-4">
<input
type="text"
value={newBranchName}
onChange={(e) => validateBranchName(e.target.value)}
placeholder={`New branch from ${branchOffMode}`}
className={`flex-grow p-2 border rounded bg-gray-800 text-gray-300 ${!validBranchName ? 'border-red-500' : ''}`}
/>
<button
onClick={handleBranchOff}
disabled={!newBranchName || !validBranchName || loadingAction === 'branchOff'}
className="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600 transition-colors text-xs"
>
{loadingAction === 'branchOff' ? 'Creating...' : 'Create'}
</button>
<div className="bg-gray-700 p-4 rounded-lg shadow-md">
<h4 className="text-sm font-semibold mb-2 text-gray-200">
Create New Branch
</h4>
<div className="flex items-center space-x-2">
<input
type="text"
value={newBranchName}
onChange={(e) => validateBranchName(e.target.value)}
placeholder={`New branch from ${branchOffMode}`}
className={`flex-grow p-2 border rounded bg-gray-800 text-gray-300 focus:ring-2 focus:ring-blue-500 transition-all ${
!validBranchName ? "border-red-500" : "border-gray-600"
}`}
/>
<button
onClick={handleBranchOff}
disabled={
!newBranchName ||
!validBranchName ||
loadingAction === "branchOff"
}
className="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600 transition-colors text-sm font-medium shadow-sm"
>
{loadingAction === "branchOff" ? "Creating..." : "Create"}
</button>
</div>
</div>
)}
{branchToDelete && (
<div className="mt-4 text-sm text-gray-200">
<p>Are you sure you want to delete the branch <strong>{branchToDelete}</strong>? This action cannot be undone.</p>
<div className="flex space-x-4 mt-2">
<div className="bg-red-900/30 border border-red-500 rounded-lg p-4 mt-4 text-sm text-gray-200">
<p className="mb-3">
Are you sure you want to delete the branch{" "}
<strong className="text-red-300">{branchToDelete}</strong>? This
action cannot be undone.
</p>
<div className="flex space-x-4">
<button
onClick={handleDeleteBranch}
disabled={loadingAction === `delete-${branchToDelete}`}
className="px-4 py-2 bg-red-500 text-white rounded hover:bg-red-600 transition-colors text-xs"
className="px-4 py-2 bg-red-500 text-white rounded hover:bg-red-600 transition-colors text-sm font-medium shadow-sm"
>
{loadingAction === `delete-${branchToDelete}` ? 'Deleting...' : 'Confirm Delete'}
{loadingAction === `delete-${branchToDelete}`
? "Deleting..."
: "Confirm Delete"}
</button>
<button
onClick={() => setBranchToDelete(null)}
className="px-4 py-2 bg-gray-500 text-white rounded hover:bg-gray-600 transition-colors text-xs"
className="px-4 py-2 bg-gray-500 text-white rounded hover:bg-gray-600 transition-colors text-sm font-medium shadow-sm"
>
Cancel
</button>