Skip to content

Commit

Permalink
Merge pull request #266 from tnc-ca-geo/permanent-deletion-check
Browse files Browse the repository at this point in the history
Permanent deletion check
  • Loading branch information
nathanielrindlaub authored Jan 7, 2025
2 parents 6b5da02 + 17e2981 commit a594082
Show file tree
Hide file tree
Showing 10 changed files with 253 additions and 159 deletions.
7 changes: 6 additions & 1 deletion src/components/Callout.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,12 @@ const StyledCallout = styled('div', {
p: {
marginTop: '$3',
color: '$textMedium',
fontSize: '$3',
fontSize: '$4',
},
ul: {
marginTop: '$3',
color: '$textMedium',
fontSize: '$4',
},
variants: {
type: {
Expand Down
1 change: 0 additions & 1 deletion src/components/HydratedModal.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,6 @@ const HydratedModal = () => {
fullHeight: true,
content: <CameraAdminModal />,
callBackOnClose: () => {
console.log('callBackOnClose() - reverting moment global timezone to local timezone');
moment.tz.setDefault();
dispatch(clearDeployments());
},
Expand Down
30 changes: 30 additions & 0 deletions src/components/PermanentActionConfirmation.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import React, { useEffect, useState } from 'react';
import { FieldRow, StandAloneInput } from './Form.jsx';

const PermanentActionConfirmation = ({ text, setConfirmed }) => {
const [value, setValue] = useState('');

useEffect(() => {
setConfirmed(value === text);
}, [value, text, setConfirmed]);

return (
<div>
<p>
To confirm, type <strong style={{ fontStyle: 'italic' }}>{text}</strong> in the text input
field:
</p>
<FieldRow>
<StandAloneInput
type="text"
value={value}
onChange={(e) => {
setValue(e.target.value);
}}
/>
</FieldRow>
</div>
);
};

export default PermanentActionConfirmation;
135 changes: 71 additions & 64 deletions src/features/cameras/DeleteCameraAlert.jsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import React, { useEffect } from 'react';
import React, { useState, useEffect } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { red } from '@radix-ui/colors';
import { styled } from '../../theme/stitches.config.js';
import Button from '../../components/Button.jsx';
import { ButtonRow } from '../../components/Form.jsx';
import {
clearDeleteCameraTask,
deleteCamera,
Expand All @@ -29,10 +27,8 @@ import {
AlertTitle,
} from '../../components/AlertDialog.jsx';
import { DeleteImagesProgressBar } from '../images/DeleteImagesProgressBar.jsx';

const BoldText = styled('span', {
fontWeight: '$5',
});
import PermanentActionConfirmation from '../../components/PermanentActionConfirmation.jsx';
import Callout from '../../components/Callout.jsx';

const DeleteCameraAlert = () => {
const deleteCameraLoading = useSelector(selectDeleteCameraLoading);
Expand All @@ -48,6 +44,8 @@ const DeleteCameraAlert = () => {
}
}, [imageCount, selectedCamera, dispatch]);

const [confirmedDelete, setConfirmedDelete] = useState(false);

const handleDeleteCameraSubmit = () => {
dispatch(deleteCamera({ cameraId: selectedCamera }));
};
Expand Down Expand Up @@ -80,63 +78,72 @@ const DeleteCameraAlert = () => {
)}
<AlertTitle>Delete Camera</AlertTitle>
{/*TODO: Add a link to the documentation for more information on how to delete images.*/}
{imageCount > ASYNC_IMAGE_DELETE_BY_FILTER_LIMIT ? (
<>
Due to the large number of images associated with this camera, we are unable to delete
Camera <BoldText>{selectedCamera}</BoldText> at this time. Please ensure that the
number of images associated with this camera do not exceed{' '}
{ASYNC_IMAGE_DELETE_BY_FILTER_LIMIT} before trying again. We apologize for the
inconvenience.
</>
) : (
<>
<p>
Are you sure you&apos;d like to delete Camera <BoldText>{selectedCamera}</BoldText>?{' '}
{imageCount === 0 && 'This will remove the Camera and the Deployments '}
{!imageCountLoading && imageCount > 0 && (
<>
This will remove the Camera, its Deployments, and{' '}
{imageCount > 1 ? 'all' : 'the'}{' '}
<BoldText>
{imageCount} image{imageCount > 1 && 's'}
</BoldText>{' '}
</>
)}
associated with it from the Project.
</p>
<p>
<BoldText>This action cannot be undone.</BoldText>
</p>
<ButtonRow>
<Button
type="button"
size="large"
disabled={deleteCameraLoading.isLoading}
onClick={handleCancelDelete}
>
Cancel
</Button>
<Button
type="submit"
size="large"
disabled={
deleteCameraLoading.isLoading ||
imageCount > ASYNC_IMAGE_DELETE_BY_FILTER_LIMIT ||
imageCountLoading
}
onClick={handleDeleteCameraSubmit}
css={{
backgroundColor: red.red4,
color: red.red11,
border: 'none',
'&:hover': { color: red.red11, backgroundColor: red.red5 },
}}
>
Delete Camera
</Button>
</ButtonRow>
</>
)}
<div>
<Callout type="warning">
{imageCount > ASYNC_IMAGE_DELETE_BY_FILTER_LIMIT ? (
<p>
Due to the large number of images associated with this camera, we are unable to
delete Camera <strong>{selectedCamera}</strong> at this time. Please ensure that
the number of images associated with this camera do not exceed{' '}
{ASYNC_IMAGE_DELETE_BY_FILTER_LIMIT} before trying again. We apologize for the
inconvenience.
</p>
) : (
<div>
<p>
Are you sure you&apos;d like to delete Camera <strong>{selectedCamera}</strong>?{' '}
{imageCount === 0 && 'This will remove the Camera and the Deployments '}
{!imageCountLoading && imageCount > 0 && (
<>
This will remove the Camera, its Deployments, and{' '}
{imageCount > 1 ? 'all' : 'the'}{' '}
<strong>
{imageCount} image{imageCount > 1 && 's'}
</strong>{' '}
</>
)}
associated with it from the Project.
</p>
<p>
<strong>This action cannot be undone.</strong>
</p>
</div>
)}
</Callout>
<PermanentActionConfirmation
text="permanently delete"
setConfirmed={setConfirmedDelete}
/>
</div>
<div style={{ display: 'flex', gap: 25, justifyContent: 'flex-end' }}>
<Button
type="button"
size="small"
disabled={deleteCameraLoading.isLoading}
onClick={handleCancelDelete}
>
Cancel
</Button>
<Button
type="submit"
size="small"
disabled={
deleteCameraLoading.isLoading ||
imageCount > ASYNC_IMAGE_DELETE_BY_FILTER_LIMIT ||
imageCountLoading ||
!confirmedDelete
}
onClick={handleDeleteCameraSubmit}
css={{
backgroundColor: red.red4,
color: red.red11,
border: 'none',
'&:hover': { color: red.red11, backgroundColor: red.red5 },
}}
>
Yes, delete
</Button>
</div>
</AlertContent>
</AlertPortal>
</Alert>
Expand Down
2 changes: 1 addition & 1 deletion src/features/cameras/UpdateCameraSerialNumberForm.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ const UpdateCameraSerialNumberForm = () => {
A camera with the serial number you entered already exists. By updating camera{' '}
<strong>{selectedCamera}</strong> with this serial number, you will be{' '}
<strong>merging</strong> images from the selected camera to the target camera,
which can not be undone.
which cannot be undone.
</p>
{affectedViews.length > 0 && (
<p>
Expand Down
32 changes: 24 additions & 8 deletions src/features/images/DeleteImagesAlert.jsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useEffect } from 'react';
import React, { useState, useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { red } from '@radix-ui/colors';
import {
Expand All @@ -24,6 +24,7 @@ import {
AlertTitle,
} from '../../components/AlertDialog.jsx';
import Button from '../../components/Button.jsx';
import Callout from '../../components/Callout.jsx';
import {
clearDeleteImagesTask,
deleteImagesTask,
Expand All @@ -32,6 +33,7 @@ import {
} from '../tasks/tasksSlice.js';
import { SimpleSpinner, SpinnerOverlay } from '../../components/Spinner.jsx';
import { DeleteImagesProgressBar } from './DeleteImagesProgressBar.jsx';
import PermanentActionConfirmation from '../../components/PermanentActionConfirmation.jsx';

const DeleteImagesAlert = () => {
const dispatch = useDispatch();
Expand All @@ -52,7 +54,7 @@ const DeleteImagesAlert = () => {
}
}, [deleteImagesTaskLoading, dispatch]);

const handleConfirmDelete = () => {
const handleDeleteClick = () => {
if (alertState.deleteImagesAlertByFilter) {
// if deleting by filter, always delete using task handler
dispatch(deleteImagesTask({ imageIds: [], filters: filters }));
Expand All @@ -71,6 +73,8 @@ const DeleteImagesAlert = () => {
dispatch(clearDeleteImagesTask());
};

const [confirmedDelete, setConfirmedDelete] = useState(false);

const deleteByIdLimitExceeded =
!alertState.deleteImagesAlertByFilter && selectedImages.length > ASYNC_IMAGE_DELETE_BY_ID_LIMIT;
const byFilterLimitExceeded =
Expand All @@ -84,22 +88,28 @@ const DeleteImagesAlert = () => {
const filterTitle = `Are you sure you'd like to delete ${
imageCount === 1 ? 'this image' : `these ${imageCount && imageCount.toLocaleString()} images`
}?`;

const selectionTitle = `Are you sure you'd like to delete ${
selectedImages.length === 1
? 'this image'
: `these ${selectedImages && selectedImages.length.toLocaleString()} images`
}?`;

const filterText = (
<div>
<p>
This will delete all images that match the currently applied filters. This action can not be
undone.
This will delete all images that match the currently applied filters.{' '}
<strong>This action can not be undone.</strong>
</p>
</div>
);

const selectionText = (
<div>
<p>This will delete all currently selected images. This action can not be undone.</p>
<p>
This will delete all currently selected images.{' '}
<strong>This action cannot be undone.</strong>
</p>
</div>
);

Expand Down Expand Up @@ -160,21 +170,27 @@ const DeleteImagesAlert = () => {
</SpinnerOverlay>
)}
<AlertTitle>{title}</AlertTitle>
{text}
<div>
<Callout type="warning">{text}</Callout>
<PermanentActionConfirmation
text="permanently delete"
setConfirmed={setConfirmedDelete}
/>
</div>
<div style={{ display: 'flex', gap: 25, justifyContent: 'flex-end' }}>
<Button size="small" css={{ border: 'none' }} onClick={handleCancelDelete}>
Cancel
</Button>
<Button
size="small"
disabled={deleteByIdLimitExceeded || byFilterLimitExceeded}
disabled={deleteByIdLimitExceeded || byFilterLimitExceeded || !confirmedDelete}
css={{
backgroundColor: red.red4,
color: red.red11,
border: 'none',
'&:hover': { color: red.red11, backgroundColor: red.red5 },
}}
onClick={handleConfirmDelete}
onClick={handleDeleteClick}
>
Yes, delete
</Button>
Expand Down
30 changes: 12 additions & 18 deletions src/features/loupe/DeleteCommentAlert.jsx
Original file line number Diff line number Diff line change
@@ -1,28 +1,22 @@
import React from 'react';
import {
Alert,
AlertContent,
AlertOverlay,
AlertPortal,
AlertTitle
} from "../../components/AlertDialog";
import {
Alert,
AlertContent,
AlertOverlay,
AlertPortal,
AlertTitle,
} from '../../components/AlertDialog';
import Button from '../../components/Button.jsx';
import { red } from '@radix-ui/colors';

export const DeleteCommentAlert = ({
isOpen,
onDeleteConfirm,
onDeleteCancel
}) => {
export const DeleteCommentAlert = ({ isOpen, onDeleteConfirm, onDeleteCancel }) => {
return (
<Alert open={isOpen}>
<AlertPortal>
<AlertOverlay />
<AlertOverlay />
<AlertContent>
<AlertTitle>
Are you sure you&apos;d like to delete this comment?
</AlertTitle>
<p>This action can not be undone.</p>
<AlertTitle>Are you sure you&apos;d like to delete this comment?</AlertTitle>
<p>This action cannot be undone.</p>
<div style={{ display: 'flex', gap: 25, justifyContent: 'flex-end' }}>
<Button size="small" css={{ border: 'none' }} onClick={() => onDeleteCancel()}>
Cancel
Expand All @@ -44,4 +38,4 @@ export const DeleteCommentAlert = ({
</AlertPortal>
</Alert>
);
}
};
Loading

0 comments on commit a594082

Please sign in to comment.