Skip to content

Commit 7dd74ec

Browse files
committed
Modal cleanup, begin transitioning towards the new dialog
1 parent 3834aca commit 7dd74ec

File tree

10 files changed

+121
-96
lines changed

10 files changed

+121
-96
lines changed

resources/lang/en/activity.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
],
3737
],
3838
'server' => [
39+
'reinstall' => 'Reinstalled server',
3940
'backup' => [
4041
'download' => 'Downloaded the :name backup',
4142
'delete' => 'Deleted the :name backup',
@@ -88,7 +89,6 @@
8889
],
8990
'settings' => [
9091
'rename' => 'Renamed the server from :old to :new',
91-
'reinstall' => 'Triggered a server reinstall',
9292
],
9393
'startup' => [
9494
'edit' => 'Changed the :variable variable from ":old" to ":new"',

resources/scripts/components/dashboard/AccountApiContainer.tsx

Lines changed: 20 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -5,46 +5,42 @@ import getApiKeys, { ApiKey } from '@/api/account/getApiKeys';
55
import SpinnerOverlay from '@/components/elements/SpinnerOverlay';
66
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
77
import { faKey, faTrashAlt } from '@fortawesome/free-solid-svg-icons';
8-
import ConfirmationModal from '@/components/elements/ConfirmationModal';
98
import deleteApiKey from '@/api/account/deleteApiKey';
10-
import { Actions, useStoreActions } from 'easy-peasy';
11-
import { ApplicationStore } from '@/state';
129
import FlashMessageRender from '@/components/FlashMessageRender';
13-
import { httpErrorToHuman } from '@/api/http';
1410
import { format } from 'date-fns';
1511
import PageContentBlock from '@/components/elements/PageContentBlock';
1612
import tw from 'twin.macro';
1713
import GreyRowBox from '@/components/elements/GreyRowBox';
14+
import { Dialog } from '@/components/elements/dialog';
15+
import { useFlashKey } from '@/plugins/useFlash';
16+
import Code from '@/components/elements/Code';
1817

1918
export default () => {
2019
const [ deleteIdentifier, setDeleteIdentifier ] = useState('');
2120
const [ keys, setKeys ] = useState<ApiKey[]>([]);
2221
const [ loading, setLoading ] = useState(true);
23-
const { addError, clearFlashes } = useStoreActions((actions: Actions<ApplicationStore>) => actions.flashes);
22+
const { clearAndAddHttpError } = useFlashKey('account');
2423

2524
useEffect(() => {
26-
clearFlashes('account');
2725
getApiKeys()
2826
.then(keys => setKeys(keys))
2927
.then(() => setLoading(false))
30-
.catch(error => {
31-
console.error(error);
32-
addError({ key: 'account', message: httpErrorToHuman(error) });
33-
});
28+
.catch(error => clearAndAddHttpError(error));
3429
}, []);
3530

3631
const doDeletion = (identifier: string) => {
3732
setLoading(true);
38-
clearFlashes('account');
33+
34+
clearAndAddHttpError();
3935
deleteApiKey(identifier)
4036
.then(() => setKeys(s => ([
4137
...(s || []).filter(key => key.identifier !== identifier),
4238
])))
43-
.catch(error => {
44-
console.error(error);
45-
addError({ key: 'account', message: httpErrorToHuman(error) });
46-
})
47-
.then(() => setLoading(false));
39+
.catch(error => clearAndAddHttpError(error))
40+
.then(() => {
41+
setLoading(false);
42+
setDeleteIdentifier('');
43+
});
4844
};
4945

5046
return (
@@ -56,19 +52,15 @@ export default () => {
5652
</ContentBox>
5753
<ContentBox title={'API Keys'} css={tw`flex-1 overflow-hidden mt-8 md:mt-0 md:ml-8`}>
5854
<SpinnerOverlay visible={loading}/>
59-
<ConfirmationModal
60-
visible={!!deleteIdentifier}
61-
title={'Confirm key deletion'}
62-
buttonText={'Yes, delete key'}
63-
onConfirmed={() => {
64-
doDeletion(deleteIdentifier);
65-
setDeleteIdentifier('');
66-
}}
67-
onModalDismissed={() => setDeleteIdentifier('')}
55+
<Dialog.Confirm
56+
title={'Delete API Key'}
57+
confirm={'Delete Key'}
58+
open={!!deleteIdentifier}
59+
onClose={() => setDeleteIdentifier('')}
60+
onConfirmed={() => doDeletion(deleteIdentifier)}
6861
>
69-
Are you sure you wish to delete this API key? All requests using it will immediately be
70-
invalidated and will fail.
71-
</ConfirmationModal>
62+
All requests using the <Code>{deleteIdentifier}</Code> key will be invalidated.
63+
</Dialog.Confirm>
7264
{
7365
keys.length === 0 ?
7466
<p css={tw`text-center text-sm`}>

resources/scripts/components/dashboard/ssh/AccountSSHContainer.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ export default () => {
5555
{format(key.createdAt, 'MMM do, yyyy HH:mm')}
5656
</p>
5757
</div>
58-
<DeleteSSHKeyButton fingerprint={key.fingerprint} />
58+
<DeleteSSHKeyButton name={key.name} fingerprint={key.fingerprint} />
5959
</GreyRowBox>
6060
))
6161
}

resources/scripts/components/dashboard/ssh/DeleteSSHKeyButton.tsx

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,11 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
33
import { faTrashAlt } from '@fortawesome/free-solid-svg-icons';
44
import React, { useState } from 'react';
55
import { useFlashKey } from '@/plugins/useFlash';
6-
import ConfirmationModal from '@/components/elements/ConfirmationModal';
76
import { deleteSSHKey, useSSHKeys } from '@/api/account/ssh-keys';
7+
import { Dialog } from '@/components/elements/dialog';
8+
import Code from '@/components/elements/Code';
89

9-
export default ({ fingerprint }: { fingerprint: string }) => {
10+
export default ({ name, fingerprint }: { name: string; fingerprint: string }) => {
1011
const { clearAndAddHttpError } = useFlashKey('account');
1112
const [ visible, setVisible ] = useState(false);
1213
const { mutate } = useSSHKeys();
@@ -19,22 +20,22 @@ export default ({ fingerprint }: { fingerprint: string }) => {
1920
deleteSSHKey(fingerprint),
2021
])
2122
.catch((error) => {
22-
mutate(undefined, true);
23+
mutate(undefined, true).catch(console.error);
2324
clearAndAddHttpError(error);
2425
});
2526
};
2627

2728
return (
2829
<>
29-
<ConfirmationModal
30-
visible={visible}
31-
title={'Confirm Key Deletion'}
32-
buttonText={'Yes, Delete SSH Key'}
30+
<Dialog.Confirm
31+
open={visible}
32+
title={'Delete SSH Key'}
33+
confirm={'Delete Key'}
3334
onConfirmed={onClick}
34-
onModalDismissed={() => setVisible(false)}
35+
onClose={() => setVisible(false)}
3536
>
36-
Are you sure you wish to delete this SSH key?
37-
</ConfirmationModal>
37+
Removing the <Code>{name}</Code> SSH key will invalidate its usage across the Panel.
38+
</Dialog.Confirm>
3839
<button css={tw`ml-4 p-2 text-sm`} onClick={() => setVisible(true)}>
3940
<FontAwesomeIcon
4041
icon={faTrashAlt}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import React from 'react';
2+
import classNames from 'classnames';
3+
4+
interface CodeProps {
5+
dark?: boolean | undefined;
6+
children: React.ReactChild | React.ReactFragment | React.ReactPortal;
7+
}
8+
9+
export default ({ dark, children }: CodeProps) => (
10+
<code
11+
className={classNames('font-mono text-sm px-2 py-1 rounded', {
12+
'bg-neutral-700': !dark,
13+
'bg-neutral-900 text-gray-100': dark,
14+
})}
15+
>
16+
{children}
17+
</code>
18+
);

resources/scripts/components/elements/activity/ActivityLogEntry.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ export default ({ activity, children }: Props) => {
8787
<span className={'text-gray-400'}>&nbsp;|&nbsp;</span>
8888
<Tooltip
8989
placement={'right'}
90-
content={format(activity.timestamp, 'MMM do, yyyy h:mma')}
90+
content={format(activity.timestamp, 'MMM do, yyyy H:mm:ss')}
9191
>
9292
<span>
9393
{formatDistanceToNowStrict(activity.timestamp, { addSuffix: true })}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import React from 'react';
2+
import { Dialog } from '@/components/elements/dialog/index';
3+
import { DialogProps } from '@/components/elements/dialog/Dialog';
4+
import { Button } from '@/components/elements/button/index';
5+
6+
type ConfirmationProps = Omit<DialogProps, 'description' | 'children'> & {
7+
children: React.ReactNode;
8+
confirm?: string | undefined;
9+
onConfirmed: () => void;
10+
}
11+
12+
export default ({ confirm = 'Okay', children, onConfirmed, ...props }: ConfirmationProps) => {
13+
return (
14+
<Dialog {...props} description={typeof children === 'string' ? children : undefined}>
15+
{typeof children !== 'string' && children}
16+
<Dialog.Buttons>
17+
<Button.Text onClick={props.onClose}>Cancel</Button.Text>
18+
<Button.Danger onClick={onConfirmed}>{confirm}</Button.Danger>
19+
</Dialog.Buttons>
20+
</Dialog>
21+
);
22+
};

resources/scripts/components/elements/dialog/Dialog.tsx

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,22 @@ import { XIcon } from '@heroicons/react/solid';
55
import DialogIcon from '@/components/elements/dialog/DialogIcon';
66
import { AnimatePresence, motion } from 'framer-motion';
77
import classNames from 'classnames';
8+
import ConfirmationDialog from '@/components/elements/dialog/ConfirmationDialog';
89

9-
interface Props {
10+
export interface DialogProps {
1011
open: boolean;
1112
onClose: () => void;
1213
hideCloseIcon?: boolean;
1314
title?: string;
14-
description?: string;
15+
description?: string | undefined;
1516
children?: React.ReactNode;
1617
}
1718

1819
const DialogButtons = ({ children }: { children: React.ReactNode }) => (
1920
<>{children}</>
2021
);
2122

22-
const Dialog = ({ open, title, description, onClose, hideCloseIcon, children }: Props) => {
23+
const Dialog = ({ open, title, description, onClose, hideCloseIcon, children }: DialogProps) => {
2324
const items = React.Children.toArray(children || []);
2425
const [ buttons, icon, content ] = [
2526
// @ts-expect-error
@@ -59,9 +60,9 @@ const Dialog = ({ open, title, description, onClose, hideCloseIcon, children }:
5960
>
6061
<div className={'flex p-6'}>
6162
{icon && <div className={'mr-4'}>{icon}</div>}
62-
<div className={'flex-1 max-h-[70vh] overflow-y-scroll overflow-x-hidden'}>
63+
<div className={'flex-1 max-h-[70vh]'}>
6364
{title &&
64-
<HDialog.Title className={'font-header text-xl font-medium mb-2 text-white pr-4'}>
65+
<HDialog.Title className={'font-header text-xl font-medium mb-2 text-gray-50 pr-4'}>
6566
{title}
6667
</HDialog.Title>
6768
}
@@ -91,6 +92,10 @@ const Dialog = ({ open, title, description, onClose, hideCloseIcon, children }:
9192
);
9293
};
9394

94-
const _Dialog = Object.assign(Dialog, { Buttons: DialogButtons, Icon: DialogIcon });
95+
const _Dialog = Object.assign(Dialog, {
96+
Confirm: ConfirmationDialog,
97+
Buttons: DialogButtons,
98+
Icon: DialogIcon,
99+
});
95100

96101
export default _Dialog;

resources/scripts/components/server/backups/BackupContextMenu.tsx

Lines changed: 29 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ import getBackupDownloadUrl from '@/api/server/backups/getBackupDownloadUrl';
1313
import useFlash from '@/plugins/useFlash';
1414
import SpinnerOverlay from '@/components/elements/SpinnerOverlay';
1515
import deleteBackup from '@/api/server/backups/deleteBackup';
16-
import ConfirmationModal from '@/components/elements/ConfirmationModal';
1716
import Can from '@/components/elements/Can';
1817
import tw from 'twin.macro';
1918
import getServerBackups from '@/api/swr/getServerBackups';
@@ -22,6 +21,7 @@ import { ServerContext } from '@/state/server';
2221
import Input from '@/components/elements/Input';
2322
import { restoreServerBackup } from '@/api/server/backups';
2423
import http, { httpErrorToHuman } from '@/api/http';
24+
import { Dialog } from '@/components/elements/dialog';
2525

2626
interface Props {
2727
backup: ServerBackup;
@@ -103,35 +103,29 @@ export default ({ backup }: Props) => {
103103

104104
return (
105105
<>
106-
<ConfirmationModal
107-
visible={modal === 'unlock'}
108-
title={'Unlock this backup?'}
106+
<Dialog.Confirm
107+
open={modal === 'unlock'}
108+
onClose={() => setModal('')}
109+
title={`Unlock "${backup.name}"`}
109110
onConfirmed={onLockToggle}
110-
onModalDismissed={() => setModal('')}
111-
buttonText={'Yes, unlock'}
112111
>
113-
Are you sure you want to unlock this backup? It will no longer be protected from automated or
114-
accidental deletions.
115-
</ConfirmationModal>
116-
<ConfirmationModal
117-
visible={modal === 'restore'}
118-
title={'Restore this backup?'}
119-
buttonText={'Restore backup'}
112+
This backup will no longer be protected from automated or accidental deletions.
113+
</Dialog.Confirm>
114+
<Dialog.Confirm
115+
open={modal === 'restore'}
116+
onClose={() => setModal('')}
117+
confirm={'Restore'}
118+
title={`Restore "${backup.name}"`}
120119
onConfirmed={() => doRestorationAction()}
121-
onModalDismissed={() => setModal('')}
122120
>
123-
<p css={tw`text-neutral-300`}>
124-
This server will be stopped in order to restore the backup. Once the backup has started you will
125-
not be able to control the server power state, access the file manager, or create additional backups
126-
until it has completed.
121+
<p>
122+
Your server will be stopped. You will not be able to control the power state, access the file
123+
manager, or create additional backups until completed.
127124
</p>
128-
<p css={tw`text-neutral-300 mt-4`}>
129-
Are you sure you want to continue?
130-
</p>
131-
<p css={tw`mt-4 -mb-2 bg-neutral-900 p-3 rounded`}>
125+
<p css={tw`mt-4 -mb-2 bg-gray-700 p-3 rounded`}>
132126
<label
133127
htmlFor={'restore_truncate'}
134-
css={tw`text-base text-neutral-200 flex items-center cursor-pointer`}
128+
css={tw`text-base flex items-center cursor-pointer`}
135129
>
136130
<Input
137131
type={'checkbox'}
@@ -141,27 +135,26 @@ export default ({ backup }: Props) => {
141135
checked={truncate}
142136
onChange={() => setTruncate(s => !s)}
143137
/>
144-
Remove all files and folders before restoring this backup.
138+
Delete all files before restoring backup.
145139
</label>
146140
</p>
147-
</ConfirmationModal>
148-
<ConfirmationModal
149-
visible={modal === 'delete'}
150-
title={'Delete this backup?'}
151-
buttonText={'Yes, delete backup'}
152-
onConfirmed={() => doDeletion()}
153-
onModalDismissed={() => setModal('')}
141+
</Dialog.Confirm>
142+
<Dialog.Confirm
143+
title={`Delete "${backup.name}"`}
144+
confirm={'Continue'}
145+
open={modal === 'delete'}
146+
onClose={() => setModal('')}
147+
onConfirmed={doDeletion}
154148
>
155-
Are you sure you wish to delete this backup? This is a permanent operation and the backup cannot
156-
be recovered once deleted.
157-
</ConfirmationModal>
149+
This is a permanent operation. The backup cannot be recovered once deleted.
150+
</Dialog.Confirm>
158151
<SpinnerOverlay visible={loading} fixed/>
159152
{backup.isSuccessful ?
160153
<DropdownMenu
161154
renderToggle={onClick => (
162155
<button
163156
onClick={onClick}
164-
css={tw`text-neutral-200 transition-colors duration-150 hover:text-neutral-100 p-2`}
157+
css={tw`text-gray-200 transition-colors duration-150 hover:text-gray-100 p-2`}
165158
>
166159
<FontAwesomeIcon icon={faEllipsisH}/>
167160
</button>
@@ -203,7 +196,7 @@ export default ({ backup }: Props) => {
203196
:
204197
<button
205198
onClick={() => setModal('delete')}
206-
css={tw`text-neutral-200 transition-colors duration-150 hover:text-neutral-100 p-2`}
199+
css={tw`text-gray-200 transition-colors duration-150 hover:text-gray-100 p-2`}
207200
>
208201
<FontAwesomeIcon icon={faTrashAlt}/>
209202
</button>

0 commit comments

Comments
 (0)