Skip to content

Commit b8e2b5d

Browse files
committed
feat: Deletion and redeployment
1 parent 05052d7 commit b8e2b5d

File tree

6 files changed

+118
-126
lines changed

6 files changed

+118
-126
lines changed

src/components/ui/buttonVariants.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ export const buttonVariants = cva(
3838
positive: 'bg-green text-white shadow-xs hover:bg-green/90',
3939
warning: 'bg-yellow text-white shadow-xs hover:bg-yellow/90',
4040
outline: `${outlineCommon} ${hoverBounce}`,
41+
ghostOutline:
42+
`${outlineCommon} ${hoverBounce} border-none`,
4143
positiveOutline:
4244
`${outlineCommon} ${hoverBounce} border-green`,
4345
destructiveOutline:

src/features/instance/applications/components/ApplicationsSidebar/index.tsx

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,9 @@ export function ApplicationsSidebar() {
3838
}
3939
}, [openedEntry, focusedItem, items]);
4040

41-
// TODO: application command with shortcuts for a new file and folder
42-
// TODO: delete file
43-
// TODO: delete folder
44-
// TODO: redeploy package
45-
// TODO: restart cluster
41+
// TODO: application command with shortcuts for creation and deletion
42+
// TODO: consistently drive file and folder selection through context (particularly during creation and deletion)
43+
// TODO: Split all this logic up into smaller files, right?
4644
// TODO: onRenameItem f2 handling
4745
// TODO: on drag item from one folder to another
4846

src/features/instance/applications/components/TextEditorView/index.tsx

