Refactoring and styling fixes in Manage-Cookie UI

This commit is contained in:
ramki-bruno
2025-03-07 13:07:46 +05:30
committed by Anoop M D
parent 51c86bc0e9
commit d5e828aef2
4 changed files with 298 additions and 285 deletions

View File

@@ -69,7 +69,7 @@ const ModifyCookieModal = ({ onClose, domain, cookie }) => {
const handleCookieDispatch = (cookie, domain, modValues, onClose) => {
if (cookie) {
dispatch(modifyCookie(domain, cookie, cookie.path, cookie.key, modValues))
dispatch(modifyCookie(domain, cookie, modValues))
.then(() => {
toast.success('Cookie modified successfully');
onClose();
@@ -241,6 +241,36 @@ const ModifyCookieModal = ({ onClose, domain, cookie }) => {
) : (
<div className="space-y-4">
<div className="grid grid-cols-2 gap-4">
<div>
<label className="block text-sm mb-1">
Domain<span className="text-red-600">*</span>{' '}
</label>
<input
type="text"
name="domain"
value={formik.values.domain}
onChange={formik.handleChange}
className="block textbox non-passphrase-input w-full disabled:opacity-50"
disabled={!!cookie}
/>
{formik.touched.domain && formik.errors.domain && (
<div className="text-red-500 text-sm mt-1">{formik.errors.domain}</div>
)}
</div>
<div>
<label className="block text-sm mb-1">Path</label>
<input
type="text"
name="path"
value={formik.values.path}
onChange={formik.handleChange}
className="block textbox non-passphrase-input w-full disabled:opacity-50"
disabled={!!cookie}
/>
{formik.touched.path && formik.errors.path && (
<div className="text-red-500 text-sm mt-1">{formik.errors.path}</div>
)}
</div>
<div>
<label className="block text-sm mb-1">
Key<span className="text-red-600">*</span>{' '}
@@ -273,36 +303,6 @@ const ModifyCookieModal = ({ onClose, domain, cookie }) => {
<div className="text-red-500 text-sm mt-1">{formik.errors.value}</div>
)}
</div>
<div>
<label className="block text-sm mb-1">
Domain<span className="text-red-600">*</span>{' '}
</label>
<input
type="text"
name="domain"
value={formik.values.domain}
onChange={formik.handleChange}
className="block textbox non-passphrase-input w-full disabled:opacity-50"
disabled={!!cookie}
/>
{formik.touched.domain && formik.errors.domain && (
<div className="text-red-500 text-sm mt-1">{formik.errors.domain}</div>
)}
</div>
<div>
<label className="block text-sm mb-1">Path</label>
<input
type="text"
name="path"
value={formik.values.path}
onChange={formik.handleChange}
className="block textbox non-passphrase-input w-full disabled:opacity-50"
disabled={!!cookie}
/>
{formik.touched.path && formik.errors.path && (
<div className="text-red-500 text-sm mt-1">{formik.errors.path}</div>
)}
</div>
</div>
{/* Date Picker */}

View File

@@ -34,19 +34,31 @@ const Wrapper = styled.div`
.scroll-box {
max-height: 500px;
overflow-y: auto;
-webkit-overflow-scrolling: touch;
overflow-scrolling: touch;
background:
/* Shadow Cover TOP */ linear-gradient(
${(props) => props.theme.modal.body.bg} 20%,
rgba(255, 255, 255, 0)
)
center top,
/* Shadow Cover BOTTOM */ linear-gradient(rgba(255, 255, 255, 0), ${(props) => props.theme.modal.body.bg} 80%)
center bottom,
/* Shadow TOP */ linear-gradient(rgba(0, 0, 0, 0.1) 0%, rgba(0, 0, 0, 0) 100%) center top,
/* Shadow BOTTOM */ linear-gradient(rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 0.1) 100%) center bottom;
/* Shadow Cover TOP */
linear-gradient(
${(props) => props.theme.modal.body.bg} 20%,
rgba(255, 255, 255, 0)
) center top,
/* Shadow Cover BOTTOM */
linear-gradient(
rgba(255, 255, 255, 0),
${(props) => props.theme.modal.body.bg} 80%
) center bottom,
/* Shadow TOP */
linear-gradient(
rgba(0, 0, 0, 0.1) 0%,
rgba(0, 0, 0, 0) 100%
) center top,
/* Shadow BOTTOM */
linear-gradient(
rgba(0, 0, 0, 0) 0%,
rgba(0, 0, 0, 0.1) 100%
) center bottom;
background-repeat: no-repeat;
background-size: 100% 30px, 100% 30px, 100% 10px, 100% 10px;

View File

@@ -10,20 +10,69 @@ import StyledWrapper from './StyledWrapper';
import moment from 'moment';
import { Tooltip } from 'react-tooltip';
const ClearDomainCookiesModal = ({ onClose, domain, onClear }) => (
<Modal onClose={onClose} handleCancel={onClose} title="Clear Domain Cookies" hideFooter={true}>
<div className="flex items-center font-normal">
<IconAlertTriangle size={32} strokeWidth={1.5} className="text-yellow-600" />
<h1 className="ml-2 text-lg font-semibold">Hold on..</h1>
</div>
<div className="font-normal mt-4">
Are you sure you want to clear all cookies for the domain {domain}?
</div>
<div className="flex justify-between mt-6">
<div>
<button className="btn btn-sm btn-close" onClick={onClose}>
Close
</button>
</div>
<div>
<button className="btn btn-sm btn-danger" onClick={onClear}>
Clear All
</button>
</div>
</div>
</Modal>
);
const DeleteCookieModal = ({ onClose, cookieName, onDelete }) => (
<Modal onClose={onClose} handleCancel={onClose} title="Delete Cookie" hideFooter={true}>
<div className="flex items-center font-normal">
<IconAlertTriangle size={32} strokeWidth={1.5} className="text-yellow-600" />
<h1 className="ml-2 text-lg font-semibold">Hold on..</h1>
</div>
<div className="font-normal mt-4">
Are you sure you want to delete the cookie {cookieName}?
</div>
<div className="flex justify-between mt-6">
<div>
<button className="btn btn-sm btn-close" onClick={onClose}>
Close
</button>
</div>
<div>
<button className="btn btn-sm btn-danger" onClick={onDelete}>
Delete
</button>
</div>
</div>
</Modal>
);
const CollectionProperties = ({ onClose }) => {
const dispatch = useDispatch();
const cookies = useSelector((state) => state.app.cookies) || [];
const [isModifyCookieModalOpen, setIsModifyCookieModalOpen] = useState(false);
const [currentDomain, setCurrentDomain] = useState('');
const [currentDomain, setCurrentDomain] = useState(null);
const [cookieToEdit, setCookieToEdit] = useState(null);
const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
const [deleteModalContent, setDeleteModalContent] = useState(null);
const [deleteModalTitle, setDeleteModalTitle] = useState('');
const [onDeleteAction, setOnDeleteAction] = useState(() => {});
const [searchText, setSearchText] = useState('');
const [domainToClear, setDomainToClear] = useState(null);
const [cookieToDelete, setCookieToDelete] = useState(null);
const [searchText, setSearchText] = useState(null);
const handleAddCookie = (domain) => {
setCurrentDomain(domain);
if(domain) setCurrentDomain(domain);
setIsModifyCookieModalOpen(true);
};
@@ -33,124 +82,44 @@ const CollectionProperties = ({ onClose }) => {
setIsModifyCookieModalOpen(true);
};
const openModal = (title, content, onDelete) => {
setDeleteModalTitle(title);
setDeleteModalContent(content);
setOnDeleteAction(() => onDelete);
setIsDeleteModalOpen(true);
const handleClearDomainCookies = (domain) => {
setDomainToClear(domain);
};
const closeDeleteModal = () => {
setIsDeleteModalOpen(false);
};
const handleDeleteDomain = (domain) => {
openModal('Delete Domain', `Are you sure you want to delete the domain ${domain}?`, () => {
dispatch(deleteCookiesForDomain(domain))
.then(() => {
toast.success('Domain deleted successfully');
})
.catch((err) => console.log(err) && toast.error('Failed to delete domain'));
closeDeleteModal();
});
const clearDomainCookiesAction = () => {
dispatch(deleteCookiesForDomain(domainToClear))
.then(() => {
toast.success('Domain cookies cleared successfully');
})
.catch((err) => console.log(err) && toast.error('Failed to clear domain cookies'));
setDomainToClear(null);
};
const handleDeleteCookie = (domain, path, key) => {
openModal('Delete Cookie', `Are you sure you want to delete the cookie ${key}?`, () => {
setCookieToDelete({ key, domain, path });
};
const deleteCookieAction = () => {
if (cookieToDelete) {
const { domain, path, key } = cookieToDelete;
dispatch(deleteCookie(domain, path, key))
.then(() => {
toast.success('Cookie deleted successfully');
})
.catch((err) => console.log(err) && toast.error('Failed to delete cookie'));
closeDeleteModal();
});
}
setCookieToDelete(null);
};
const filteredCookies = useMemo(() => {
return cookies.filter((cookie) => cookie.domain.toLowerCase().includes(searchText.toLowerCase()));
if (!searchText) return cookies;
return cookies.filter((cookie) =>
cookie.domain.toLowerCase().includes(searchText.toLowerCase())
);
}, [cookies, searchText]);
if (!cookies || !cookies.length) {
return (
<>
<Modal size="xl" title="Cookies" hideFooter={true} handleCancel={onClose}>
<StyledWrapper>
<div className="flex items-center justify-center flex-col">
<IconCookieOff size={48} />
<h2 className="text-lg font-semibold mt-4">No cookies found</h2>
<p className="text-gray-500 mt-2">Add cookies to get started</p>
<button
type="submit"
className="submit btn btn-sm btn-secondary flex items-center gap-1 mt-4"
onClick={() => {
handleAddCookie('');
}}
>
<IconCirclePlus strokeWidth={1.5} size={16} />
<span>Add cookie</span>
</button>
</div>
</StyledWrapper>
</Modal>
{isModifyCookieModalOpen && (
<ModifyCookieModal
onClose={() => {
setCookieToEdit(null);
setCurrentDomain('');
setIsModifyCookieModalOpen(false);
}}
domain={currentDomain}
cookie={cookieToEdit}
/>
)}
</>
);
}
if (cookies.length && !filteredCookies.length) {
return (
<>
<Modal
size="xl"
title="Cookies"
hideFooter={true}
handleCancel={onClose}
customHeader={
<StyledWrapper className="flex items-center justify-between w-full">
<h2 className="text-sm font-semibold">Cookies</h2>
<input
type="search"
placeholder="Search by domain"
value={searchText}
onChange={(e) => {
setSearchText(e.target.value);
}}
className="block textbox non-passphrase-input h-9 ml-auto"
/>
<button
type="submit"
className="submit btn btn-sm h-9 btn-secondary flex items-center gap-1 mx-4"
onClick={() => {
handleAddCookie('');
}}
>
<IconCirclePlus strokeWidth={1.5} size={16} />
<span>Add New</span>
</button>
</StyledWrapper>
}
>
<StyledWrapper>
<div className="flex items-center justify-center flex-col">
<IconSearch size={48} />
<h2 className="text-lg font-semibold mt-4">No search results</h2>
<p className="text-gray-500 mt-2">Try a different search term</p>
</div>
</StyledWrapper>
</Modal>
</>
);
}
const shouldShowHeader = cookies && cookies.length > 0;
return (
<>
@@ -159,171 +128,203 @@ const CollectionProperties = ({ onClose }) => {
title="Cookies"
hideFooter={true}
handleCancel={onClose}
customHeader={
customHeader={shouldShowHeader ? (
<StyledWrapper className="flex items-center justify-between w-full">
<h2 className="text-sm font-semibold">Cookies</h2>
<input
type="search"
placeholder="Search by domain"
value={searchText}
value={searchText || ''}
onChange={(e) => setSearchText(e.target.value)}
className="block textbox non-passphrase-input h-9 ml-auto"
/>
<button
type="submit"
className="submit btn btn-sm h-9 btn-secondary flex items-center gap-1 mx-4"
onClick={() => {
handleAddCookie('');
onClick={(e) => {
e.stopPropagation();
handleAddCookie();
}}
>
<IconCirclePlus strokeWidth={1.5} size={16} />
<span>Add New</span>
</button>
</StyledWrapper>
}
) : null}
>
<StyledWrapper>
<div className="scroll-box">
<Accordion defaultIndex={0}>
{filteredCookies.map((domainWithCookies, i) => (
<Accordion.Item key={i} index={i}>
<Accordion.Header index={i} className="flex items-center">
<div className="flex items-center">
<span>{domainWithCookies.domain}</span>
<span className="ml-2 text-xs dark:text-gray-300 text-gray-500">
({domainWithCookies.cookies.length}{' '}
{domainWithCookies.cookies.length === 1 ? 'cookie' : 'cookies'})
</span>
<div className="ml-auto flex items-center gap-2">
<button
type="submit"
className="flex items-center gap-1 text-gray-500 hover:text-gray-950 dark:text-white dark:hover:text-gray-300"
onClick={(e) => {
e.stopPropagation();
handleAddCookie(domainWithCookies.domain);
}}
>
<IconCirclePlus strokeWidth={1.5} size={16} />
</button>
<button
onClick={(e) => {
e.stopPropagation();
handleDeleteDomain(domainWithCookies.domain);
}}
className="text-gray-950 dark:text-white dark:hover:hover:text-red-600 hover:text-red-600 mr-2"
>
<IconTrash strokeWidth={1.5} size={16} />
</button>
{!cookies || !cookies.length ? (
// No cookies found
<div className="flex items-center justify-center flex-col">
<IconCookieOff size={48} />
<h2 className="text-lg font-semibold mt-4">No cookies found</h2>
<p className="text-gray-500 mt-2">Add cookies to get started</p>
<button
type="submit"
className="submit btn btn-sm btn-secondary flex items-center gap-1 mt-4"
onClick={(e) => {
e.stopPropagation();
handleAddCookie();
}}
>
<IconCirclePlus strokeWidth={1.5} size={16} />
<span>Add New</span>
</button>
</div>
) : cookies.length && !filteredCookies.length ? (
// No search results
<div className="flex items-center justify-center flex-col">
<IconSearch size={48} />
<h2 className="text-lg font-semibold mt-4">No search results</h2>
<p className="text-gray-500 mt-2">Try a different search term</p>
</div>
) : (
// Show cookies list
<div className="scroll-box">
<Accordion defaultIndex={0}>
{filteredCookies.map((domainWithCookies, i) => (
<Accordion.Item key={i} index={i}>
<Accordion.Header index={i} className="flex items-center">
<div className="flex items-center">
<span>{domainWithCookies.domain}</span>
<span className="ml-2 text-xs dark:text-gray-300 text-gray-500">
({domainWithCookies.cookies.length}{' '}
{domainWithCookies.cookies.length === 1 ? 'cookie' : 'cookies'})
</span>
<div className="ml-auto flex items-center gap-2">
<button
type="submit"
className="flex items-center gap-1 text-gray-500 hover:text-gray-950 dark:text-white dark:hover:text-gray-300"
onClick={(e) => {
e.stopPropagation();
handleAddCookie(domainWithCookies.domain);
}}
>
<IconCirclePlus strokeWidth={1.5} size={16} />
</button>
<button
onClick={(e) => {
e.stopPropagation();
handleClearDomainCookies(domainWithCookies.domain);
}}
className="text-gray-950 dark:text-white dark:hover:hover:text-red-600 hover:text-red-600 mr-2"
>
<IconTrash strokeWidth={1.5} size={16} />
</button>
</div>
</div>
</div>
</Accordion.Header>
<Accordion.Content index={i}>
<div className="flex items-center justify-between">
<table className="w-full">
<thead>
<tr className="text-left border-b border-gray-300 dark:border-gray-500">
<th className="py-2 px-4 font-medium w-32">Name</th>
<th className="py-2 px-4 font-medium w-52">Value</th>
<th className="py-2 px-4 font-medium">Path</th>
<th className="py-2 px-4 font-medium">Expires</th>
<th className="py-2 px-4 font-medium text-center">Secure</th>
<th className="py-2 px-4 font-medium text-center">HTTP Only</th>
<th className="py-2 px-4 font-medium text-right w-24">Actions</th>
</tr>
</thead>
<tbody>
{domainWithCookies.cookies.map((cookie) => (
<tr key={cookie.key} className="border-b border-gray-300 dark:border-gray-500">
<td className="py-2 px-4 truncate">{cookie.key}</td>
<td className="py-2 px-4 truncate">
<span id={`cookie-value-${cookie.key}`}>{cookie.value}</span>
<Tooltip
anchorId={`cookie-value-${cookie.key}`}
className="tooltip-mod"
html={cookie.value}
/>
</td>
<td className="py-2 px-4 truncate">{cookie.path || '/'}</td>
<td className="py-2 px-4 truncate">
<span id={`cookie-expires-${cookie.key}`}>
{cookie.expires && moment(cookie.expires).isValid()
? new Date(cookie.expires).toLocaleString()
: 'Session Cookie'}
</span>
{cookie.expires && moment(cookie.expires).isValid() && (
<Tooltip
anchorId={`cookie-expires-${cookie.key}`}
className="tooltip-mod"
html={new Date(cookie.expires).toLocaleString()}
/>
)}
</td>
<td className="py-2 px-4 text-center">{cookie.secure ? '✓' : ''}</td>
<td className="py-2 px-4 text-center">{cookie.httpOnly ? '✓' : ''}</td>
<td className="py-2 px-4">
<div className="flex items-center justify-end gap-2">
<button
onClick={() => handleEditCookie(domainWithCookies.domain, cookie)}
className="text-gray-700 hover:text-gray-950
dark:text-white dark:hover:text-gray-300"
>
<IconEdit strokeWidth={1.5} size={16} />
</button>
<button
onClick={() =>
handleDeleteCookie(domainWithCookies.domain, cookie.path, cookie.key)
}
className="text-gray-950 dark:text-white dark:hover:hover:text-red-600 hover:text-red-600"
>
<IconTrash strokeWidth={1.5} size={16} />
</button>
</div>
</td>
</Accordion.Header>
<Accordion.Content index={i}>
<div className="flex items-center justify-between">
<table className="w-full">
<thead>
<tr className="text-left border-b border-gray-300 dark:border-neutral-600">
<th className="py-2 px-4 font-medium w-32">Name</th>
<th className="py-2 px-4 font-medium w-52">Value</th>
<th className="py-2 px-4 font-medium">Path</th>
<th className="py-2 px-4 font-medium">Expires</th>
<th className="py-2 px-4 font-medium text-center">Secure</th>
<th className="py-2 px-4 font-medium text-center">HTTP Only</th>
<th className="py-2 px-4 font-medium text-right w-24">Actions</th>
</tr>
))}
</tbody>
</table>
</div>
</Accordion.Content>
</Accordion.Item>
))}
</Accordion>
</div>
</thead>
<tbody>
{domainWithCookies.cookies.map((cookie) => (
<tr key={cookie.key} className="border-b border-gray-300 dark:border-neutral-600 last:border-none">
<td className="py-2 px-4 truncate">
<span id={`cookie-key-${cookie.key}`}>{cookie.key}</span>
<Tooltip
anchorId={`cookie-key-${cookie.key}`}
className="tooltip-mod"
html={cookie.key}
/>
</td>
<td className="py-2 px-4 truncate">
<span id={`cookie-value-${cookie.key}`}>{cookie.value}</span>
<Tooltip
anchorId={`cookie-value-${cookie.key}`}
className="tooltip-mod"
html={cookie.value}
/>
</td>
<td className="py-2 px-4 truncate">{cookie.path || '/'}</td>
<td className="py-2 px-4 truncate">
<span id={`cookie-expires-${cookie.key}`}>
{cookie.expires && moment(cookie.expires).isValid()
? new Date(cookie.expires).toLocaleString()
: 'Session'}
</span>
{cookie.expires && moment(cookie.expires).isValid() && (
<Tooltip
anchorId={`cookie-expires-${cookie.key}`}
className="tooltip-mod"
html={new Date(cookie.expires).toLocaleString()}
/>
)}
</td>
<td className="py-2 px-4 text-center">{cookie.secure ? '✓' : ''}</td>
<td className="py-2 px-4 text-center">{cookie.httpOnly ? '✓' : ''}</td>
<td className="py-2 px-4">
<div className="flex items-center justify-end gap-2">
<button
onClick={(e) => {
e.stopPropagation();
handleEditCookie(domainWithCookies.domain, cookie);
}}
className="text-gray-700 hover:text-gray-950
dark:text-white dark:hover:text-gray-300"
>
<IconEdit strokeWidth={1.5} size={16} />
</button>
<button
onClick={(e) => {
e.stopPropagation();
handleDeleteCookie(domainWithCookies.domain, cookie.path, cookie.key);
}}
className="text-gray-950 dark:text-white dark:hover:hover:text-red-600 hover:text-red-600"
>
<IconTrash strokeWidth={1.5} size={16} />
</button>
</div>
</td>
</tr>
))}
</tbody>
</table>
</div>
</Accordion.Content>
</Accordion.Item>
))}
</Accordion>
</div>
)}
</StyledWrapper>
</Modal>
{isDeleteModalOpen && (
<Modal onClose={closeDeleteModal} handleCancel={closeDeleteModal} title={deleteModalTitle} hideFooter={true}>
<div className="flex items-center font-normal">
<IconAlertTriangle size={32} strokeWidth={1.5} className="text-yellow-600" />
<h1 className="ml-2 text-lg font-semibold">Hold on..</h1>
</div>
<div className="font-normal mt-4">{deleteModalContent}</div>
<div className="flex justify-between mt-6">
<div>
<button className="btn btn-sm btn-close" onClick={closeDeleteModal}>
Close
</button>
</div>
<div>
<button className="btn btn-sm btn-danger" onClick={onDeleteAction}>
Delete
</button>
</div>
</div>
</Modal>
)}
{isModifyCookieModalOpen && (
<ModifyCookieModal
onClose={() => {
setCookieToEdit(null);
setCurrentDomain('');
setCurrentDomain(null);
setIsModifyCookieModalOpen(false);
}}
domain={currentDomain}
cookie={cookieToEdit}
/>
)}
{domainToClear ? (
<ClearDomainCookiesModal
onClose={() => setDomainToClear(null)}
domain={domainToClear}
onClear={clearDomainCookiesAction}
/>
) : null}
{cookieToDelete ? (
<DeleteCookieModal
onClose={() => setCookieToDelete(null)}
cookieName={cookieToDelete.key}
onDelete={deleteCookieAction}
/>
) : null}
</>
);
};

View File

@@ -138,7 +138,7 @@ export const addCookie = (domain, cookie) => (dispatch, getState) => {
});
};
export const modifyCookie = (domain, oldCookie, path, key, cookie) => (dispatch, getState) => {
export const modifyCookie = (domain, oldCookie, cookie) => (dispatch, getState) => {
return new Promise((resolve, reject) => {
const { ipcRenderer } = window;