1
0
Fork 0
mirror of https://github.com/portainer/portainer.git synced 2025-07-25 00:09:40 +02:00

refactor(ui/modals): replace bootbox with react solution [EE-4541] (#8010)

This commit is contained in:
Chaim Lev-Ari 2023-02-14 13:49:41 +05:30 committed by GitHub
parent 392c7f74b8
commit e66dea44e3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
111 changed files with 1330 additions and 1562 deletions

View file

@ -0,0 +1,62 @@
import { useState } from 'react';
import { Modal, OnSubmit, ModalType, openModal } from '@@/modals';
import { Button } from '@@/buttons';
import { SwitchField } from '@@/form-components/SwitchField';
import { TextTip } from '@@/Tip/TextTip';
interface Props {
onSubmit: OnSubmit<{ pullLatest: boolean }>;
cannotPullImage: boolean;
}
function ConfirmRecreationModal({ onSubmit, cannotPullImage }: Props) {
const [pullLatest, setPullLatest] = useState(false);
return (
<Modal
onDismiss={() => onSubmit()}
aria-label="confirm recreate container modal"
>
<Modal.Header title="Are you sure?" modalType={ModalType.Destructive} />
<Modal.Body>
<p>
You&apos;re about to recreate this container and any non-persisted
data will be lost. This container will be removed and another one will
be created using the same configuration.
</p>
<SwitchField
name="pullLatest"
label="Re-pull image"
checked={pullLatest}
onChange={setPullLatest}
disabled={cannotPullImage}
/>
{cannotPullImage && (
<div className="mt-1 text-sm">
<TextTip color="orange">
Cannot re-pull as the image is inaccessible - either it no longer
exists or the tag or name is no longer correct.
</TextTip>
</div>
)}
</Modal.Body>
<Modal.Footer>
<Button onClick={() => onSubmit()} color="default">
Cancel
</Button>
<Button onClick={() => onSubmit({ pullLatest })} color="danger">
Recreate
</Button>
</Modal.Footer>
</Modal>
);
}
export async function confirmContainerRecreation(cannotPullImage: boolean) {
return openModal(ConfirmRecreationModal, {
cannotPullImage,
});
}

View file

@ -11,7 +11,7 @@ import {
import * as notifications from '@/portainer/services/notifications';
import { useAuthorizations, Authorized } from '@/react/hooks/useUser';
import { confirmContainerDeletion } from '@/portainer/services/modal.service/prompt';
import { confirmContainerDeletion } from '@/react/docker/containers/common/confirm-container-delete-modal';
import { setPortainerAgentTargetHeader } from '@/portainer/services/http-request.helper';
import {
ContainerId,
@ -242,7 +242,7 @@ export function ContainersDatatableActions({
);
}
function onRemoveClick(selectedItems: DockerContainer[]) {
async function onRemoveClick(selectedItems: DockerContainer[]) {
const isOneContainerRunning = selectedItems.some(
(container) => container.State === 'running'
);
@ -250,14 +250,13 @@ export function ContainersDatatableActions({
const runningTitle = isOneContainerRunning ? 'running' : '';
const title = `You are about to remove one or more ${runningTitle} containers.`;
confirmContainerDeletion(title, (result: string[]) => {
if (!result) {
return;
}
const cleanVolumes = !!result[0];
const result = await confirmContainerDeletion(title);
if (!result) {
return;
}
const { removeVolumes } = result;
removeSelectedContainers(selectedItems, cleanVolumes);
});
removeSelectedContainers(selectedItems, removeVolumes);
}
async function executeActionOnContainerList(

View file

@ -0,0 +1,16 @@
import { ModalType } from '@@/modals';
import { openSwitchPrompt } from '@@/modals/SwitchPrompt';
import { buildConfirmButton } from '@@/modals/utils';
export async function confirmContainerDeletion(title: string) {
const result = await openSwitchPrompt(
title,
'Automatically remove non-persistent volumes',
{
confirmButton: buildConfirmButton('Remove', 'danger'),
modalType: ModalType.Destructive,
}
);
return result ? { removeVolumes: result.value } : undefined;
}

View file

@ -0,0 +1,58 @@
import { useState } from 'react';
import { Registry } from '@/react/portainer/environments/environment.service/registries';
import { Modal, OnSubmit, openModal } from '@@/modals';
import { Button } from '@@/buttons';
import { PortainerSelect } from '@@/form-components/PortainerSelect';
interface Props {
registries: Registry[];
onSubmit: OnSubmit<Registry['Id']>;
defaultValue: Registry['Id'];
}
function RegistrySelectPrompt({ onSubmit, defaultValue, registries }: Props) {
const title = 'Which registry do you want to use?';
const [registryId, setRegistryId] = useState(defaultValue);
const options = registries2Options(registries);
return (
<Modal onDismiss={() => onSubmit()} aria-label={title}>
<Modal.Header title={title} />
<Modal.Body>
<PortainerSelect
onChange={setRegistryId}
value={registryId}
options={options}
/>
</Modal.Body>
<Modal.Footer>
<Button onClick={() => onSubmit()} color="default">
Cancel
</Button>
<Button onClick={() => onSubmit(registryId)} color="primary">
Update
</Button>
</Modal.Footer>
</Modal>
);
}
export function selectRegistry(
registries: Registry[],
defaultValue: Registry['Id']
) {
return openModal(RegistrySelectPrompt, {
registries,
defaultValue,
});
}
function registries2Options(registries: Registry[]) {
return registries.map((r) => ({
label: r.Name,
value: r.Id,
}));
}

View file

@ -0,0 +1,15 @@
import { ModalType } from '@@/modals';
import { ConfirmCallback, openConfirm } from '@@/modals/confirm';
import { buildConfirmButton } from '@@/modals/utils';
export async function confirmImageExport(callback: ConfirmCallback) {
const result = await openConfirm({
modalType: ModalType.Warn,
title: 'Caution',
message:
'The export may take several minutes, do not navigate away whilst the export is in progress.',
confirmButton: buildConfirmButton('Continue'),
});
callback(result);
}

View file

@ -4,13 +4,13 @@ import { useQueryClient } from 'react-query';
import _ from 'lodash';
import { useEnvironmentId } from '@/react/hooks/useEnvironmentId';
import { confirmDeletionAsync } from '@/portainer/services/modal.service/confirm';
import { AccessControlPanel } from '@/react/portainer/access-control/AccessControlPanel/AccessControlPanel';
import { ResourceControlType } from '@/react/portainer/access-control/types';
import { DockerContainer } from '@/react/docker/containers/types';
import { ResourceControlViewModel } from '@/react/portainer/access-control/models/ResourceControlViewModel';
import { useContainers } from '@/react/docker/containers/queries/containers';
import { confirmDelete } from '@@/modals/confirm';
import { PageHeader } from '@@/PageHeader';
import { useNetwork, useDeleteNetwork } from '../queries';
@ -103,7 +103,7 @@ export function ItemView() {
async function onRemoveNetworkClicked() {
const message = 'Do you want to delete the network?';
const confirmed = await confirmDeletionAsync(message);
const confirmed = await confirmDelete(message);
if (confirmed) {
deleteNetworkMutation.mutate(

View file

@ -0,0 +1,13 @@
import { openSwitchPrompt } from '@@/modals/SwitchPrompt';
import { ModalType } from '@@/modals';
import { buildConfirmButton } from '@@/modals/utils';
export async function confirmServiceForceUpdate(message: string) {
const result = await openSwitchPrompt('Are you sure?', 'Re-pull image', {
message,
confirmButton: buildConfirmButton('Update'),
modalType: ModalType.Warn,
});
return result ? { pullLatest: result.value } : undefined;
}

View file

@ -0,0 +1,21 @@
import { openSwitchPrompt } from '@@/modals/SwitchPrompt';
import { ModalType } from '@@/modals';
import { buildConfirmButton } from '@@/modals/utils';
export async function confirmStackUpdate(
message: string,
defaultValue: boolean
) {
const result = await openSwitchPrompt(
'Are you sure?',
'Re-pull image and redeploy',
{
message,
confirmButton: buildConfirmButton('Update'),
modalType: ModalType.Warn,
defaultValue,
}
);
return result ? { pullImage: result.value } : undefined;
}