Lines changed: 24 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,17 @@ import { useInstanceClientIdParams } from '@/config/useInstanceClient';
55
import { isDirectory } from '@/features/instance/applications/context/isDirectory';
66
import { useEditorView } from '@/features/instance/applications/hooks/useEditorView';
77
import { AddDirectoryOrFileModal } from '@/features/instance/applications/modals/AddDirectoryOrFileModal';
8-
import { DeleteFolderFileModal } from '@/features/instance/applications/modals/DeleteFolderFileModal';
8+
import { DeleteDirectoryOrFileModal } from '@/features/instance/applications/modals/DeleteDirectoryOrFileModal';
99
import { RedeployApplicationModal } from '@/features/instance/applications/modals/RedeployApplicationModal';
1010
import { useDeployComponentMutation } from '@/features/instance/operations/mutations/deployComponent';
11-
import { useDropComponent } from '@/features/instance/operations/mutations/dropComponent';
1211
import { useEffectedState } from '@/hooks/useEffectedState';
1312
import { useToggler } from '@/hooks/useToggler';
1413
import { parseFileExtension } from '@/lib/string/parseFileExtension';
1514
import { Editor, EditorProps, OnMount } from '@monaco-editor/react';
1615
import { useQueryClient } from '@tanstack/react-query';
1716
import { useParams } from '@tanstack/react-router';
1817
import { FileIcon, FolderIcon, PackageIcon, PencilIcon, SaveIcon, TrashIcon, Undo2Icon } from 'lucide-react';
19-
import { useCallback, useEffect, useRef, useState } from 'react';
18+
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
2019
import './directory-read-me.css';
2120
import Markdown from 'react-markdown';
2221
import { toast } from 'sonner';
@@ -50,8 +49,6 @@ export function TextEditorView() {
5049

5150
const mountedRef = useRef<Parameters<OnMount> | null>(null);
5251

53-
// TODO: Split all this logic up into smaller files, right?
54-
5552
useEffect(() => {
5653
const extension = parseFileExtension(openedEntry?.path);
5754
const updatedLanguage = extensionToLanguageMap[extension] || 'plaintext';
@@ -117,45 +114,14 @@ export function TextEditorView() {
117114
toggleOff: hideAddingDirectory,
118115
} = useToggler(false);
119116
const { toggled: isAddingFile, toggleOn: onAddFileClicked, toggleOff: hideAddingFile } = useToggler(false);
120-
const { toggled: isDeleteDirectoryOrFileClicked, setToggled: setIsDeleteDirectoryOrFileClicked } = useToggler(false);
121-
const { toggled: isRedeployApplicationClicked, setToggled: setIsRedeployApplicationClicked } = useToggler(false);
122-
const { mutate: deleteFolderFile, isPending: isDeleteFolderFilePending } = useDropComponent();
117+
const { toggled: isDeleteDirectoryOrFileClicked, toggleOn: onDeleteClick, setToggled: setIsDeleteDirectoryOrFileClicked } = useToggler(false);
118+
const { toggled: isRedeployApplicationClicked, toggleOn: onRedeployClicked, setToggled: setIsRedeployApplicationClicked } = useToggler(false);
123119

124120
const onHideAddDirectoryModal = useCallback(() => {
125121
hideAddingDirectory();
126122
hideAddingFile();
127123
}, []);
128124

129-
const handleDeleteFolderOrFile = useCallback(async () => {
130-
if (!openedEntry) {
131-
return;
132-
}
133-
deleteFolderFile(
134-
{
135-
file: openedEntry.package
136-
? undefined
137-
: `${openedEntry.path.split('/').slice(1).join('/')}`,
138-
project: openedEntry.project,
139-
replicated: instanceParams.entityType === 'cluster',
140-
...instanceParams,
141-
},
142-
{
143-
onSuccess: () => {
144-
// TODO: Select parent.
145-
// setOpenedEntry({
146-
// filePath: '',
147-
// projectName: '',
148-
// entries: [],
149-
// content: '',
150-
// pkg: '',
151-
// });
152-
// refetchComponents();
153-
setIsDeleteDirectoryOrFileClicked(false);
154-
},
155-
},
156-
);
157-
}, [deleteFolderFile, instanceParams, openedEntry]);
158-
159125
const { mutate: reDeployApplication, isPending: isDeployComponentPending } = useDeployComponentMutation();
160126

161127
const redeployPackage = useCallback((applicationUrl: string) => {
@@ -189,16 +155,14 @@ export function TextEditorView() {
189155
},
190156
});
191157
}, [reDeployApplication, openedEntry, instanceParams, queryClient]);
192-
// TODO:
193-
// const restrictPackageModification = useMemo(() => {
194-
// return openedEntry.package?.includes('github.com/HarperDB/status-check-fabric')
195-
// || openedEntry.package?.includes('github.com/HarperFast/status-check-fabric');
196-
// }, [openedEntry.package]);
197158

198-
199-
const onDeleteClick = useCallback(() => {
200-
setIsDeleteDirectoryOrFileClicked(true);
201-
}, []);
159+
const restrictPackageModification = useMemo(() => {
160+
if (!openedEntry) {
161+
return false;
162+
}
163+
return openedEntry.package?.includes('github.com/HarperDB/status-check-fabric')
164+
|| openedEntry.package?.includes('github.com/HarperFast/status-check-fabric');
165+
}, [openedEntry?.package]);
202166

203167
if (!openedEntry) {
204168
return null;
@@ -232,7 +196,7 @@ export function TextEditorView() {
232196

233197
<div className="absolute top-0 right-0 left-0 backdrop-blur-sm bg-black-10 shadow-xl flex pr-12 -mr-1">
234198

235-
{!isDirectory(openedEntry) && <Button
199+
{!isDirectory(openedEntry) && !openedEntry.package && <Button
236200
variant="default"
237201
className="rounded-none"
238202
onClick={onSaveClick}
@@ -247,7 +211,7 @@ export function TextEditorView() {
247211
<span className="hidden lg:inline-block"><u>S</u>ave</span>
248212
</Button>}
249213

250-
<Button
214+
{!openedEntry.package && <Button
251215
variant="ghost"
252216
className="rounded-none"
253217
// onClick={onRenameClick}
@@ -260,32 +224,32 @@ export function TextEditorView() {
260224
>
261225
<PencilIcon />
262226
<span className="hidden lg:inline-block"><u>R</u>ename</span>
263-
</Button>
227+
</Button>}
264228

265-
<Button
229+
{!openedEntry.package && <Button
266230
variant="ghost"
267231
className="rounded-none"
268232
onClick={onAddFileClicked}
269233
accessKey="n"
270234
>
271235
<FileIcon />
272236
<span className="hidden lg:inline-block"><u>N</u>ew File</span>
273-
</Button>
237+
</Button>}
274238

275-
<Button
239+
{!openedEntry.package && <Button
276240
variant="ghost"
277241
className="rounded-none"
278242
onClick={onAddDirectoryClicked}
279243
accessKey="n"
280244
>
281245
<FolderIcon />
282246
<span className="hidden lg:inline-block"><u>A</u>dd Directory</span>
283-
</Button>
247+
</Button>}
284248

285-
{openedEntry.package && <Button
249+
{!!openedEntry.package && !restrictPackageModification && <Button
286250
variant="ghost"
287251
className="rounded-none"
288-
// onClick={onRedeplyClick}
252+
onClick={onRedeployClicked}
289253
accessKey="n"
290254
>
291255
<PackageIcon />
@@ -302,17 +266,17 @@ export function TextEditorView() {
302266

303267
<div className="grow"></div>
304268

305-
<Button
269+
{!restrictPackageModification && <Button
306270
variant="destructiveGhost"
307271
className="rounded-none"
308272
onClick={onDeleteClick}
309273
accessKey="n"
310274
>
311275
<TrashIcon />
312276
<span className="hidden xl:inline-block"><u>D</u>elete</span>
313-
</Button>
277+
</Button>}
314278

315-
{!isDirectory(openedEntry) && <Button
279+
{!isDirectory(openedEntry) && !openedEntry.package && <Button
316280
variant="ghost"
317281
className="rounded-none"
318282
onClick={onDiscardClick}
@@ -334,13 +298,9 @@ export function TextEditorView() {
334298
hideModal={onHideAddDirectoryModal}
335299
type={isAddingDirectory ? 'directory' : 'file'}
336300
/>
337-
<DeleteFolderFileModal
301+
<DeleteDirectoryOrFileModal
338302
isModalOpen={isDeleteDirectoryOrFileClicked}
339303
setIsModalOpen={setIsDeleteDirectoryOrFileClicked}
340-
isFolderSelected={isDirectory(openedEntry)}
341-
isPackageSelected={!!openedEntry.package}
342-
isPending={isDeleteFolderFilePending}
343-
handleDeleteFolderOrFile={handleDeleteFolderOrFile}
344304
/>
345305
<RedeployApplicationModal
346306
isModalOpen={isRedeployApplicationClicked}

src/features/instance/applications/modals/AddDirectoryOrFileModal.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,8 @@ export function AddDirectoryOrFileModal({
108108
<Input
109109
disabled={isPending}
110110
type="text"
111+
autoComplete="off"
112+
autoCapitalize="off"
111113
{...field}
112114
/>
113115
</FormControl>
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
import { Button } from '@/components/ui/button';
2+
import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from '@/components/ui/dialog';
3+
import { useInstanceClientIdParams } from '@/config/useInstanceClient';
4+
import { isDirectory } from '@/features/instance/applications/context/isDirectory';
5+
import { useEditorView } from '@/features/instance/applications/hooks/useEditorView';
6+
import { useDropComponent } from '@/features/instance/operations/mutations/dropComponent';
7+
import { Ban, Trash } from 'lucide-react';
8+
import { MouseEvent, useCallback } from 'react';
9+
10+
export function DeleteDirectoryOrFileModal({
11+
isModalOpen = false,
12+
setIsModalOpen,
13+
}: {
14+
readonly isModalOpen: boolean;
15+
readonly setIsModalOpen: (value: boolean) => void;
16+
}) {
17+
const instanceParams = useInstanceClientIdParams();
18+
const { openedEntry, reloadRootEntries, setOpenedEntry } = useEditorView();
19+
const isFolderSelected = isDirectory(openedEntry);
20+
const isPackageSelected = !!openedEntry?.package;
21+
const thing = isPackageSelected ? 'Package' : isFolderSelected ? 'Folder' : 'File';
22+
const { mutate: deleteFolderFile, isPending, isSuccess } = useDropComponent();
23+
24+
const handleDeleteFolderOrFile = useCallback(() => {
25+
if (!openedEntry) {
26+
return;
27+
}
28+
deleteFolderFile(
29+
{
30+
file: openedEntry.package
31+
? undefined
32+
: `${openedEntry.path.split('/').slice(1).join('/')}`,
33+
project: openedEntry.project,
34+
replicated: instanceParams.entityType === 'cluster',
35+
...instanceParams,
36+
},
37+
{
38+
onSuccess: () => {
39+
setIsModalOpen(false);
40+
setOpenedEntry(null);
41+
reloadRootEntries();
42+
},
43+
},
44+
);
45+
}, [deleteFolderFile, instanceParams, openedEntry]);
46+
47+
const onClickYes = useCallback((e: MouseEvent) => {
48+
e.preventDefault();
49+
handleDeleteFolderOrFile();
50+
}, [handleDeleteFolderOrFile]);
51+
52+
const onClickNo = useCallback(() => {
53+
setIsModalOpen(false);
54+
}, [setIsModalOpen]);
55+
56+
return (
57+
<Dialog onOpenChange={setIsModalOpen} open={isModalOpen}>
58+
<DialogContent aria-describedby={undefined} className="text-white">
59+
<DialogHeader>
60+
<DialogTitle>Delete {thing}</DialogTitle>
61+
<DialogDescription>
62+
Are you sure you want to delete this {thing.toLowerCase()}?
63+
</DialogDescription>
64+
<DialogDescription>
65+
{openedEntry?.path}
66+
</DialogDescription>
67+
</DialogHeader>
68+
69+
<div className="flex w-full gap-4">
70+
<Button variant="ghostOutline" className="w-full rounded-full" onClick={onClickNo}>
71+
<Ban /> Cancel
72+
</Button>
73+
<Button
74+
variant="destructiveOutline"
75+
type="button"
76+
className="w-full rounded-full"
77+
disabled={isPending}
78+
autoFocus={true}
79+
onClick={onClickYes}
80+
>
81+
<Trash /> {isSuccess ? 'Deleted' : isPending ? 'Deleting' : 'Delete'} {thing}{isPending ? '...' : ''}
82+
</Button>
83+
</div>
84+
</DialogContent>
85+
</Dialog>
86+
);
87+
}

src/features/instance/applications/modals/DeleteFolderFileModal.tsx

Lines changed: 0 additions & 57 deletions
This file was deleted.

0 commit comments

Comments
 (0)